web.php 112 KB
Newer Older
1
2
<?php
/**
Francois Marier's avatar
Francois Marier committed
3
 * Mahara: Electronic portfolio, weblog, resume builder and social networking
4
5
 * Copyright (C) 2006-2009 Catalyst IT Ltd and others; see:
 *                         http://wiki.mahara.org/Contributors
6
 *
Francois Marier's avatar
Francois Marier committed
7
8
9
10
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
11
 *
Francois Marier's avatar
Francois Marier committed
12
13
14
15
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
16
 *
Francois Marier's avatar
Francois Marier committed
17
18
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20
21
 *
 * @package    mahara
 * @subpackage core
22
 * @author     Catalyst IT Ltd
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL
24
 * @copyright  (C) 2006-2009 Catalyst IT Ltd http://catalyst.net.nz
25
26
27
28
29
30
 * @copyright  (C) portions from Moodle, (C) Martin Dougiamas http://dougiamas.com
 */

defined('INTERNAL') || die();


31
32
33
function smarty_core() {
    require_once 'dwoo/dwoo/dwooAutoload.php';
    require_once 'dwoo/mahara/Dwoo_Mahara.php';
34

35
    return new Dwoo_Mahara();
36
37
38
}


39
40
41
42
43
44
/**
 * This function creates a Smarty object and sets it up for use within our
 * podclass app, setting up some variables.
 *
 * The variables that it sets up are:
 *
45
 * - WWWROOT: The base url for the Mahara system
46
47
48
49
50
 * - USER: The user object
 * - JAVASCRIPT: A list of javascript files to include in the header.  This
 *   list is passed into this function (see below).
 * - HEADERS: An array of any further headers to set.  Each header is just
 *   straight HTML (see below).
51
52
 * - PUBLIC: Set true if this page is a public page
 * - MAINNAV: Array defining the main navigation
53
 *
54
 * @param $javascript A list of javascript includes.  Each include should be just
55
 *                    the name of a file, and reside in js/{filename}
56
57
58
 * @param $headers    A list of additional headers.  These are to be specified as
 *                    actual HTML.
 * @param $strings    A list of language strings required by the javascript code.
59
60
 * @return Smarty
 */
61

62
function smarty($javascript = array(), $headers = array(), $pagestrings = array(), $extraconfig = array()) {
63
    global $USER, $SESSION, $THEME;
64
65
66
67
68
69
70
71
72
73
74

    if (!is_array($headers)) {
        $headers = array();
    }
    if (!is_array($pagestrings)) {
        $pagestrings = array();
    }
    if (!is_array($extraconfig)) {
        $extraconfig = array();
    }

Martyn Smith's avatar
Martyn Smith committed
75
    $SIDEBLOCKS = array();
76

77
78
    $smarty = smarty_core();

79
    $wwwroot = get_config('wwwroot');
80
    // NOTE: not using jswwwroot - it seems to wreck image paths if you 
81
    // drag them around the wysiwyg editor
82
    $jswwwroot = json_encode($wwwroot);
Martyn Smith's avatar
Martyn Smith committed
83
84

    $theme_list = array();
85
86
87
88
    
    if (function_exists('pieform_get_headdata')) {
        $headers = array_merge($headers, pieform_get_headdata());
    }
89

90
    // Insert the appropriate javascript tags 
91
    $javascript_array = array();
92
    $jsroot = $wwwroot . 'js/';
93

94
95
    $langdirection = get_string('thisdirection', 'langconfig');

Richard Mansfield's avatar
Richard Mansfield committed
96
    // TinyMCE must be included first for some reason we're not sure about
97
    $checkarray = array(&$javascript, &$headers);
98
    $found_tinymce = false;
99
    foreach ($checkarray as &$check) {
100
101
102
103
        if (($key = array_search('tinymce', $check)) !== false || ($key = array_search('tinytinymce', $check)) !== false) {
            if (!$found_tinymce) {
                $found_tinymce = $check[$key];
                $javascript_array[] = $jsroot . 'tinymce/tiny_mce.js';
104
                $content_css = json_encode($THEME->get_url('style/tinymce.css'));
105
                $language = substr(current_language(), 0, 2);
106
107
108
                if ($language != 'en' && !file_exists(get_config('docroot') . 'js/tinymce/langs/' . $language . '.js')) {
                    $language = 'en';
                }
109
                $extrasetup = isset($extraconfig['tinymcesetup']) ? $extraconfig['tinymcesetup'] : '';
110

111
                $adv_buttons = array(
112
                    "undo,redo,separator,bold,italic,underline,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,bullist,numlist,separator,link,unlink,separator,code,fullscreen",
113
                    "bold,italic,underline,strikethrough,separator,forecolor,backcolor,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,hr,emotions,image,spellchecker,cleanup,separator,link,unlink,separator,code,fullscreen",
114
                    "undo,redo,separator,bullist,numlist,separator,tablecontrols,separator,cut,copy,paste,pasteword",
115
116
117
118
                    "fontselect,separator,fontsizeselect,separator,formatselect",
                );

                // For right-to-left langs, reverse button order & align controls right.
119
120
                $tinymce_langdir = $langdirection == 'rtl' ? 'rtl' : 'ltr';
                $toolbar_align = 'left';
121
122

                if ($check[$key] == 'tinymce') {
123
                    $spellchecker_rpc = $jsroot.'tinymce/plugins/spellchecker/rpc.php';
124
                    $tinymce_config = <<<EOF
125
    mode: "none",
126
    theme: "advanced",
127
    plugins: "table,emotions,spellchecker,inlinepopups,paste,fullscreen",
128
129
130
    theme_advanced_buttons1 : "{$adv_buttons[1]}",
    theme_advanced_buttons2 : "{$adv_buttons[2]}",
    theme_advanced_buttons3 : "{$adv_buttons[3]}",
131
    theme_advanced_toolbar_location : "top",
132
    theme_advanced_toolbar_align : "{$toolbar_align}",
133
    fix_list_elements: true,
134
    spellchecker_rpc_url : "{$spellchecker_rpc}",
135
    //width: '512',
136
EOF;
137
138
139
                }
                else {
                    $tinymce_config = <<<EOF
140
    mode: "textareas",
141
142
    editor_selector: 'tinywysiwyg',
    theme: "advanced",
143
    plugins: "fullscreen,inlinepopups,autoresize",
144
    theme_advanced_buttons1 : "{$adv_buttons[0]}",
145
    theme_advanced_buttons2 : "",
146
147
    theme_advanced_buttons3 : "",
    theme_advanced_toolbar_location : "top",
148
    theme_advanced_toolbar_align : "{$toolbar_align}",
149
150
151
    fullscreen_new_window: true,
    fullscreen_settings: {
        theme: "advanced",
152
        plugins: "table,emotions,iespell,inlinepopups,paste,fullscreen",
153
154
155
        theme_advanced_buttons1 : "{$adv_buttons[1]}",
        theme_advanced_buttons2 : "{$adv_buttons[2]}",
        theme_advanced_buttons3 : "{$adv_buttons[3]}"
156
    },
157
EOF;
158
                }
159

160
                $headers[] = <<<EOF
161
162
163
164
<script type="text/javascript">
tinyMCE.init({
    button_tile_map: true,
    {$tinymce_config}
165
    extended_valid_elements : "object[width|height|classid|codebase],param[name|value],embed[src|type|width|height|flashvars|wmode],script[src,type,language],+ul[id|type|compact],iframe[src|width|height|align|title|class|type|frameborder|allowfullscreen]",
166
    urlconverter_callback : "custom_urlconvert",
167
    language: '{$language}',
168
    directionality: "{$tinymce_langdir}",
169
    content_css : {$content_css},
170
    //document_base_url: {$jswwwroot},
171
    remove_script_host: false,
172
173
174
    relative_urls: false,
    setup: function(ed) {
        ed.onInit.add(function(ed) {
175
            if (typeof(editor_to_focus) == 'string' && ed.editorId == editor_to_focus) {
176
177
178
                ed.focus();
            }
        });
179
        {$extrasetup}
180
    }
181
});
182
function custom_urlconvert (u, n, e) {
183
  // Don't convert the url on the skype status buttons.
184
185
  if (u.indexOf('skype:') == 0) {
      return u;
186
  }
187
188
189
190
191
192
193
194
195
196
197
198
199
200
  var t = tinyMCE.activeEditor, s = t.settings;

  // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
  if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)
      return u;

  // Convert to relative
  if (s.relative_urls)
      return t.documentBaseURI.toRelative(u);

  // Convert to absolute
  u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);

  return u;
201
}
202
203
204
</script>

EOF;
205
206
207
208
209
210
211
                unset($check[$key]);
            }
            else {
                if ($check[$key] != $found_tinymce) {
                    log_warn('Two differently configured tinyMCE instances have been asked for on this page! This is not possible');
                }
                unset($check[$key]);
212
            }
213
        }
214
215
        // Load jquery first, so that it doesn't break Mochikit
        if (($key = array_search('jquery', $check)) !== false) {
216
            $jquery = (get_config('developermode') & DEVMODE_UNPACKEDJS) ? 'jquery-1.5.2.js' : 'jquery-1.5.2.min.js';
217
218
219
220
221
            array_unshift($javascript_array, $jsroot . 'jquery/' . $jquery);
            // Make jQuery accessible with $j (Mochikit has $)
            $headers[] = '<script type="text/javascript">$j=jQuery;</script>';
            unset($check[$key]);
        }
222
    }
223

224
    if (get_config('developermode') & DEVMODE_UNPACKEDJS) {
225
        $javascript_array[] = $jsroot . 'MochiKit/MochiKit.js';
226
227
228
229
        $javascript_array[] = $jsroot . 'MochiKit/Position.js';
        $javascript_array[] = $jsroot . 'MochiKit/Color.js';
        $javascript_array[] = $jsroot . 'MochiKit/Visual.js';
        $javascript_array[] = $jsroot . 'MochiKit/DragAndDrop.js';
230
        $javascript_array[] = $jsroot . 'MochiKit/Format.js';
231
232
233
234
    }
    else {
        $javascript_array[] = $jsroot . 'MochiKit/Packed.js';
    }
Martyn Smith's avatar
Martyn Smith committed
235
    $javascript_array[] = $jsroot . 'keyboardNavigation.js';
236

237
    $strings = array();
238
239
240
241
242
243
244
245
246
    foreach ($pagestrings as $k => $v) {
        if (is_array($v)) {
            foreach ($v as $tag) {
                $strings[$tag] = get_raw_string($tag, $k);
            }
        }
        else {
            $strings[$k] = get_raw_string($k, $v);
        }
247
248
    }

249
    $jsstrings = jsstrings();
Martyn Smith's avatar
Martyn Smith committed
250
    $themepaths = themepaths();
251

Richard Mansfield's avatar
Richard Mansfield committed
252
    foreach ($javascript as $jsfile) {
253
254
255
256
        // For now, if there's no path in the js file, assume it's in
        // $jsroot and append '.js' to the name.  Later we may want to
        // ensure all smarty() calls include the full path to the js
        // file, with the proper extension.
257
        if (strpos($jsfile, '/') === false) {
258
            $javascript_array[] = $jsroot . $jsfile . '.js';
259
            if (isset($jsstrings[$jsfile])) {
260
261
262
                foreach ($jsstrings[$jsfile] as $section => $tags) {
                    foreach ($tags as $tag) {
                        $strings[$tag] = get_raw_string($tag, $section);
263
264
265
                    }
                }
            }
Martyn Smith's avatar
Martyn Smith committed
266
267
            if (isset($themepaths[$jsfile])) {
                foreach ($themepaths[$jsfile] as $themepath) {
Nigel McNie's avatar
Nigel McNie committed
268
                    $theme_list[$themepath] = $THEME->get_url($themepath);
Martyn Smith's avatar
Martyn Smith committed
269
270
                }
            }
271
        }
272
273
        else if (strpos($jsfile, 'http://') === false) {
            // A local .js file with a fully specified path
274
            $javascript_array[] = $wwwroot . $jsfile;
275
276
277
278
279
280
            // If $jsfile is from a plugin (i.e. plugintype/pluginname/js/foo.js)
            // Then get js strings from static function jsstrings in plugintype/pluginname/lib.php 
            $bits = explode('/', $jsfile);
            if (count($bits) == 4) {
                safe_require($bits[0], $bits[1]);
                $pluginclass = generate_class_name($bits[0], $bits[1]);
281
                $name = substr($bits[3], 0, strpos($bits[3], '.js'));
282
283
                if (is_callable(array($pluginclass, 'jsstrings'))) {
                    $tempstrings = call_static_method($pluginclass, 'jsstrings', $name);
284
285
286
                    foreach ($tempstrings as $section => $tags) {
                        foreach ($tags as $tag) {
                            $strings[$tag] = get_raw_string($tag, $section);
287
288
                        }
                    }
Richard Mansfield's avatar
Richard Mansfield committed
289
                }
290
291
292
293
294
295
296
297
298
                if (is_callable(array($pluginclass, 'jshelp'))) {
                    $tempstrings = call_static_method($pluginclass, 'jshelp', $name);
                    foreach ($tempstrings as $section => $tags) {
                        foreach ($tags as $tag) {
                            $strings[$tag . '.help'] = get_help_icon($bits[0], $bits[1], null, null,
                                                                     null, $tag);
                        }
                    }
                }
Martyn Smith's avatar
Martyn Smith committed
299
300
301
                if (is_callable(array($pluginclass, 'themepaths'))) {
                    $tmpthemepaths = call_static_method($pluginclass, 'themepaths', $name);
                    foreach ($tmpthemepaths as $themepath) {
Nigel McNie's avatar
Nigel McNie committed
302
                        $theme_list[$themepath] = $THEME->get_url($themepath);
Martyn Smith's avatar
Martyn Smith committed
303
304
                    }
                }
Martyn Smith's avatar
Martyn Smith committed
305
            }
Martyn Smith's avatar
Martyn Smith committed
306
        }
307
308
309
310
        else {
            // A remote .js file
            $javascript_array[] = $jsfile;
        }
311
    }
312
313

    $javascript_array[] = $jsroot . 'mahara.js';
314
    if (get_config('developermode') & DEVMODE_DEBUGJS) {
315
        $javascript_array[] = $jsroot . 'debug.js';
316
    }
317

318
319
320
    foreach ($jsstrings['mahara'] as $section => $tags) {
        foreach ($tags as $tag) {
            $strings[$tag] = get_raw_string($tag, $section);
321
322
        }
    }
323
324
    if (isset($extraconfig['themepaths']) && is_array($extraconfig['themepaths'])) {
        foreach ($extraconfig['themepaths'] as $themepath) {
Nigel McNie's avatar
Nigel McNie committed
325
            $theme_list[$themepath] = $THEME->get_url($themepath);
326
327
        }
    }
328

329
    $stringjs = '<script type="text/javascript">';
330
    $stringjs .= 'var strings = ' . json_encode($strings) . ';';
331
332
    $stringjs .= '</script>';

333
    // stylesheet set up - if we're in a plugin also get its stylesheet
Nigel McNie's avatar
Nigel McNie committed
334
    $stylesheets = array_reverse(array_values($THEME->get_url('style/style.css', true)));
335
    if (defined('SECTION_PLUGINTYPE') && defined('SECTION_PLUGINNAME') && SECTION_PLUGINTYPE != 'core') {
Nigel McNie's avatar
Nigel McNie committed
336
        if ($pluginsheets = $THEME->get_url('style/style.css', true, SECTION_PLUGINTYPE . '/' . SECTION_PLUGINNAME)) {
337
338
339
            $stylesheets = array_merge($stylesheets, array_reverse($pluginsheets));
        }
    }
340
    if (defined('ADMIN') || defined('INSTITUTIONALADMIN')) {
341
342
343
        if ($adminsheets = $THEME->get_url('style/admin.css', true)) {
            $stylesheets = array_merge($stylesheets, array_reverse($adminsheets));
        }
344
    }
345
    if (get_config('developermode') & DEVMODE_DEBUGCSS) {
346
347
        $stylesheets[] = get_config('wwwroot') . 'theme/debug.css';
    }
348

349
350
351
    // look for extra stylesheets
    if (isset($extraconfig['stylesheets']) && is_array($extraconfig['stylesheets'])) {
        foreach ($extraconfig['stylesheets'] as $extrasheet) {
352
            if ($sheets = $THEME->get_url($extrasheet, true)) {
Nigel McNie's avatar
Nigel McNie committed
353
                $stylesheets = array_merge($stylesheets, array_reverse(array_values($sheets)));
354
355
356
            }
        }
    }
357
358
359
    if ($sheets = $THEME->additional_stylesheets()) {
        $stylesheets = array_merge($stylesheets, $sheets);
    }
360

361
    // Include rtl.css for right-to-left langs
362
    if ($langdirection == 'rtl') {
363
        $smarty->assign('LANGDIRECTION', 'rtl');
364
365
366
367
368
        if ($rtlsheets = $THEME->get_url('style/rtl.css', true)) {
            $stylesheets = array_merge($stylesheets, array_reverse($rtlsheets));
        }
    }

369
    $smarty->assign('STRINGJS', $stringjs);
370

371
    $smarty->assign('STYLESHEETLIST', $stylesheets);
372
373
374
375
    if (!empty($theme_list)) {
        // this gets assigned in smarty_core, but do it again here if it's changed locally
        $smarty->assign('THEMELIST', json_encode(array_merge((array)json_decode($smarty->get_template_vars('THEMELIST')),  $theme_list))); 
    }
376

377
378
379
380
    $dropdownmenu = get_config('dropdownmenu');
    if ($dropdownmenu) {
        $smarty->assign('DROPDOWNMENU', $dropdownmenu);
    }
381

382
383
384
385
    $sitename = get_config('sitename');
    if (!$sitename) {
       $sitename = 'Mahara';
    }
386
    $smarty->assign('sitename', $sitename);
387
    $smarty->assign('sitelogo', $THEME->header_logo());
388

Martyn Smith's avatar
Martyn Smith committed
389
    if (defined('TITLE')) {
390
        $smarty->assign('PAGETITLE', TITLE . ' - ' . $sitename);
391
        $smarty->assign('heading', TITLE);
Martyn Smith's avatar
Martyn Smith committed
392
393
    }
    else {
394
        $smarty->assign('PAGETITLE', $sitename);
Martyn Smith's avatar
Martyn Smith committed
395
396
    }

397
398
399
    if (function_exists('local_header_top_content')) {
        $smarty->assign('SITETOP', local_header_top_content());
    }
400
401
402
    if (defined('PUBLIC')) {
        $smarty->assign('PUBLIC', true);
    }
403
404
405
    if (defined('ADMIN')) {
        $smarty->assign('ADMIN', true);
    }
406
407
408
    if (defined('INSTITUTIONALADMIN')) {
        $smarty->assign('INSTITUTIONALADMIN', true);
    }
409

410
411
    $smarty->assign('LOGGEDIN', $USER->is_logged_in());
    if ($USER->is_logged_in()) {
412
        global $SELECTEDSUBNAV; // It's evil, but rightnav & mainnav stuff are now in different templates.
413
        $smarty->assign('MAINNAV', main_nav());
414
        $mainnavsubnav = $SELECTEDSUBNAV;
415
        $smarty->assign('RIGHTNAV', right_nav());
416
417
418
419
420
421
422
423
424
        if (!$mainnavsubnav && $dropdownmenu) {
            // In drop-down navigation, the submenu is only usable if its parent is one of the top-level menu
            // items.  But if the submenu comes from something in right_nav (settings), it's unreachable.
            // Turning the submenu into SUBPAGENAV group-style tabs makes it usable.
            $smarty->assign('SUBPAGENAV', $SELECTEDSUBNAV);
        }
        else {
            $smarty->assign('SELECTEDSUBNAV', $SELECTEDSUBNAV);
        }
425
    }
426
    else {
427
        $smarty->assign('languageform', language_select_form());
428
    }
429
    $smarty->assign('FOOTERMENU', footer_menu());
430

431
    $smarty->assign_by_ref('USER', $USER);
432
    $smarty->assign('SESSKEY', $USER->get('sesskey'));
433
    $smarty->assign_by_ref('JAVASCRIPT', $javascript_array);
434
    $smarty->assign_by_ref('HEADERS', $headers);
435
436
    $siteclosedforupgrade = get_config('siteclosed');
    if ($siteclosedforupgrade && get_config('disablelogin')) {
437
        $smarty->assign('SITECLOSED', 'logindisabled');
438
439
    }
    else if ($siteclosedforupgrade || get_config('siteclosedbyadmin')) {
440
        $smarty->assign('SITECLOSED', 'loginallowed');
441
    }
442

443
444
    if ((!isset($extraconfig['pagehelp']) || $extraconfig['pagehelp'] !== false)
        and $help = has_page_help()) {
445
446
447
        $smarty->assign('PAGEHELPNAME', $help[0]);
        $smarty->assign('PAGEHELPICON', $help[1]);
    }
448
    if (defined('GROUP')) {
449
        require_once('group.php');
450
451
452
453
454
455
        if ($group = group_current_group()) {
            $smarty->assign('GROUP', $group);
            if (!defined('NOGROUPMENU')) {
                $smarty->assign('SUBPAGENAV', group_get_menu_tabs());
                $smarty->assign('PAGEHEADING', $group->name);
            }
456
        }
457
    }
458

Martyn Smith's avatar
Martyn Smith committed
459
    // ---------- sideblock stuff ----------
460
461
    $sidebars = !isset($extraconfig['sidebars']) || $extraconfig['sidebars'] !== false;
    if ($sidebars && !defined('INSTALLER') && (!defined('MENUITEM') || substr(MENUITEM, 0, 5) != 'admin')) {
Richard Mansfield's avatar
Richard Mansfield committed
462
        if (get_config('installed') && !defined('ADMIN') && !defined('INSTITUTIONALADMIN')) {
463
464
465
466
            $data = site_menu();
            if (!empty($data)) {
                $smarty->assign('SITEMENU', site_menu());
                $SIDEBLOCKS[] = array(
467
                    'name'   => 'linksandresources',
468
469
470
471
472
473
                    'weight' => 10,
                    'data'   => $data,
                );
            }
        }

474
475
        if ($USER->is_logged_in() && defined('MENUITEM') &&
            (substr(MENUITEM, 0, 11) == 'myportfolio' || substr(MENUITEM, 0, 7) == 'content')) {
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
            if (get_config('showselfsearchsideblock')) {
                $SIDEBLOCKS[] = array(
                    'name'   => 'selfsearch',
                    'weight' => 0,
                    'data'   => array(),
                );
            }
            if (get_config('showtagssideblock')) {
                $SIDEBLOCKS[] = array(
                    'name'   => 'tags',
                    'id'     => 'sb-tags',
                    'weight' => 0,
                    'data'   => tags_sideblock(),
                );
            }
Clare Lenihan's avatar
Clare Lenihan committed
491
        }
Clare Lenihan's avatar
Clare Lenihan committed
492

Richard Mansfield's avatar
Richard Mansfield committed
493
        if($USER->is_logged_in() && !defined('ADMIN') && !defined('INSTITUTIONALADMIN')) {
494
495
            $SIDEBLOCKS[] = array(
                'name'   => 'profile',
496
                'id'     => 'sb-profile',
497
498
499
                'weight' => -20,
                'data'   => profile_sideblock()
            );
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
            $showusers = 2;
            $institutions = $USER->institutions;
            if (!empty($institutions)) {
                $showusers = 0;
                foreach ($institutions as $i) {
                    if ($i->showonlineusers == 2) {
                        $showusers = 2;
                        break;
                    }
                    if ($i->showonlineusers == 1) {
                        $showusers = 1;
                    }
                }
            }
            if (get_config('showonlineuserssideblock') && $showusers > 0) {
515
516
                $SIDEBLOCKS[] = array(
                    'name'   => 'onlineusers',
517
                    'id'     => 'sb-onlineusers',
518
519
520
521
                    'weight' => -10,
                    'data'   => onlineusers_sideblock(),
                );
            }
522
        }
Martyn Smith's avatar
Martyn Smith committed
523

Richard Mansfield's avatar
Richard Mansfield committed
524
525
526
        if(defined('GROUP')) {
            $SIDEBLOCKS[] = array(
                'name'   => 'group',
527
                'id'     => 'sb-groupnav',
Richard Mansfield's avatar
Richard Mansfield committed
528
529
530
531
532
                'weight' => -10,
                'data'   => group_sideblock()
            );
        }

533
        if (!$USER->is_logged_in() && !(get_config('siteclosed') && get_config('disablelogin'))) {
534
            $SIDEBLOCKS[] = array(
535
536
                'name'   => 'login',
                'weight' => -10,
537
                'id'     => 'sb-loginbox',
538
539
540
                'data'   => array(
                    'loginform' => auth_generate_login_form(),
                ),
541
542
            );
        }
543

544
545
546
547
548
549
550
551
552
        if (get_config('enablenetworking')) {
            require_once(get_config('docroot') .'api/xmlrpc/lib.php');
            if ($USER->is_logged_in() && $ssopeers = get_service_providers($USER->authinstance)) {
                $SIDEBLOCKS[] = array(
                    'name'   => 'ssopeers',
                    'weight' => 1,
                    'data'   => $ssopeers,
                );
            }
Martyn Smith's avatar
Martyn Smith committed
553
554
        }

555
556
557
558
559
        if (isset($extraconfig['sideblocks']) && is_array($extraconfig['sideblocks'])) {
            foreach ($extraconfig['sideblocks'] as $sideblock) {
                $SIDEBLOCKS[] = $sideblock;
            }
        }
Martyn Smith's avatar
Martyn Smith committed
560

561
562
        usort($SIDEBLOCKS, create_function('$a,$b', 'if ($a["weight"] == $b["weight"]) return 0; return ($a["weight"] < $b["weight"]) ? -1 : 1;'));

563
564
565
        // Place all sideblocks on the right. If this structure is munged 
        // appropriately, you can put blocks on the left. In future versions of 
        // Mahara, we'll make it easy to do this.
566
        $sidebars = $sidebars && !empty($SIDEBLOCKS);
567
        $SIDEBLOCKS = array('left' => array(), 'right' => $SIDEBLOCKS);
568

569
        $smarty->assign('userauthinstance', $SESSION->get('authinstance'));
570
        $smarty->assign('MNETUSER', $SESSION->get('mnetuser'));
571
        $smarty->assign('SIDEBLOCKS', $SIDEBLOCKS);
572
        $smarty->assign('SIDEBARS', $sidebars);
573

574
575
576
577
    }

    if ($USER->get('parentuser')) {
        $smarty->assign('USERMASQUERADING', true);
578
        $smarty->assign('masqueradedetails', get_string('youaremasqueradingas', 'mahara', display_name($USER)));
579
580
        $smarty->assign('becomeyouagain',
            ' <a href="' . hsc($wwwroot) . 'admin/users/changeuser.php?restore=1">'
581
            . get_string('becomeadminagain', 'admin', hsc($USER->get('parentuser')->name))
582
            . '</a>');
583
    }
Martyn Smith's avatar
Martyn Smith committed
584

585
586
587
    return $smarty;
}

588
589
590
591
592
593
594
595
596
597
598
599
600
601

/**
 * Manages theme configuration.
 *
 * Does its best to give the user _a_ theme, even if it's not the theme they 
 * want to use (e.g. the theme they want has been uninstalled)
 */
class Theme {

    /**
     * The base name of the theme (the name of the directory in which it lives)
     */
    public $basename = '';

602
603
604
605
606
    /**
     * A user may have had the header logo overridden by an institution
     */
    public $headerlogo;

607
608
609
610
611
    /**
     * Additional stylesheets to display after the basename theme's stylesheets
     */
    public $addedstylesheets;

612
613
614
615
616
617
618
619
620
621
    /**
     * A human-readable version of the theme name
     */
    public $displayname = '';

    /**
     * Which pieform renderer to use by default for all forms
     */
    public $formrenderer = '';

622
623
624
625
626
627
628
629
630
631
    /**
     * Directories where to look for templates by default
     */
    public $templatedirs = array();

    /**
     * Theme inheritance path from this theme to 'raw'
     */
    public $inheritance = array();

632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
    /**
     * What unit the left/center/right column widths are in. 'pixels' or 'percent'
     */
    public $columnwidthunits    = '';

    /**
     * Width of the left column. Integer - see $columnwidthunits
     */
    public $leftcolumnwidth     = 256;

    /**
     * Background colour for the left column
     */
    public $leftcolumnbgcolor   = '#fff';

    /**
     * Background colour for the center column
     */
    public $centercolumnbgcolor = '#fff';

    /**
     * Width of the right column. Integer - see $columnwidthunits
     */
    public $rightcolumnwidth    = 256;

    /**
     * Background colour for the right column
     */
    public $rightcolumnbgcolor  = '#fff';


    /**
     * Initialises a theme object based on the theme 'hint' passed.
     *
     * If arg is a string, it's taken to be a theme name. If it's a user 
     * object, we ask it for a theme name. If it's an integer, we pretend 
     * that's a user ID and ask for the theme for that user.
     *
     * If the theme they want doesn't exist, the object is initialised for the 
     * default theme. This means you can initialise one of these for a user
     * and then use it without worrying if the theme exists.
     *
     * @param mixed $arg Theme name, user object or user ID
     */
    public function __construct($arg) {
        if (is_string($arg)) {
            $themename = $arg;
679
            $themedata = null;
680
681
        }
        else if ($arg instanceof User) {
682
            $themedata = $arg->get_themedata();
683
684
685
686
        }
        else if (is_int($arg)) {
            $user = new User();
            $user->find_by_id($arg);
687
            $themedata = $user->get_themedata();
688
689
690
691
692
        }
        else {
            throw new SystemException("Argument to Theme::__construct was not a theme name, user object or user ID");
        }

693
694
695
696
        if (isset($themedata)) {
            $themename = $themedata->basename;
        }

697
698
        if (!$themename) {
            // Theme to show to when no theme has been suggested
699
700
701
            if (!$themename = get_config('theme')) {
                $themename = 'raw';
            }
702
        }
703
704

        // check the validity of the name
705
        if (!$this->name_is_valid($themename)) {
706
707
            throw new SystemException("Theme name is in invalid form: '$themename'");
        }
708
709

        $this->init_theme($themename, $themedata);
710
711
712
713
714
715
716
717
    }

    /**
     * Given a theme name, check that it is valid
     */
    public static function name_is_valid($themename) {
        // preg_match returns 0 if invalid characters were found, 1 if not
        return (preg_match('/^[a-zA-Z0-9_-]+$/', $themename) == 1);
718
719
720
721
722
    }

    /**
     * Given a theme name, reads in all config and sets fields on this object
     */
723
    private function init_theme($themename, $themedata) {
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
        $this->basename = $themename;

        $themeconfigfile = get_config('docroot') . 'theme/' . $this->basename . '/themeconfig.php';
        if (!is_readable($themeconfigfile)) {
            // We can safely assume that the default theme is installed, users 
            // should never be able to remove it
            $this->basename = 'default';
            $themeconfigfile = get_config('docroot') . 'theme/default/themeconfig.php';
        }

        require($themeconfigfile);

        foreach (get_object_vars($theme) as $key => $value) {
            $this->$key = $value;
        }

        if (!isset($this->displayname)) {
741
            $this->displayname = $this->basename;
742
743
744
745
746
        }
        if (!isset($theme->parent) || !$theme->parent) {
            $theme->parent = 'raw';
        }

747
748
749
        $this->templatedirs[] = get_config('docroot') . 'theme/' . $this->basename . '/templates/';
        $this->inheritance[]  = $this->basename;

750
751
        $this->templatedirs[] = get_config('docroot') . 'local/theme/templates/';

752
753
754
755
756
757
758
759
        // Now go through the theme hierarchy assigning variables from the 
        // parent themes
        $currenttheme = $this->basename;
        while ($currenttheme != 'raw') {
            $currenttheme = isset($theme->parent) ? $theme->parent : 'raw';
            $parentconfigfile = get_config('docroot') . 'theme/' . $currenttheme . '/themeconfig.php';
            require($parentconfigfile);
            foreach (get_object_vars($theme) as $key => $value) {
760
                if (!isset($this->$key) || !$this->$key) {
761
762
763
                    $this->$key = $value;
                }
            }
764
765
            $this->templatedirs[] = get_config('docroot') . 'theme/' . $currenttheme . '/templates/';
            $this->inheritance[]  = $currenttheme;
766
        }
767
768
769
770

        if (!empty($themedata->headerlogo)) {
            $this->headerlogo = $themedata->headerlogo;
        }
771
772
773
        if (!empty($themedata->stylesheets)) {
            $this->addedstylesheets = $themedata->stylesheets;
        }
774
775
    }

776
777
778
    /**
     * stuff
     */
779
780
    public function get_url($filename, $all=false, $plugindirectory='') {
        return $this->_get_path($filename, $all, $plugindirectory, get_config('wwwroot'));
781
782
    }

783
784
    public function get_path($filename, $all=false, $plugindirectory='') {
        return $this->_get_path($filename, $all, $plugindirectory, get_config('docroot'));
785
786
    }

787
    private function _get_path($filename, $all, $plugindirectory, $returnprefix) {
788
        $list = array();
789
        $plugindirectory = ($plugindirectory && substr($plugindirectory, -1) != '/') ? $plugindirectory . '/' : $plugindirectory;
790
791

        foreach ($this->inheritance as $themedir) {
792
            if (is_readable(get_config('docroot') . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename)) {
793
                if ($all) {
794
                    $list[$themedir] = $returnprefix . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename;
795
796
                }
                else {
797
                    return $returnprefix . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename;
798
799
800
801
802
803
804
805
                }
            }
        }
        if ($all) {
            return $list;
        }

        $extra = '';
806
807
        if ($plugindirectory) {
            $extra = ", plugindir $plugindirectory";
808
809
        }
        log_debug("Missing file in theme {$this->basename}{$extra}: $filename");
810
        return $returnprefix . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename;
811
812
    }

813
814
815
816
817
818
    public function header_logo() {
        if (!empty($this->headerlogo)) {
            return get_config('wwwroot') . 'thumb.php?type=logobyid&id=' . $this->headerlogo;
        }
        return $this->get_url('images/site-logo.png');
    }
819
820
821
822

    public function additional_stylesheets() {
        return $this->addedstylesheets;
    }
823
824
825
}


826
827
/** 
 * Returns the lists of strings used in the .js files
828
 * @return array                   
829
830
 */

831
function jsstrings() {
Martyn Smith's avatar
Martyn Smith committed
832
    return array(
833
834
835
       'mahara' => array(                        // js file
            'mahara' => array(                   // section
                'namedfieldempty',               // string name
836
                'processing',
837
838
                'unknownerror',
                'loading',
Martyn Smith's avatar
Martyn Smith committed
839
                'showtags',
840
841
                'unreadmessages',
                'unreadmessage',
842
                'couldnotgethelp',
843
844
845
846
847
848
                'password',
                'username',
                'login',
                'sessiontimedout',
                'loginfailed',
                'home',
849
                'youhavenottaggedanythingyet',
850
            ),
851
852
        ),
        'tablerenderer' => array(
853
854
855
856
857
858
            'mahara' => array(
                'firstpage',
                'nextpage',
                'prevpage',
                'lastpage',
            )
859
        ),
860
861
862
        'views' => array(
            'view' => array(
                'confirmdeleteblockinstance',
863
                'blocksinstructionajax',
864
865
            ),
        ),
Martyn Smith's avatar
Martyn Smith committed
866
867
868
    );
}

Martyn Smith's avatar
Martyn Smith committed
869
function themepaths() {
870
871
872
873
874
875

    static $paths;
    if (empty($paths)) {
        $paths = array(
            'mahara' => array(
                'images/icon_close.gif',
876
                'images/edit.gif',
877
878
879
                'images/failure.gif',
                'images/loading.gif',
                'images/success.gif',
880
                'images/icon_problem.gif',
881
                'images/icon_help.gif',
882
                'style/js.css',
883
884
885
886
            ),
        );
    }
    return $paths;
Martyn Smith's avatar
Martyn Smith committed
887
888
}

889
890
891
892
893
894
/** 
 * Takes an array of string identifiers and returns an array of the
 * corresponding strings, quoted for use in inline javascript here
 * docs.
 */

895
896
897
898
function quotestrings($strings) {
    $qstrings = array();
    foreach ($strings as $section => $tags) {
        foreach ($tags as $tag) {
899
            $qstrings[$tag] = json_encode(get_string($tag, $section));
900
        }
901
    }
902
    return $qstrings;
903
904
}

905
906
907
908
909
910
911
/** 
 * This function sets up and caches info about the current selected theme
 * contains inheritance path (used for locating images) and template dirs
 * and potentially more stuff later ( like mime header to send (html vs xhtml))
 * @return object
 */
function theme_setup() {
912
913
914
    global $THEME;
    log_warn("theme_setup() is deprecated - please use the global \$THEME object instead");
    return $THEME;
915
916
917
918
919
920
921
922
}

/** 
 * This function returns the full url to an image
 * Always use it to get image urls
 * @param $imagelocation path to image relative to theme/$theme/static/
 * @param $pluginlocation path to plugin relative to docroot
 */
923
function theme_get_url($location, $pluginlocation='', $all = false) {
924
925
926
927
928
929
    global $THEME;
    log_warn("theme_get_url() is deprecated: Use \$THEME->get_url() instead");
    $plugintype = $pluginname = '';
    if ($pluginlocation) {
        list($plugintype, $pluginname) = explode('/', $pluginlocation);
        $pluginname = substr($pluginname, 0, -1);
930
    }
931
    return $THEME->get_url($location, $all, $plugintype, $pluginname);
932
933
}

934
935
936
937
938
939
/** 
 * This function returns the full path to an image
 * Always use it to get image paths
 * @param $imagelocation path to image relative to theme/$theme/static/
 * @param $pluginlocation path to plugin relative to docroot
 */
940
function theme_get_path($location, $pluginlocation='', $all=false) {
941
942
943
944
945
946
    global $THEME;
    log_warn("theme_get_path() is deprecated: Use \$THEME->get_path() instead");
    $plugintype = $pluginname = '';
    if ($pluginlocation) {
        list($plugintype, $pluginname) = explode('/', $pluginlocation);
        $pluginname = substr($pluginname, 0, -1);
947
    }
948
    return $THEME->get_path($location, $all, $plugintype, $pluginname);
949
950
}

951
952
953
954
955
/**
 * This function sends headers suitable for all JSON returning scripts.
 *
 */
function json_headers() {
956
    // @todo Catalyst IT Ltd
957
958
    // header('Content-type: text/x-json');
    header('Content-type: text/plain');
Martyn Smith's avatar
Martyn Smith committed
959
    header('Pragma: no-cache');
960
961
}

Richard Mansfield's avatar
Richard Mansfield committed
962
/**
963
 * This function sends a JSON message, and ends the script.
Richard Mansfield's avatar
Richard Mansfield committed
964
 *
965
966
967
968
969
970
971
972
 * Scripts receiving replies will recieve a JSON array with two fields:
 *
 *  - error: True or false depending on whether the request was successful
 *  - message: JSON data representing a message sent back from the script
 *
 * @param boolean $error   Whether the script ended in an error or not
 * @param string  $message A message to pass back to the user, can be an
 *                         array of JSON data
Richard Mansfield's avatar
Richard Mansfield committed
973
 */
974
function json_reply($error, $message, $returncode=0) {
Richard Mansfield's avatar
Richard Mansfield committed
975
    json_headers();
976
    echo json_encode(array('error' => $error, 'message' => $message, 'returnCode' => $returncode));
977
    perf_to_log();
Richard Mansfield's avatar
Richard Mansfield committed
978
979
980
    exit;
}

981
function _param_retrieve($name) {
982
983
984
985
986
987
988
    // prefer post
    if (isset($_POST[$name])) {
        $value = $_POST[$name];
    } 
    else if (isset($_GET[$name])) {
        $value = $_GET[$name];
    }