web.php 123 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
    $checkarray = array(&$javascript, &$headers);
116
    $found_tinymce = false;
117
    foreach ($checkarray as &$check) {
118
119
120
121
        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';
122
                $content_css = json_encode($THEME->get_url('style/tinymce.css'));
123
                $language = substr(current_language(), 0, 2);
124
125
126
                if ($language != 'en' && !file_exists(get_config('docroot') . 'js/tinymce/langs/' . $language . '.js')) {
                    $language = 'en';
                }
127
                $extrasetup = isset($extraconfig['tinymcesetup']) ? $extraconfig['tinymcesetup'] : '';
128

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

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

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

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

EOF;
223
224
225
226
227
228
229
                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]);
230
            }
231
        }
232
233

        // If any page adds jquery explicitly, remove it from the list
234
235
236
        if (($key = array_search('jquery', $check)) !== false) {
            unset($check[$key]);
        }
237
    }
238

239
240
241
242
    // Make jQuery accessible with $j (Mochikit has $)
    $javascript_array[] = $jsroot . 'jquery/jquery.js';
    $headers[] = '<script type="text/javascript">$j=jQuery;</script>';

243
    if (get_config('developermode') & DEVMODE_UNPACKEDJS) {
244
        $javascript_array[] = $jsroot . 'MochiKit/MochiKit.js';
245
246
247
248
        $javascript_array[] = $jsroot . 'MochiKit/Position.js';
        $javascript_array[] = $jsroot . 'MochiKit/Color.js';
        $javascript_array[] = $jsroot . 'MochiKit/Visual.js';
        $javascript_array[] = $jsroot . 'MochiKit/DragAndDrop.js';
249
        $javascript_array[] = $jsroot . 'MochiKit/Format.js';
250
251
252
253
    }
    else {
        $javascript_array[] = $jsroot . 'MochiKit/Packed.js';
    }
Martyn Smith's avatar
Martyn Smith committed
254
    $javascript_array[] = $jsroot . 'keyboardNavigation.js';
255

256
    $strings = array();
257
258
259
260
261
262
263
264
265
    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);
        }
266
267
    }

268
    $jsstrings = jsstrings();
Martyn Smith's avatar
Martyn Smith committed
269
    $themepaths = themepaths();
270

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

    $javascript_array[] = $jsroot . 'mahara.js';
333
    if (get_config('developermode') & DEVMODE_DEBUGJS) {
334
        $javascript_array[] = $jsroot . 'debug.js';
335
    }
336

337
338
339
    foreach ($jsstrings['mahara'] as $section => $tags) {
        foreach ($tags as $tag) {
            $strings[$tag] = get_raw_string($tag, $section);
340
341
        }
    }
342
343
    if (isset($extraconfig['themepaths']) && is_array($extraconfig['themepaths'])) {
        foreach ($extraconfig['themepaths'] as $themepath) {
Nigel McNie's avatar
Nigel McNie committed
344
            $theme_list[$themepath] = $THEME->get_url($themepath);
345
346
        }
    }
347

348
    $stringjs = '<script type="text/javascript">';
349
    $stringjs .= 'var strings = ' . json_encode($strings) . ';';
350
    $stringjs .= "\nfunction plural(n) { return " . get_raw_string('pluralrule', 'langconfig') . "; }\n";
351
352
    $stringjs .= '</script>';

353
    // stylesheet set up - if we're in a plugin also get its stylesheet
Nigel McNie's avatar
Nigel McNie committed
354
    $stylesheets = array_reverse(array_values($THEME->get_url('style/style.css', true)));
355
    if (defined('SECTION_PLUGINTYPE') && defined('SECTION_PLUGINNAME') && SECTION_PLUGINTYPE != 'core') {
Nigel McNie's avatar
Nigel McNie committed
356
        if ($pluginsheets = $THEME->get_url('style/style.css', true, SECTION_PLUGINTYPE . '/' . SECTION_PLUGINNAME)) {
357
358
359
            $stylesheets = array_merge($stylesheets, array_reverse($pluginsheets));
        }
    }
360
361

    if ($adminsection = in_admin_section()) {
362
363
364
        if ($adminsheets = $THEME->get_url('style/admin.css', true)) {
            $stylesheets = array_merge($stylesheets, array_reverse($adminsheets));
        }
365
    }
366
    if (get_config('developermode') & DEVMODE_DEBUGCSS) {
367
368
        $stylesheets[] = get_config('wwwroot') . 'theme/debug.css';
    }
369

370
371
372
    // look for extra stylesheets
    if (isset($extraconfig['stylesheets']) && is_array($extraconfig['stylesheets'])) {
        foreach ($extraconfig['stylesheets'] as $extrasheet) {
373
            if ($sheets = $THEME->get_url($extrasheet, true)) {
Nigel McNie's avatar
Nigel McNie committed
374
                $stylesheets = array_merge($stylesheets, array_reverse(array_values($sheets)));
375
376
377
            }
        }
    }
378
379
380
    if ($sheets = $THEME->additional_stylesheets()) {
        $stylesheets = array_merge($stylesheets, $sheets);
    }
381

382
    // Include rtl.css for right-to-left langs
383
    if ($langdirection == 'rtl') {
384
        $smarty->assign('LANGDIRECTION', 'rtl');
385
386
387
388
389
        if ($rtlsheets = $THEME->get_url('style/rtl.css', true)) {
            $stylesheets = array_merge($stylesheets, array_reverse($rtlsheets));
        }
    }

390
    $smarty->assign('STRINGJS', $stringjs);
391

392
    $smarty->assign('STYLESHEETLIST', $stylesheets);
393
394
395
396
    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))); 
    }
397

398
399
400
401
    $dropdownmenu = get_config('dropdownmenu');
    if ($dropdownmenu) {
        $smarty->assign('DROPDOWNMENU', $dropdownmenu);
    }
402

403
404
405
406
    $sitename = get_config('sitename');
    if (!$sitename) {
       $sitename = 'Mahara';
    }
407
    $smarty->assign('sitename', $sitename);
408
    $smarty->assign('sitelogo', $THEME->header_logo());
409
410
    $smarty->assign('sitelogo4facebook', $THEME->facebook_logo());
    $smarty->assign('sitedescription4facebook', get_string('facebookdescription', 'mahara'));
411

Martyn Smith's avatar
Martyn Smith committed
412
    if (defined('TITLE')) {
413
        $smarty->assign('PAGETITLE', TITLE . ' - ' . $sitename);
414
        $smarty->assign('heading', TITLE);
Martyn Smith's avatar
Martyn Smith committed
415
416
    }
    else {
417
        $smarty->assign('PAGETITLE', $sitename);
Martyn Smith's avatar
Martyn Smith committed
418
419
    }

420
    $smarty->assign('PRODUCTIONMODE', get_config('productionmode'));
421
    if (function_exists('local_header_top_content')) {
422
        $sitetop = (isset($sitetop) ? $sitetop : '') . local_header_top_content();
423
424
425
    }
    if (isset($sitetop)) {
        $smarty->assign('SITETOP', $sitetop);
426
    }
427
428
429
    if (defined('PUBLIC')) {
        $smarty->assign('PUBLIC', true);
    }
430
431
432
    if (defined('ADMIN')) {
        $smarty->assign('ADMIN', true);
    }
433
434
435
    if (defined('INSTITUTIONALADMIN')) {
        $smarty->assign('INSTITUTIONALADMIN', true);
    }
436
437
438
439
440
441
    if (defined('STAFF')) {
        $smarty->assign('STAFF', true);
    }
    if (defined('INSTITUTIONALSTAFF')) {
        $smarty->assign('INSTITUTIONALSTAFF', true);
    }
442

443
444
    $smarty->assign('LOGGEDIN', $USER->is_logged_in());
    if ($USER->is_logged_in()) {
445
        global $SELECTEDSUBNAV; // It's evil, but rightnav & mainnav stuff are now in different templates.
446
        $smarty->assign('MAINNAV', main_nav());
447
        $mainnavsubnav = $SELECTEDSUBNAV;
448
        $smarty->assign('RIGHTNAV', right_nav());
449
450
451
452
453
454
455
456
457
        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);
        }
458
    }
459
    else {
460
        $smarty->assign('languageform', language_select_form());
461
    }
462
    $smarty->assign('FOOTERMENU', footer_menu());
463

464
    $smarty->assign_by_ref('USER', $USER);
465
    $smarty->assign('SESSKEY', $USER->get('sesskey'));
466
    $smarty->assign_by_ref('JAVASCRIPT', $javascript_array);
467
468
    $siteclosedforupgrade = get_config('siteclosed');
    if ($siteclosedforupgrade && get_config('disablelogin')) {
469
        $smarty->assign('SITECLOSED', 'logindisabled');
470
471
    }
    else if ($siteclosedforupgrade || get_config('siteclosedbyadmin')) {
472
        $smarty->assign('SITECLOSED', 'loginallowed');
473
    }
474

475
476
    if ((!isset($extraconfig['pagehelp']) || $extraconfig['pagehelp'] !== false)
        and $help = has_page_help()) {
477
478
479
        $smarty->assign('PAGEHELPNAME', $help[0]);
        $smarty->assign('PAGEHELPICON', $help[1]);
    }
480
    if (defined('GROUP')) {
481
        require_once('group.php');
482
483
484
485
486
487
        if ($group = group_current_group()) {
            $smarty->assign('GROUP', $group);
            if (!defined('NOGROUPMENU')) {
                $smarty->assign('SUBPAGENAV', group_get_menu_tabs());
                $smarty->assign('PAGEHEADING', $group->name);
            }
488
        }
489
    }
490

Martyn Smith's avatar
Martyn Smith committed
491
    // ---------- sideblock stuff ----------
492
493
    $sidebars = !isset($extraconfig['sidebars']) || $extraconfig['sidebars'] !== false;
    if ($sidebars && !defined('INSTALLER') && (!defined('MENUITEM') || substr(MENUITEM, 0, 5) != 'admin')) {
494
        if (get_config('installed') && !$adminsection) {
495
496
497
498
            $data = site_menu();
            if (!empty($data)) {
                $smarty->assign('SITEMENU', site_menu());
                $SIDEBLOCKS[] = array(
499
                    'name'   => 'linksandresources',
500
501
502
503
504
505
                    'weight' => 10,
                    'data'   => $data,
                );
            }
        }

506
507
        if ($USER->is_logged_in() && defined('MENUITEM') &&
            (substr(MENUITEM, 0, 11) == 'myportfolio' || substr(MENUITEM, 0, 7) == 'content')) {
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
            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
523
        }
Clare Lenihan's avatar
Clare Lenihan committed
524

525
        if ($USER->is_logged_in() && !$adminsection) {
526
527
            $SIDEBLOCKS[] = array(
                'name'   => 'profile',
528
                'id'     => 'sb-profile',
529
530
531
                'weight' => -20,
                'data'   => profile_sideblock()
            );
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
            $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) {
547
548
                $SIDEBLOCKS[] = array(
                    'name'   => 'onlineusers',
549
                    'id'     => 'sb-onlineusers',
550
551
552
553
                    'weight' => -10,
                    'data'   => onlineusers_sideblock(),
                );
            }
554
        }
Martyn Smith's avatar
Martyn Smith committed
555

Richard Mansfield's avatar
Richard Mansfield committed
556
557
558
        if(defined('GROUP')) {
            $SIDEBLOCKS[] = array(
                'name'   => 'group',
559
                'id'     => 'sb-groupnav',
Richard Mansfield's avatar
Richard Mansfield committed
560
561
562
563
564
                'weight' => -10,
                'data'   => group_sideblock()
            );
        }

565
        if (!$USER->is_logged_in() && !(get_config('siteclosed') && get_config('disablelogin'))) {
566
            $SIDEBLOCKS[] = array(
567
568
                'name'   => 'login',
                'weight' => -10,
569
                'id'     => 'sb-loginbox',
570
571
572
                'data'   => array(
                    'loginform' => auth_generate_login_form(),
                ),
573
574
            );
        }
575

576
577
578
579
580
581
582
583
584
        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
585
586
        }

587
588
589
590
591
        if (isset($extraconfig['sideblocks']) && is_array($extraconfig['sideblocks'])) {
            foreach ($extraconfig['sideblocks'] as $sideblock) {
                $SIDEBLOCKS[] = $sideblock;
            }
        }
Martyn Smith's avatar
Martyn Smith committed
592

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

595
596
597
        // 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.
598
        $sidebars = $sidebars && !empty($SIDEBLOCKS);
599
        $SIDEBLOCKS = array('left' => array(), 'right' => $SIDEBLOCKS);
600

601
        $smarty->assign('userauthinstance', $SESSION->get('authinstance'));
602
        $smarty->assign('MNETUSER', $SESSION->get('mnetuser'));
603
        $smarty->assign('SIDEBLOCKS', $SIDEBLOCKS);
604
        $smarty->assign('SIDEBARS', $sidebars);
605

606
607
    }

608
609
610
611
612
    if (is_array($HEADDATA) && !empty($HEADDATA)) {
        $headers = array_merge($HEADDATA, $headers);
    }
    $smarty->assign_by_ref('HEADERS', $headers);

613
614
    if ($USER->get('parentuser')) {
        $smarty->assign('USERMASQUERADING', true);
615
        $smarty->assign('masqueradedetails', get_string('youaremasqueradingas', 'mahara', display_name($USER)));
616
617
        $smarty->assign('becomeyouagain',
            ' <a href="' . hsc($wwwroot) . 'admin/users/changeuser.php?restore=1">'
618
            . get_string('becomeadminagain', 'admin', hsc($USER->get('parentuser')->name))
619
            . '</a>');
620
    }
Martyn Smith's avatar
Martyn Smith committed
621

622
623
624
    return $smarty;
}

625
626
627
628
629
630
631
632
633
634
635
636
637
638

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

639
640
641
642
643
    /**
     * A user may have had the header logo overridden by an institution
     */
    public $headerlogo;

644
645
646
647
648
    /**
     * Additional stylesheets to display after the basename theme's stylesheets
     */
    public $addedstylesheets;

649
650
651
652
653
654
655
656
657
658
    /**
     * A human-readable version of the theme name
     */
    public $displayname = '';

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

659
660
661
662
663
664
665
666
667
668
    /**
     * Directories where to look for templates by default
     */
    public $templatedirs = array();

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

669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
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
    /**
     * 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;
716
            $themedata = null;
717
718
        }
        else if ($arg instanceof User) {
719
            $themedata = $arg->get_themedata();
720
721
722
723
        }
        else if (is_int($arg)) {
            $user = new User();
            $user->find_by_id($arg);
724
            $themedata = $user->get_themedata();
725
726
727
728
729
        }
        else {
            throw new SystemException("Argument to Theme::__construct was not a theme name, user object or user ID");
        }

730
731
732
733
        if (isset($themedata)) {
            $themename = $themedata->basename;
        }

734
        if (empty($themename)) {
735
            // Theme to show to when no theme has been suggested
736
737
738
            if (!$themename = get_config('theme')) {
                $themename = 'raw';
            }
739
        }
740
741

        // check the validity of the name
742
        if (!$this->name_is_valid($themename)) {
743
744
            throw new SystemException("Theme name is in invalid form: '$themename'");
        }
745
746

        $this->init_theme($themename, $themedata);
747
748
749
750
751
752
753
754
    }

    /**
     * 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);
755
756
757
758
759
    }

    /**
     * Given a theme name, reads in all config and sets fields on this object
     */
760
    private function init_theme($themename, $themedata) {
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
        $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)) {
778
            $this->displayname = $this->basename;
779
780
781
782
783
        }
        if (!isset($theme->parent) || !$theme->parent) {
            $theme->parent = 'raw';
        }

784
785
786
        $this->templatedirs[] = get_config('docroot') . 'theme/' . $this->basename . '/templates/';
        $this->inheritance[]  = $this->basename;

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

789
790
791
792
793
794
795
796
        // 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) {
797
                if (!isset($this->$key) || !$this->$key) {
798
799
800
                    $this->$key = $value;
                }
            }
801
802
            $this->templatedirs[] = get_config('docroot') . 'theme/' . $currenttheme . '/templates/';
            $this->inheritance[]  = $currenttheme;
803
        }
804
805
806
807

        if (!empty($themedata->headerlogo)) {
            $this->headerlogo = $themedata->headerlogo;
        }
808
809
810
        if (!empty($themedata->stylesheets)) {
            $this->addedstylesheets = $themedata->stylesheets;
        }
811
812
    }

813
814
815
    /**
     * stuff
     */
816
817
    public function get_url($filename, $all=false, $plugindirectory='') {
        return $this->_get_path($filename, $all, $plugindirectory, get_config('wwwroot'));
818
819
    }

820
821
    public function get_path($filename, $all=false, $plugindirectory='') {
        return $this->_get_path($filename, $all, $plugindirectory, get_config('docroot'));
822
823
    }

824
    private function _get_path($filename, $all, $plugindirectory, $returnprefix) {
825
        $list = array();
826
        $plugindirectory = ($plugindirectory && substr($plugindirectory, -1) != '/') ? $plugindirectory . '/' : $plugindirectory;
827
828

        foreach ($this->inheritance as $themedir) {
829
            if (is_readable(get_config('docroot') . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename)) {
830
                if ($all) {
831
                    $list[$themedir] = $returnprefix . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename;
832
833
                }
                else {
834
                    return $returnprefix . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename;
835
836
837
838
839
840
841
842
                }
            }
        }
        if ($all) {
            return $list;
        }

        $extra = '';
843
844
        if ($plugindirectory) {
            $extra = ", plugindir $plugindirectory";
845
846
        }
        log_debug("Missing file in theme {$this->basename}{$extra}: $filename");
847
        return $returnprefix . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename;
848
849
    }

850
851
852
853
854
855
    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');
    }
856

857
858
859
860
    public function facebook_logo() {
        return $this->get_url('images/site-logo4facebook.png');
    }

861
862
863
    public function additional_stylesheets() {
        return $this->addedstylesheets;
    }
864
865
866
}


867
868
/** 
 * Returns the lists of strings used in the .js files
869
 * @return array                   
870
871
 */

872
function jsstrings() {
Martyn Smith's avatar
Martyn Smith committed
873
    return array(
874
875
876
       'mahara' => array(                        // js file
            'mahara' => array(                   // section
                'namedfieldempty',               // string name
877
                'processing',
878
879
                'unknownerror',
                'loading',
Martyn Smith's avatar
Martyn Smith committed
880
                'showtags',
881
                'couldnotgethelp',
882
883
884
885
886
887
                'password',
                'username',
                'login',
                'sessiontimedout',
                'loginfailed',
                'home',
888
                'youhavenottaggedanythingyet',
889
            ),
890
891
        ),
        'tablerenderer' => array(
892
893
894
895
896
897
            'mahara' => array(
                'firstpage',
                'nextpage',
                'prevpage',
                'lastpage',
            )
898
        ),
899
900
901
        'views' => array(
            'view' => array(
                'confirmdeleteblockinstance',
902
                'blocksinstructionajax',
903
904
            ),
        ),
Martyn Smith's avatar
Martyn Smith committed
905
906
907
    );
}

Martyn Smith's avatar
Martyn Smith committed
908
function themepaths() {
909
910
911
912
913
914

    static $paths;
    if (empty($paths)) {
        $paths = array(
            'mahara' => array(
                'images/icon_close.gif',
915
                'images/edit.gif',
916
917
918
                'images/failure.gif',
                'images/loading.gif',
                'images/success.gif',
919
                'images/icon_problem.gif',
920
                'images/icon_help.gif',
921
                'style/js.css',
922
923
924
925
            ),
        );
    }
    return $paths;
Martyn Smith's avatar
Martyn Smith committed
926
927
}

928
929
930
931
932
933
/** 
 * Takes an array of string identifiers and returns an array of the
 * corresponding strings, quoted for use in inline javascript here
 * docs.
 */

934
935
936
937
function quotestrings($strings) {
    $qstrings = array();
    foreach ($strings as $section => $tags) {
        foreach ($tags as $tag) {
938
            $qstrings[$tag] = json_encode(get_string($tag, $section));
939
        }
940
    }
941
    return $qstrings;
942
943
}

944
945
946
947
948
949
950
/** 
 * 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() {
951
952
953
    global $THEME;
    log_warn("theme_setup() is deprecated - please use the global \$THEME object instead");
    return $THEME;
954
955
956
957
958
959
960
961
}

/** 
 * 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
 */
962
function theme_get_url($location, $pluginlocation='', $all = false) {
963
964
965
966
967
968
    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);
969
    }
970
    return $THEME->get_url($location, $all, $plugintype, $pluginname);
971
972
}

973
974
975
976
977
978
/** 
 * 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
 */
979
function theme_get_path($location, $pluginlocation='', $all=false) {
980
981
982
983
984
985
    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);
986
    }
987
    return $THEME->get_path($location, $all, $plugintype, $pluginname);
988
989
}

990
991
992
993
994
/**
 * This function sends headers suitable for all JSON returning scripts.
 *
 */
function json_headers() {
Nigel McNie's avatar