web.php 125 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, $HEADDATA;
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
    // Workaround for $cfg->cleanurlusersubdomains.
    // When cleanurlusersubdomains is on, ajax requests might come from somewhere other than
    // the wwwroot.  To avoid cross-domain requests, set a js variable when this page is on a
    // different subdomain, and let the ajax wrapper function sendjsonrequest rewrite its url
    // if necessary.
    if (get_config('cleanurls') && get_config('cleanurlusersubdomains')) {
        if ($requesthost = get_requested_host_name()) {
            $wwwrootparts = parse_url($wwwroot);
            if ($wwwrootparts['host'] != $requesthost) {
                $fakewwwroot = $wwwrootparts['scheme'] . '://' . $requesthost . '/';
                $headers[] = '<script type="text/javascript">var fakewwwroot = ' . json_encode($fakewwwroot) . ';</script>';
            }
        }
    }

Martyn Smith's avatar
Martyn Smith committed
99
    $theme_list = array();
100
101
102
    
    if (function_exists('pieform_get_headdata')) {
        $headers = array_merge($headers, pieform_get_headdata());
103
104
105
        if (!defined('PIEFORM_GOT_HEADDATA')) {
          define('PIEFORM_GOT_HEADDATA', 1);
        }
106
    }
107

108
    // Insert the appropriate javascript tags 
109
    $javascript_array = array();
110
    $jsroot = $wwwroot . 'js/';
111

112
113
    $langdirection = get_string('thisdirection', 'langconfig');

Richard Mansfield's avatar
Richard Mansfield committed
114
    // TinyMCE must be included first for some reason we're not sure about
115
116
117
118
    //
    // Note: we do not display tinyMCE for mobile devices
    // as it doesn't work on some of them and can
    // disable the editing of a textarea field
119
    if ($SESSION->get('handheld_device') == false) {
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
        $checkarray = array(&$javascript, &$headers);
        $found_tinymce = false;
        foreach ($checkarray as &$check) {
            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';
                    $content_css = json_encode($THEME->get_url('style/tinymce.css'));
                    $language = substr(current_language(), 0, 2);
                    if ($language != 'en' && !file_exists(get_config('docroot') . 'js/tinymce/langs/' . $language . '.js')) {
                        $language = 'en';
                    }
                    $extrasetup = isset($extraconfig['tinymcesetup']) ? $extraconfig['tinymcesetup'] : '';

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

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

                    if ($check[$key] == 'tinymce') {
                        $spellchecker_rpc = $jsroot.'tinymce/plugins/spellchecker/rpc.php';
                        $tinymce_config = <<<EOF
148
    mode: "none",
149
    theme: "advanced",
150
    plugins: "table,emotions,spellchecker,inlinepopups,paste,fullscreen",
151
152
153
    theme_advanced_buttons1 : "{$adv_buttons[1]}",
    theme_advanced_buttons2 : "{$adv_buttons[2]}",
    theme_advanced_buttons3 : "{$adv_buttons[3]}",
154
    theme_advanced_toolbar_location : "top",
155
    theme_advanced_toolbar_align : "{$toolbar_align}",
156
    fix_list_elements: true,
157
    spellchecker_rpc_url : "{$spellchecker_rpc}",
158
    //width: '512',
159
EOF;
160
161
162
                    }
                    else {
                        $tinymce_config = <<<EOF
163
    mode: "textareas",
164
165
    editor_selector: 'tinywysiwyg',
    theme: "advanced",
166
    plugins: "fullscreen,inlinepopups,autoresize",
167
    theme_advanced_buttons1 : "{$adv_buttons[0]}",
168
    theme_advanced_buttons2 : "",
169
170
    theme_advanced_buttons3 : "",
    theme_advanced_toolbar_location : "top",
171
    theme_advanced_toolbar_align : "{$toolbar_align}",
172
173
174
    fullscreen_new_window: true,
    fullscreen_settings: {
        theme: "advanced",
175
        plugins: "table,emotions,iespell,inlinepopups,paste,fullscreen",
176
177
178
        theme_advanced_buttons1 : "{$adv_buttons[1]}",
        theme_advanced_buttons2 : "{$adv_buttons[2]}",
        theme_advanced_buttons3 : "{$adv_buttons[3]}"
179
    },
180
EOF;
181
                    }
182

183
                    $headers[] = <<<EOF
184
185
186
187
<script type="text/javascript">
tinyMCE.init({
    button_tile_map: true,
    {$tinymce_config}
188
    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]",
189
    urlconverter_callback : "custom_urlconvert",
190
    language: '{$language}',
191
    directionality: "{$tinymce_langdir}",
192
    content_css : {$content_css},
193
    //document_base_url: {$jswwwroot},
194
    remove_script_host: false,
195
196
197
    relative_urls: false,
    setup: function(ed) {
        ed.onInit.add(function(ed) {
198
            if (typeof(editor_to_focus) == 'string' && ed.editorId == editor_to_focus) {
199
200
201
                ed.focus();
            }
        });
202
        {$extrasetup}
203
    }
204
});
205
function custom_urlconvert (u, n, e) {
206
  // Don't convert the url on the skype status buttons.
207
208
  if (u.indexOf('skype:') == 0) {
      return u;
209
  }
210
211
212
213
214
215
216
217
218
219
220
221
222
223
  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;
224
}
225
226
227
</script>

EOF;
228
229
230
231
232
233
234
                    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]);
235
                }
236
            }
237

238
239
240
241
            // If any page adds jquery explicitly, remove it from the list
            if (($key = array_search('jquery', $check)) !== false) {
                unset($check[$key]);
            }
242
        }
243
    }
244
245
246
247
248
249
250
251
    else {
        if (($key = array_search('tinymce', $javascript)) !== false || ($key = array_search('tinytinymce', $javascript)) !== false) {
            unset($javascript[$key]);
        }
        if (($key = array_search('tinymce', $headers)) !== false || ($key = array_search('tinytinymce', $headers)) !== false) {
            unset($headers[$key]);
        }
    }
252

253
254
255
256
    // Make jQuery accessible with $j (Mochikit has $)
    $javascript_array[] = $jsroot . 'jquery/jquery.js';
    $headers[] = '<script type="text/javascript">$j=jQuery;</script>';

257
    if (get_config('developermode') & DEVMODE_UNPACKEDJS) {
258
        $javascript_array[] = $jsroot . 'MochiKit/MochiKit.js';
259
260
261
262
        $javascript_array[] = $jsroot . 'MochiKit/Position.js';
        $javascript_array[] = $jsroot . 'MochiKit/Color.js';
        $javascript_array[] = $jsroot . 'MochiKit/Visual.js';
        $javascript_array[] = $jsroot . 'MochiKit/DragAndDrop.js';
263
        $javascript_array[] = $jsroot . 'MochiKit/Format.js';
264
265
266
267
    }
    else {
        $javascript_array[] = $jsroot . 'MochiKit/Packed.js';
    }
Martyn Smith's avatar
Martyn Smith committed
268
    $javascript_array[] = $jsroot . 'keyboardNavigation.js';
269

270
    $strings = array();
271
272
273
274
275
276
277
278
279
    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);
        }
280
281
    }

282
    $jsstrings = jsstrings();
Martyn Smith's avatar
Martyn Smith committed
283
    $themepaths = themepaths();
284

Richard Mansfield's avatar
Richard Mansfield committed
285
    foreach ($javascript as $jsfile) {
286
287
288
289
        // 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.
290
        if (strpos($jsfile, '/') === false) {
291
            $javascript_array[] = $jsroot . $jsfile . '.js';
292
            if (isset($jsstrings[$jsfile])) {
293
294
295
                foreach ($jsstrings[$jsfile] as $section => $tags) {
                    foreach ($tags as $tag) {
                        $strings[$tag] = get_raw_string($tag, $section);
296
297
298
                    }
                }
            }
Martyn Smith's avatar
Martyn Smith committed
299
300
            if (isset($themepaths[$jsfile])) {
                foreach ($themepaths[$jsfile] as $themepath) {
Nigel McNie's avatar
Nigel McNie committed
301
                    $theme_list[$themepath] = $THEME->get_url($themepath);
Martyn Smith's avatar
Martyn Smith committed
302
303
                }
            }
304
        }
305
306
        else if (strpos($jsfile, 'http://') === false) {
            // A local .js file with a fully specified path
307
            $javascript_array[] = $wwwroot . $jsfile;
308
309
310
311
312
313
            // 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]);
314
                $name = substr($bits[3], 0, strpos($bits[3], '.js'));
315
316
                if (is_callable(array($pluginclass, 'jsstrings'))) {
                    $tempstrings = call_static_method($pluginclass, 'jsstrings', $name);
317
318
319
                    foreach ($tempstrings as $section => $tags) {
                        foreach ($tags as $tag) {
                            $strings[$tag] = get_raw_string($tag, $section);
320
321
                        }
                    }
Richard Mansfield's avatar
Richard Mansfield committed
322
                }
323
324
325
326
327
328
329
330
331
                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
332
333
334
                if (is_callable(array($pluginclass, 'themepaths'))) {
                    $tmpthemepaths = call_static_method($pluginclass, 'themepaths', $name);
                    foreach ($tmpthemepaths as $themepath) {
Nigel McNie's avatar
Nigel McNie committed
335
                        $theme_list[$themepath] = $THEME->get_url($themepath);
Martyn Smith's avatar
Martyn Smith committed
336
337
                    }
                }
Martyn Smith's avatar
Martyn Smith committed
338
            }
Martyn Smith's avatar
Martyn Smith committed
339
        }
340
341
342
343
        else {
            // A remote .js file
            $javascript_array[] = $jsfile;
        }
344
    }
345
346

    $javascript_array[] = $jsroot . 'mahara.js';
347
    if (get_config('developermode') & DEVMODE_DEBUGJS) {
348
        $javascript_array[] = $jsroot . 'debug.js';
349
    }
350

351
352
353
    foreach ($jsstrings['mahara'] as $section => $tags) {
        foreach ($tags as $tag) {
            $strings[$tag] = get_raw_string($tag, $section);
354
355
        }
    }
356
357
    if (isset($extraconfig['themepaths']) && is_array($extraconfig['themepaths'])) {
        foreach ($extraconfig['themepaths'] as $themepath) {
Nigel McNie's avatar
Nigel McNie committed
358
            $theme_list[$themepath] = $THEME->get_url($themepath);
359
360
        }
    }
361

362
    $stringjs = '<script type="text/javascript">';
363
    $stringjs .= 'var strings = ' . json_encode($strings) . ';';
364
    $stringjs .= "\nfunction plural(n) { return " . get_raw_string('pluralrule', 'langconfig') . "; }\n";
365
366
    $stringjs .= '</script>';

367
    // stylesheet set up - if we're in a plugin also get its stylesheet
Nigel McNie's avatar
Nigel McNie committed
368
    $stylesheets = array_reverse(array_values($THEME->get_url('style/style.css', true)));
369
    if (defined('SECTION_PLUGINTYPE') && defined('SECTION_PLUGINNAME') && SECTION_PLUGINTYPE != 'core') {
Nigel McNie's avatar
Nigel McNie committed
370
        if ($pluginsheets = $THEME->get_url('style/style.css', true, SECTION_PLUGINTYPE . '/' . SECTION_PLUGINNAME)) {
371
372
373
            $stylesheets = array_merge($stylesheets, array_reverse($pluginsheets));
        }
    }
374
375

    if ($adminsection = in_admin_section()) {
376
377
378
        if ($adminsheets = $THEME->get_url('style/admin.css', true)) {
            $stylesheets = array_merge($stylesheets, array_reverse($adminsheets));
        }
379
    }
380
    if (get_config('developermode') & DEVMODE_DEBUGCSS) {
381
382
        $stylesheets[] = get_config('wwwroot') . 'theme/debug.css';
    }
383

384
385
386
    // look for extra stylesheets
    if (isset($extraconfig['stylesheets']) && is_array($extraconfig['stylesheets'])) {
        foreach ($extraconfig['stylesheets'] as $extrasheet) {
387
            if ($sheets = $THEME->get_url($extrasheet, true)) {
Nigel McNie's avatar
Nigel McNie committed
388
                $stylesheets = array_merge($stylesheets, array_reverse(array_values($sheets)));
389
390
391
            }
        }
    }
392
393
394
    if ($sheets = $THEME->additional_stylesheets()) {
        $stylesheets = array_merge($stylesheets, $sheets);
    }
395

396
    // Include rtl.css for right-to-left langs
397
    if ($langdirection == 'rtl') {
398
        $smarty->assign('LANGDIRECTION', 'rtl');
399
400
401
402
403
        if ($rtlsheets = $THEME->get_url('style/rtl.css', true)) {
            $stylesheets = array_merge($stylesheets, array_reverse($rtlsheets));
        }
    }

404
    $smarty->assign('STRINGJS', $stringjs);
405

406
    $smarty->assign('STYLESHEETLIST', $stylesheets);
407
408
409
410
    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))); 
    }
411

412
413
    // disable drop-downs if a handheld device detected
    $dropdownmenu = $SESSION->get('handheld_device') ? false : get_config('dropdownmenu');
414
415
416
    if ($dropdownmenu) {
        $smarty->assign('DROPDOWNMENU', $dropdownmenu);
    }
417

418
    $smarty->assign('MOBILE', $SESSION->get('mobile'));
419
    $smarty->assign('HANDHELD_DEVICE', $SESSION->get('handheld_device'));
420

421
422
423
424
    $sitename = get_config('sitename');
    if (!$sitename) {
       $sitename = 'Mahara';
    }
425
    $smarty->assign('sitename', $sitename);
426
    $smarty->assign('sitelogo', $THEME->header_logo());
427
428
    $smarty->assign('sitelogo4facebook', $THEME->facebook_logo());
    $smarty->assign('sitedescription4facebook', get_string('facebookdescription', 'mahara'));
429

Martyn Smith's avatar
Martyn Smith committed
430
    if (defined('TITLE')) {
431
        $smarty->assign('PAGETITLE', TITLE . ' - ' . $sitename);
432
        $smarty->assign('heading', TITLE);
Martyn Smith's avatar
Martyn Smith committed
433
434
    }
    else {
435
        $smarty->assign('PAGETITLE', $sitename);
Martyn Smith's avatar
Martyn Smith committed
436
437
    }

438
    $smarty->assign('PRODUCTIONMODE', get_config('productionmode'));
439
    if (function_exists('local_header_top_content')) {
440
        $sitetop = (isset($sitetop) ? $sitetop : '') . local_header_top_content();
441
442
443
    }
    if (isset($sitetop)) {
        $smarty->assign('SITETOP', $sitetop);
444
    }
445
446
447
    if (defined('PUBLIC')) {
        $smarty->assign('PUBLIC', true);
    }
448
449
450
    if (defined('ADMIN')) {
        $smarty->assign('ADMIN', true);
    }
451
452
453
    if (defined('INSTITUTIONALADMIN')) {
        $smarty->assign('INSTITUTIONALADMIN', true);
    }
454
455
456
457
458
459
    if (defined('STAFF')) {
        $smarty->assign('STAFF', true);
    }
    if (defined('INSTITUTIONALSTAFF')) {
        $smarty->assign('INSTITUTIONALSTAFF', true);
    }
460

461
462
    $smarty->assign('LOGGEDIN', $USER->is_logged_in());
    if ($USER->is_logged_in()) {
463
        global $SELECTEDSUBNAV; // It's evil, but rightnav & mainnav stuff are now in different templates.
464
        $smarty->assign('MAINNAV', main_nav());
465
        $mainnavsubnav = $SELECTEDSUBNAV;
466
        $smarty->assign('RIGHTNAV', right_nav());
467
468
469
470
471
472
473
474
475
        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);
        }
476
    }
477
    else {
478
        $smarty->assign('languageform', language_select_form());
479
    }
480
    $smarty->assign('FOOTERMENU', footer_menu());
481

482
    $smarty->assign_by_ref('USER', $USER);
483
    $smarty->assign('SESSKEY', $USER->get('sesskey'));
484
    $smarty->assign_by_ref('JAVASCRIPT', $javascript_array);
485
486
    $siteclosedforupgrade = get_config('siteclosed');
    if ($siteclosedforupgrade && get_config('disablelogin')) {
487
        $smarty->assign('SITECLOSED', 'logindisabled');
488
489
    }
    else if ($siteclosedforupgrade || get_config('siteclosedbyadmin')) {
490
        $smarty->assign('SITECLOSED', 'loginallowed');
491
    }
492

493
494
    if ((!isset($extraconfig['pagehelp']) || $extraconfig['pagehelp'] !== false)
        and $help = has_page_help()) {
495
496
497
        $smarty->assign('PAGEHELPNAME', $help[0]);
        $smarty->assign('PAGEHELPICON', $help[1]);
    }
498
    if (defined('GROUP')) {
499
        require_once('group.php');
500
501
502
503
504
505
        if ($group = group_current_group()) {
            $smarty->assign('GROUP', $group);
            if (!defined('NOGROUPMENU')) {
                $smarty->assign('SUBPAGENAV', group_get_menu_tabs());
                $smarty->assign('PAGEHEADING', $group->name);
            }
506
        }
507
    }
508

Martyn Smith's avatar
Martyn Smith committed
509
    // ---------- sideblock stuff ----------
510
511
    $sidebars = !isset($extraconfig['sidebars']) || $extraconfig['sidebars'] !== false;
    if ($sidebars && !defined('INSTALLER') && (!defined('MENUITEM') || substr(MENUITEM, 0, 5) != 'admin')) {
512
        if (get_config('installed') && !$adminsection) {
513
514
515
516
            $data = site_menu();
            if (!empty($data)) {
                $smarty->assign('SITEMENU', site_menu());
                $SIDEBLOCKS[] = array(
517
                    'name'   => 'linksandresources',
518
519
520
521
522
523
                    'weight' => 10,
                    'data'   => $data,
                );
            }
        }

524
525
        if ($USER->is_logged_in() && defined('MENUITEM') &&
            (substr(MENUITEM, 0, 11) == 'myportfolio' || substr(MENUITEM, 0, 7) == 'content')) {
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
            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
541
        }
Clare Lenihan's avatar
Clare Lenihan committed
542

543
        if ($USER->is_logged_in() && !$adminsection) {
544
545
            $SIDEBLOCKS[] = array(
                'name'   => 'profile',
546
                'id'     => 'sb-profile',
547
548
549
                'weight' => -20,
                'data'   => profile_sideblock()
            );
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
            $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) {
565
566
                $SIDEBLOCKS[] = array(
                    'name'   => 'onlineusers',
567
                    'id'     => 'sb-onlineusers',
568
569
570
571
                    'weight' => -10,
                    'data'   => onlineusers_sideblock(),
                );
            }
572
        }
Martyn Smith's avatar
Martyn Smith committed
573

Richard Mansfield's avatar
Richard Mansfield committed
574
575
576
        if(defined('GROUP')) {
            $SIDEBLOCKS[] = array(
                'name'   => 'group',
577
                'id'     => 'sb-groupnav',
Richard Mansfield's avatar
Richard Mansfield committed
578
579
580
581
582
                'weight' => -10,
                'data'   => group_sideblock()
            );
        }

583
        if (!$USER->is_logged_in() && !(get_config('siteclosed') && get_config('disablelogin'))) {
584
            $SIDEBLOCKS[] = array(
585
586
                'name'   => 'login',
                'weight' => -10,
587
                'id'     => 'sb-loginbox',
588
589
590
                'data'   => array(
                    'loginform' => auth_generate_login_form(),
                ),
591
592
            );
        }
593

594
595
596
597
598
599
600
601
602
        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
603
604
        }

605
606
607
608
609
        if (isset($extraconfig['sideblocks']) && is_array($extraconfig['sideblocks'])) {
            foreach ($extraconfig['sideblocks'] as $sideblock) {
                $SIDEBLOCKS[] = $sideblock;
            }
        }
Martyn Smith's avatar
Martyn Smith committed
610

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

613
614
615
        // 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.
616
        $sidebars = $sidebars && !empty($SIDEBLOCKS);
617
        $SIDEBLOCKS = array('left' => array(), 'right' => $SIDEBLOCKS);
618

619
        $smarty->assign('userauthinstance', $SESSION->get('authinstance'));
620
        $smarty->assign('MNETUSER', $SESSION->get('mnetuser'));
621
        $smarty->assign('SIDEBLOCKS', $SIDEBLOCKS);
622
        $smarty->assign('SIDEBARS', $sidebars);
623

624
625
    }

626
627
628
629
630
    if (is_array($HEADDATA) && !empty($HEADDATA)) {
        $headers = array_merge($HEADDATA, $headers);
    }
    $smarty->assign_by_ref('HEADERS', $headers);

631
632
    if ($USER->get('parentuser')) {
        $smarty->assign('USERMASQUERADING', true);
633
        $smarty->assign('masqueradedetails', get_string('youaremasqueradingas', 'mahara', display_name($USER)));
634
635
        $smarty->assign('becomeyouagain',
            ' <a href="' . hsc($wwwroot) . 'admin/users/changeuser.php?restore=1">'
636
            . get_string('becomeadminagain', 'admin', hsc($USER->get('parentuser')->name))
637
            . '</a>');
638
    }
Martyn Smith's avatar
Martyn Smith committed
639

640
641
642
    return $smarty;
}

643
644
645
646
647
648
649
650
651
652
653
654
655
656

/**
 * 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 = '';

657
658
659
660
661
    /**
     * A user may have had the header logo overridden by an institution
     */
    public $headerlogo;

662
663
664
665
666
    /**
     * Additional stylesheets to display after the basename theme's stylesheets
     */
    public $addedstylesheets;

667
668
669
670
671
672
673
674
675
676
    /**
     * A human-readable version of the theme name
     */
    public $displayname = '';

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

677
678
679
680
681
682
683
684
685
686
    /**
     * Directories where to look for templates by default
     */
    public $templatedirs = array();

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

687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
    /**
     * 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;
734
            $themedata = null;
735
736
        }
        else if ($arg instanceof User) {
737
            $themedata = $arg->get_themedata();
738
739
740
741
        }
        else if (is_int($arg)) {
            $user = new User();
            $user->find_by_id($arg);
742
            $themedata = $user->get_themedata();
743
744
745
746
747
        }
        else {
            throw new SystemException("Argument to Theme::__construct was not a theme name, user object or user ID");
        }

748
749
750
751
        if (isset($themedata)) {
            $themename = $themedata->basename;
        }

752
        if (empty($themename)) {
753
            // Theme to show to when no theme has been suggested
754
755
756
            if (!$themename = get_config('theme')) {
                $themename = 'raw';
            }
757
        }
758
759

        // check the validity of the name
760
        if (!$this->name_is_valid($themename)) {
761
762
            throw new SystemException("Theme name is in invalid form: '$themename'");
        }
763
764

        $this->init_theme($themename, $themedata);
765
766
767
768
769
770
771
772
    }

    /**
     * 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);
773
774
775
776
777
    }

    /**
     * Given a theme name, reads in all config and sets fields on this object
     */
778
    private function init_theme($themename, $themedata) {
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
        $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)) {
796
            $this->displayname = $this->basename;
797
798
799
800
801
        }
        if (!isset($theme->parent) || !$theme->parent) {
            $theme->parent = 'raw';
        }

802
803
804
        $this->templatedirs[] = get_config('docroot') . 'theme/' . $this->basename . '/templates/';
        $this->inheritance[]  = $this->basename;

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

807
808
809
810
811
812
813
814
        // 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) {
815
                if (!isset($this->$key) || !$this->$key) {
816
817
818
                    $this->$key = $value;
                }
            }
819
820
            $this->templatedirs[] = get_config('docroot') . 'theme/' . $currenttheme . '/templates/';
            $this->inheritance[]  = $currenttheme;
821
        }
822
823
824
825

        if (!empty($themedata->headerlogo)) {
            $this->headerlogo = $themedata->headerlogo;
        }
826
827
828
        if (!empty($themedata->stylesheets)) {
            $this->addedstylesheets = $themedata->stylesheets;
        }
829
830
    }

831
832
833
    /**
     * stuff
     */
834
835
    public function get_url($filename, $all=false, $plugindirectory='') {
        return $this->_get_path($filename, $all, $plugindirectory, get_config('wwwroot'));
836
837
    }

838
839
    public function get_path($filename, $all=false, $plugindirectory='') {
        return $this->_get_path($filename, $all, $plugindirectory, get_config('docroot'));
840
841
    }

842
    private function _get_path($filename, $all, $plugindirectory, $returnprefix) {
843
        $list = array();
844
        $plugindirectory = ($plugindirectory && substr($plugindirectory, -1) != '/') ? $plugindirectory . '/' : $plugindirectory;
845
846

        foreach ($this->inheritance as $themedir) {
847
            if (is_readable(get_config('docroot') . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename)) {
848
                if ($all) {
849
                    $list[$themedir] = $returnprefix . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename;
850
851
                }
                else {
852
                    return $returnprefix . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename;
853
854
855
856
857
858
859
860
                }
            }
        }
        if ($all) {
            return $list;
        }

        $extra = '';
861
862
        if ($plugindirectory) {
            $extra = ", plugindir $plugindirectory";
863
864
        }
        log_debug("Missing file in theme {$this->basename}{$extra}: $filename");
865
        return $returnprefix . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename;
866
867
    }

868
869
870
871
872
873
    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');
    }
874

875
876
877
878
    public function facebook_logo() {
        return $this->get_url('images/site-logo4facebook.png');
    }

879
880
881
    public function additional_stylesheets() {
        return $this->addedstylesheets;
    }
882
883
884
}


885
886
/** 
 * Returns the lists of strings used in the .js files
887
 * @return array                   
888
889
 */

890
function jsstrings() {
Martyn Smith's avatar
Martyn Smith committed
891
    return array(
892
893
894
       'mahara' => array(                        // js file
            'mahara' => array(                   // section
                'namedfieldempty',               // string name
895
                'processing',
896
897
                'unknownerror',
                'loading',
Martyn Smith's avatar
Martyn Smith committed
898
                'showtags',
899
                'couldnotgethelp',
900
901
902
903
904
905
                'password',
                'username',
                'login',
                'sessiontimedout',
                'loginfailed',
                'home',
906
                'youhavenottaggedanythingyet',
907
            ),
908
909
        ),
        'tablerenderer' => array(
910
911
912
913
914
915
            'mahara' => array(
                'firstpage',
                'nextpage',
                'prevpage',
                'lastpage',
            )
916
        ),
917
918
919
        'views' => array(
            'view' => array(
                'confirmdeleteblockinstance',
920
                'blocksinstructionajax',
921
922
            ),
        ),
Martyn Smith's avatar
Martyn Smith committed
923
924
925
    );
}

Martyn Smith's avatar
Martyn Smith committed
926
function themepaths() {
927
928
929
930
931
932

    static $paths;
    if (empty($paths)) {
        $paths = array(
            'mahara' => array(
                'images/icon_close.gif',
933
                'images/edit.gif',
934
935
936
                'images/failure.gif',
                'images/loading.gif',
                'images/success.gif',
937
                'images/icon_problem.gif',
938
                'images/icon_help.gif',
939
                'style/js.css',
940
941
942
943
            ),
        );
    }
    return $paths;
Martyn Smith's avatar
Martyn Smith committed
944
945
}

946
947
948
949
950
951
/** 
 * Takes an array of string identifiers and returns an array of the
 * corresponding strings, quoted for use in inline javascript here
 * docs.
 */

952
953
954
955
function quotestrings($strings) {
    $qstrings = array();
    foreach ($strings as $section => $tags) {
        foreach ($tags as $tag) {
956
            $qstrings[$tag] = json_encode(get_string($tag, $section));
957
        }
958
    }
959
    return $qstrings;
960
961
}

962
963
964
965
966
967
968
/** 
 * 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() {
969
970
971
    global $THEME;
    log_warn("theme_setup() is deprecated - please use the global \$THEME object instead");
    return $THEME;
972
973
974
975
976
977
978
979
}

/** 
 * 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
 */
980
function theme_get_url($location, $pluginlocation='', $all = false) {
981
982
983
984
985
986
    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);
987
    }
988
    return $THEME->get_url($location, $all, $plugintype, $pluginname);
989
990
}

991
992
993
994
995
996
/** 
 * 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
 */
997
function theme_get_path($location, $pluginlocation='', $all=false) {
998
999
1000
1001
1002
1003
    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);
1004
    }
1005
    return $THEME->get_path($location, $all, $plugintype, $pluginname);
1006
1007
}

1008
1009
1010
1011
1012
/**
 * This function sends headers suitable for all JSON returning scripts.
 *
 */
function json_headers() {
1013
    // @todo Catalyst IT Ltd
Martyn Smith's avatar