web.php 96.8 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
                $extrasetup = isset($extraconfig['tinymcesetup']) ? $extraconfig['tinymcesetup'] : '';
107

108
109
110
111
112
113
114
115
                $adv_buttons = array(
                    "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,iespell,cleanup,separator,link,unlink,separator,code",
                    "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.
116
117
                $tinymce_langdir = $langdirection == 'rtl' ? 'rtl' : 'ltr';
                $toolbar_align = 'left';
118
119
120

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

154
                $headers[] = <<<EOF
155
156
157
158
<script type="text/javascript">
tinyMCE.init({
    button_tile_map: true,
    {$tinymce_config}
159
    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]",
160
    urlconverter_callback : "custom_urlconvert",
161
    language: '{$language}',
162
    directionality: "{$tinymce_langdir}",
163
    content_css : {$content_css},
164
    //document_base_url: {$jswwwroot},
165
    remove_script_host: false,
166
167
168
169
170
171
172
    relative_urls: false,
    setup: function(ed) {
        ed.onInit.add(function(ed) {
            if (editor_to_focus && typeof(editor_to_focus) == 'string' && ed.editorId == editor_to_focus) {
                ed.focus();
            }
        });
173
        {$extrasetup}
174
    }
175
});
176
function custom_urlconvert (u, n, e) {
177
  // Don't convert the url on the skype status buttons.
178
179
  if (u.indexOf('skype:') == 0) {
      return u;
180
  }
181
182
183
184
185
186
187
188
189
190
191
192
193
194
  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;
195
}
196
197
198
</script>

EOF;
199
200
201
202
203
204
205
                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]);
206
            }
207
        }
208
209
    }

210
    if (get_config('developermode') & DEVMODE_UNPACKEDJS) {
211
        $javascript_array[] = $jsroot . 'MochiKit/MochiKit.js';
212
213
214
215
        $javascript_array[] = $jsroot . 'MochiKit/Position.js';
        $javascript_array[] = $jsroot . 'MochiKit/Color.js';
        $javascript_array[] = $jsroot . 'MochiKit/Visual.js';
        $javascript_array[] = $jsroot . 'MochiKit/DragAndDrop.js';
216
        $javascript_array[] = $jsroot . 'MochiKit/Format.js';
217
218
219
220
    }
    else {
        $javascript_array[] = $jsroot . 'MochiKit/Packed.js';
    }
Martyn Smith's avatar
Martyn Smith committed
221
    $javascript_array[] = $jsroot . 'keyboardNavigation.js';
222

223
    $strings = array();
224
225
226
227
228
229
230
231
232
    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);
        }
233
234
    }

235
    $jsstrings = jsstrings();
Martyn Smith's avatar
Martyn Smith committed
236
    $themepaths = themepaths();
237

Richard Mansfield's avatar
Richard Mansfield committed
238
    foreach ($javascript as $jsfile) {
239
240
241
242
        // 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.
243
        if (strpos($jsfile, '/') === false) {
244
            $javascript_array[] = $jsroot . $jsfile . '.js';
245
            if (isset($jsstrings[$jsfile])) {
246
247
248
                foreach ($jsstrings[$jsfile] as $section => $tags) {
                    foreach ($tags as $tag) {
                        $strings[$tag] = get_raw_string($tag, $section);
249
250
251
                    }
                }
            }
Martyn Smith's avatar
Martyn Smith committed
252
253
            if (isset($themepaths[$jsfile])) {
                foreach ($themepaths[$jsfile] as $themepath) {
Nigel McNie's avatar
Nigel McNie committed
254
                    $theme_list[$themepath] = $THEME->get_url($themepath);
Martyn Smith's avatar
Martyn Smith committed
255
256
                }
            }
257
258
        }
        else {
259
            // A .js file with a fully specified path
260
            $javascript_array[] = $wwwroot . $jsfile;
261
262
263
264
265
266
            // 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]);
267
                $name = substr($bits[3], 0, strpos($bits[3], '.js'));
268
269
                if (is_callable(array($pluginclass, 'jsstrings'))) {
                    $tempstrings = call_static_method($pluginclass, 'jsstrings', $name);
270
271
272
                    foreach ($tempstrings as $section => $tags) {
                        foreach ($tags as $tag) {
                            $strings[$tag] = get_raw_string($tag, $section);
273
274
                        }
                    }
Richard Mansfield's avatar
Richard Mansfield committed
275
                }
276
277
278
279
280
281
282
283
284
                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
285
286
287
                if (is_callable(array($pluginclass, 'themepaths'))) {
                    $tmpthemepaths = call_static_method($pluginclass, 'themepaths', $name);
                    foreach ($tmpthemepaths as $themepath) {
Nigel McNie's avatar
Nigel McNie committed
288
                        $theme_list[$themepath] = $THEME->get_url($themepath);
Martyn Smith's avatar
Martyn Smith committed
289
290
                    }
                }
Martyn Smith's avatar
Martyn Smith committed
291
            }
Martyn Smith's avatar
Martyn Smith committed
292
        }
293
    }
294
295

    $javascript_array[] = $jsroot . 'mahara.js';
296
    if (get_config('developermode') & DEVMODE_DEBUGJS) {
297
        $javascript_array[] = $jsroot . 'debug.js';
298
    }
299

300
301
302
    foreach ($jsstrings['mahara'] as $section => $tags) {
        foreach ($tags as $tag) {
            $strings[$tag] = get_raw_string($tag, $section);
303
304
        }
    }
305
306
    if (isset($extraconfig['themepaths']) && is_array($extraconfig['themepaths'])) {
        foreach ($extraconfig['themepaths'] as $themepath) {
Nigel McNie's avatar
Nigel McNie committed
307
            $theme_list[$themepath] = $THEME->get_url($themepath);
308
309
        }
    }
310

311
    $stringjs = '<script type="text/javascript">';
312
    $stringjs .= 'var strings = ' . json_encode($strings) . ';';
313
314
    $stringjs .= '</script>';

315
    // stylesheet set up - if we're in a plugin also get its stylesheet
Nigel McNie's avatar
Nigel McNie committed
316
    $stylesheets = array_reverse(array_values($THEME->get_url('style/style.css', true)));
317
    if (defined('SECTION_PLUGINTYPE') && defined('SECTION_PLUGINNAME') && SECTION_PLUGINTYPE != 'core') {
Nigel McNie's avatar
Nigel McNie committed
318
        if ($pluginsheets = $THEME->get_url('style/style.css', true, SECTION_PLUGINTYPE . '/' . SECTION_PLUGINNAME)) {
319
320
321
            $stylesheets = array_merge($stylesheets, array_reverse($pluginsheets));
        }
    }
322
    if (defined('ADMIN') || defined('INSTITUTIONALADMIN')) {
323
324
325
        if ($adminsheets = $THEME->get_url('style/admin.css', true)) {
            $stylesheets = array_merge($stylesheets, array_reverse($adminsheets));
        }
326
    }
327
    if (get_config('developermode') & DEVMODE_DEBUGCSS) {
328
329
        $stylesheets[] = get_config('wwwroot') . 'theme/debug.css';
    }
330

331
332
333
    // look for extra stylesheets
    if (isset($extraconfig['stylesheets']) && is_array($extraconfig['stylesheets'])) {
        foreach ($extraconfig['stylesheets'] as $extrasheet) {
334
            if ($sheets = $THEME->get_url($extrasheet, true)) {
Nigel McNie's avatar
Nigel McNie committed
335
                $stylesheets = array_merge($stylesheets, array_reverse(array_values($sheets)));
336
337
338
339
            }
        }
    }

340
    // Include rtl.css for right-to-left langs
341
    if ($langdirection == 'rtl') {
342
        $smarty->assign('LANGDIRECTION', 'rtl');
343
344
345
346
347
        if ($rtlsheets = $THEME->get_url('style/rtl.css', true)) {
            $stylesheets = array_merge($stylesheets, array_reverse($rtlsheets));
        }
    }

348
    $smarty->assign('STRINGJS', $stringjs);
349

350
    $smarty->assign('STYLESHEETLIST', $stylesheets);
351
352
353
354
    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))); 
    }
355

356

357
358
359
360
    $sitename = get_config('sitename');
    if (!$sitename) {
       $sitename = 'Mahara';
    }
361
    $smarty->assign('sitename', $sitename);
362

Martyn Smith's avatar
Martyn Smith committed
363
    if (defined('TITLE')) {
364
        $smarty->assign('PAGETITLE', TITLE . ' - ' . $sitename);
365
        $smarty->assign('heading', TITLE);
Martyn Smith's avatar
Martyn Smith committed
366
367
    }
    else {
368
        $smarty->assign('PAGETITLE', $sitename);
Martyn Smith's avatar
Martyn Smith committed
369
370
    }

371
372
373
    if (defined('PUBLIC')) {
        $smarty->assign('PUBLIC', true);
    }
374
375
376
    if (defined('ADMIN')) {
        $smarty->assign('ADMIN', true);
    }
377
378
379
    if (defined('INSTITUTIONALADMIN')) {
        $smarty->assign('INSTITUTIONALADMIN', true);
    }
380

381
382
    $smarty->assign('LOGGEDIN', $USER->is_logged_in());
    if ($USER->is_logged_in()) {
383
        $smarty->assign('MAINNAV', main_nav());
384
        $smarty->assign('RIGHTNAV', right_nav());
385
    }
386
387
    else {
        $smarty->assign('sitedefaultlang', get_string('sitedefault', 'admin') . ' (' . 
388
                        get_string_from_language(get_config('lang'), 'thislanguage') . ')');
389
390
        $smarty->assign('LANGUAGES', get_languages());
    }
391
    $smarty->assign('FOOTERMENU', footer_menu());
392

393
    $smarty->assign_by_ref('USER', $USER);
394
    $smarty->assign('SESSKEY', $USER->get('sesskey'));
395
    $smarty->assign_by_ref('JAVASCRIPT', $javascript_array);
396
    $smarty->assign_by_ref('HEADERS', $headers);
397
398
    $siteclosedforupgrade = get_config('siteclosed');
    if ($siteclosedforupgrade && get_config('disablelogin')) {
399
        $smarty->assign('SITECLOSED', 'logindisabled');
400
401
    }
    else if ($siteclosedforupgrade || get_config('siteclosedbyadmin')) {
402
        $smarty->assign('SITECLOSED', 'loginallowed');
403
    }
404

405
406
    if ((!isset($extraconfig['pagehelp']) || $extraconfig['pagehelp'] !== false)
        and $help = has_page_help()) {
407
408
409
        $smarty->assign('PAGEHELPNAME', $help[0]);
        $smarty->assign('PAGEHELPICON', $help[1]);
    }
410
    if (defined('GROUP')) {
411
        require_once('group.php');
412
413
        $group = group_current_group();
        $smarty->assign('GROUP', $group);
414
        $smarty->assign('SUBPAGENAV', group_get_menu_tabs());
415
        $smarty->assign('PAGEHEADING', $group->name);
416
    }
417

Martyn Smith's avatar
Martyn Smith committed
418
    // ---------- sideblock stuff ----------
419
420
    $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
421
        if (get_config('installed') && !defined('ADMIN') && !defined('INSTITUTIONALADMIN')) {
422
423
424
425
            $data = site_menu();
            if (!empty($data)) {
                $smarty->assign('SITEMENU', site_menu());
                $SIDEBLOCKS[] = array(
426
                    'name'   => 'linksandresources',
427
428
429
430
431
432
433
                    'weight' => 10,
                    'data'   => $data,
                );
            }
        }

        if ($USER->is_logged_in() && defined('MENUITEM') && substr(MENUITEM, 0, 11) == 'myportfolio') {
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
            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
449
        }
Clare Lenihan's avatar
Clare Lenihan committed
450

Richard Mansfield's avatar
Richard Mansfield committed
451
        if($USER->is_logged_in() && !defined('ADMIN') && !defined('INSTITUTIONALADMIN')) {
452
453
            $SIDEBLOCKS[] = array(
                'name'   => 'profile',
454
                'id'     => 'sb-profile',
455
456
457
                'weight' => -20,
                'data'   => profile_sideblock()
            );
458
459
460
            if (get_config('showonlineuserssideblock')) {
                $SIDEBLOCKS[] = array(
                    'name'   => 'onlineusers',
461
                    'id'     => 'sb-onlineusers',
462
463
464
465
                    'weight' => -10,
                    'data'   => onlineusers_sideblock(),
                );
            }
466
        }
Martyn Smith's avatar
Martyn Smith committed
467

Richard Mansfield's avatar
Richard Mansfield committed
468
469
470
        if(defined('GROUP')) {
            $SIDEBLOCKS[] = array(
                'name'   => 'group',
471
                'id'     => 'sb-groupnav',
Richard Mansfield's avatar
Richard Mansfield committed
472
473
474
475
476
                'weight' => -10,
                'data'   => group_sideblock()
            );
        }

477
        if (!$USER->is_logged_in() && !(get_config('siteclosed') && get_config('disablelogin'))) {
478
            $SIDEBLOCKS[] = array(
479
480
                'name'   => 'login',
                'weight' => -10,
481
                'id'     => 'sb-loginbox',
482
483
484
                'data'   => array(
                    'loginform' => auth_generate_login_form(),
                ),
485
486
            );
        }
487

488
489
490
491
492
493
494
495
496
        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
497
498
        }

499
500
501
502
503
        if (isset($extraconfig['sideblocks']) && is_array($extraconfig['sideblocks'])) {
            foreach ($extraconfig['sideblocks'] as $sideblock) {
                $SIDEBLOCKS[] = $sideblock;
            }
        }
Martyn Smith's avatar
Martyn Smith committed
504

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

507
508
509
        // 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.
510
        $SIDEBLOCKS = array('left' => array(), 'right' => $SIDEBLOCKS);
511

512
        $smarty->assign('userauthinstance', $SESSION->get('authinstance'));
513
        $smarty->assign('MNETUSER', $SESSION->get('mnetuser'));
514
        $smarty->assign('SIDEBLOCKS', $SIDEBLOCKS);
515
        $smarty->assign('SIDEBARS', $sidebars);
516

517
518
519
520
    }

    if ($USER->get('parentuser')) {
        $smarty->assign('USERMASQUERADING', true);
521
        $smarty->assign('masqueradedetails', get_string('youaremasqueradingas', 'mahara', display_name($USER)));
522
523
        $smarty->assign('becomeyouagain',
            ' <a href="' . hsc($wwwroot) . 'admin/users/changeuser.php?restore=1">'
524
            . get_string('becomeadminagain', 'admin', hsc($USER->get('parentuser')->name))
525
            . '</a>');
526
    }
Martyn Smith's avatar
Martyn Smith committed
527

528
529
530
    return $smarty;
}

531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554

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

    /**
     * A human-readable version of the theme name
     */
    public $displayname = '';

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

555
556
557
558
559
560
561
562
563
564
    /**
     * Directories where to look for templates by default
     */
    public $templatedirs = array();

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

565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
    /**
     * 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;
        }
        else if ($arg instanceof User) {
            $themename = $arg->get('theme');
        }
        else if (is_int($arg)) {
            $user = new User();
            $user->find_by_id($arg);
            $themename = $user->get('theme');
        }
        else {
            throw new SystemException("Argument to Theme::__construct was not a theme name, user object or user ID");
        }

        if (!$themename) {
            // Theme to show to when no theme has been suggested
627
628
629
            if (!$themename = get_config('theme')) {
                $themename = 'raw';
            }
630
631
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
        }
        $this->init_theme($themename);
    }

    /**
     * Given a theme name, reads in all config and sets fields on this object
     */
    private function init_theme($themename) {
        if (!preg_match('/^[a-zA-Z0-9_-]+$/', $themename)) {
            throw new SystemException("Theme name is in invalid form: '$themename'");
        }

        $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)) {
659
            $this->displayname = $this->basename;
660
661
662
663
664
        }
        if (!isset($theme->parent) || !$theme->parent) {
            $theme->parent = 'raw';
        }

665
666
667
        $this->templatedirs[] = get_config('docroot') . 'theme/' . $this->basename . '/templates/';
        $this->inheritance[]  = $this->basename;

668
669
670
671
672
673
674
675
        // 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) {
676
                if (!isset($this->$key) || !$this->$key) {
677
678
679
                    $this->$key = $value;
                }
            }
680
681
            $this->templatedirs[] = get_config('docroot') . 'theme/' . $currenttheme . '/templates/';
            $this->inheritance[]  = $currenttheme;
682
683
684
        }
    }

685
686
687
    /**
     * stuff
     */
688
689
    public function get_url($filename, $all=false, $plugindirectory='') {
        return $this->_get_path($filename, $all, $plugindirectory, get_config('wwwroot'));
690
691
    }

692
693
    public function get_path($filename, $all=false, $plugindirectory='') {
        return $this->_get_path($filename, $all, $plugindirectory, get_config('docroot'));
694
695
    }

696
    private function _get_path($filename, $all, $plugindirectory, $returnprefix) {
697
        $list = array();
698
        $plugindirectory = ($plugindirectory && substr($plugindirectory, -1) != DIRECTORY_SEPARATOR) ? $plugindirectory . DIRECTORY_SEPARATOR : $plugindirectory;
699
700

        foreach ($this->inheritance as $themedir) {
701
            if (is_readable(get_config('docroot') . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename)) {
702
                if ($all) {
703
                    $list[$themedir] = $returnprefix . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename;
704
705
                }
                else {
706
                    return $returnprefix . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename;
707
708
709
710
711
712
713
714
                }
            }
        }
        if ($all) {
            return $list;
        }

        $extra = '';
715
716
        if ($plugindirectory) {
            $extra = ", plugindir $plugindirectory";
717
718
        }
        log_debug("Missing file in theme {$this->basename}{$extra}: $filename");
719
        return $returnprefix . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename;
720
721
    }

722
723
724
}


725
726
/** 
 * Returns the lists of strings used in the .js files
727
 * @return array                   
728
729
 */

730
function jsstrings() {
Martyn Smith's avatar
Martyn Smith committed
731
    return array(
732
733
734
       'mahara' => array(                        // js file
            'mahara' => array(                   // section
                'namedfieldempty',               // string name
735
                'processing',
736
737
738
                'requiredfieldempty',
                'unknownerror',
                'loading',
Martyn Smith's avatar
Martyn Smith committed
739
                'showtags',
740
741
                'unreadmessages',
                'unreadmessage',
742
743
                'pendingfriend',
                'pendingfriends',
744
                'couldnotgethelp',
745
746
747
748
749
750
                'password',
                'username',
                'login',
                'sessiontimedout',
                'loginfailed',
                'home',
751
                'youhavenottaggedanythingyet',
752
            ),
753
754
        ),
        'tablerenderer' => array(
755
756
757
758
759
760
            'mahara' => array(
                'firstpage',
                'nextpage',
                'prevpage',
                'lastpage',
            )
761
        ),
762
        'friends' => array(
763
            'group' => array(
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
                'confirmremovefriend',
                'seeallviews',
                'noviewstosee',
                'sendmessage',
                'whymakemeyourfriend',
                'approverequest',
                'denyrequest',
                'pending',
                'removefromfriendslist',
                'views',
                'trysearchingforfriends',
                'nobodyawaitsfriendapproval',
                'sendfriendrequest',
                'addtomyfriends',
                'friendshiprequested',
                'userdoesntwantfriends',
                'existingfriend',
                'nosearchresultsfound',
782
                'reason',
783
                'requestfriendship',
784
                'cancel',
785
786
            ),
        ),
787
788
789
        'views' => array(
            'view' => array(
                'confirmdeleteblockinstance',
790
                'blocksinstructionajax',
791
                'Configure',
792
793
            ),
        ),
794
795
796
797
798
799
800
801
802
        'adminusersearch' => array(
            'admin' => array(
                'suspenduser',
                'suspensionreason',
            ),
            'mahara' => array(
                'cancel',
            ),
        ),
Martyn Smith's avatar
Martyn Smith committed
803
804
805
    );
}

Martyn Smith's avatar
Martyn Smith committed
806
function themepaths() {
807
808
809
810
811
812
813
814
815

    static $paths;
    if (empty($paths)) {
        $paths = array(
            'mahara' => array(
                'images/icon_close.gif',
                'images/failure.gif',
                'images/loading.gif',
                'images/success.gif',
816
                'images/icon_problem.gif',
817
                'images/icon_help.gif',
818
                'style/js.css',
819
820
821
822
            ),
        );
    }
    return $paths;
Martyn Smith's avatar
Martyn Smith committed
823
824
}

825
826
827
828
829
830
/** 
 * Takes an array of string identifiers and returns an array of the
 * corresponding strings, quoted for use in inline javascript here
 * docs.
 */

831
832
833
834
function quotestrings($strings) {
    $qstrings = array();
    foreach ($strings as $section => $tags) {
        foreach ($tags as $tag) {
835
            $qstrings[$tag] = json_encode(get_string($tag, $section));
836
        }
837
    }
838
    return $qstrings;
839
840
}

841
842
843
844
845
846
847
/** 
 * 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() {
848
849
850
    global $THEME;
    log_warn("theme_setup() is deprecated - please use the global \$THEME object instead");
    return $THEME;
851
852
853
854
855
856
857
858
}

/** 
 * 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
 */
859
function theme_get_url($location, $pluginlocation='', $all = false) {
860
861
862
863
864
865
    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);
866
    }
867
    return $THEME->get_url($location, $all, $plugintype, $pluginname);
868
869
}

870
871
872
873
874
875
/** 
 * 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
 */
876
function theme_get_path($location, $pluginlocation='', $all=false) {
877
878
879
880
881
882
    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);
883
    }
884
    return $THEME->get_path($location, $all, $plugintype, $pluginname);
885
886
}

887
888
889
890
891
/**
 * This function sends headers suitable for all JSON returning scripts.
 *
 */
function json_headers() {
892
    // @todo Catalyst IT Ltd
893
894
    // header('Content-type: text/x-json');
    header('Content-type: text/plain');
Martyn Smith's avatar
Martyn Smith committed
895
    header('Pragma: no-cache');
896
897
}

Richard Mansfield's avatar
Richard Mansfield committed
898
/**
899
 * This function sends a JSON message, and ends the script.
Richard Mansfield's avatar
Richard Mansfield committed
900
 *
901
902
903
904
905
906
907
908
 * 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
909
 */
910
function json_reply($error, $message, $returncode=0) {
Richard Mansfield's avatar
Richard Mansfield committed
911
    json_headers();
912
    echo json_encode(array('error' => $error, 'message' => $message, 'returnCode' => $returncode));
913
    perf_to_log();
Richard Mansfield's avatar
Richard Mansfield committed
914
915
916
    exit;
}

917
function _param_retrieve($name) {
918
919
920
921
922
923
924
    // prefer post
    if (isset($_POST[$name])) {
        $value = $_POST[$name];
    } 
    else if (isset($_GET[$name])) {
        $value = $_GET[$name];
    }
925
    else if (func_num_args() == 2) {
926
927
928
        $php_work_around = func_get_arg(1);
        return array($php_work_around, true);
    }
929
    else {
930
931
932
        throw new ParameterException("Missing parameter '$name' and no default supplied");
    }

933
    return array($value, false);
934
935
}

Martyn Smith's avatar
Martyn Smith committed
936
937
938
939
940
941
942
943
944
945
946
/**
 * This function returns a GET or POST parameter with optional default.  If the
 * default isn't specified and the parameter hasn't been sent, a
 * ParameterException exception is thrown
 *
 * @param string The GET or POST parameter you want returned
 * @param mixed [optional] the default value for this parameter
 *
 * @return string The value of the parameter
 *
 */
Martyn Smith's avatar
Martyn Smith committed
947
function param_variable($name) {
948
949
950
    $args = func_get_args();
    list ($value) = call_user_func_array('_param_retrieve', $args);
    return $value;
Martyn Smith's avatar
Martyn Smith committed
951
952
953
954
955
956
957
958
959
960
961
}

/**
 * This function returns a GET or POST parameter as an integer with optional
 * default.  If the default isn't specified and the parameter hasn't been sent,
 * a ParameterException exception is thrown. Likewise, if the parameter isn't a
 * valid integer, a ParameterException exception is thrown
 *
 * @param string The GET or POST parameter you want returned
 * @param mixed [optional] the default value for this parameter
 *
Nigel McNie's avatar
Nigel McNie committed
962
 * @return int The value of the parameter
Martyn Smith's avatar
Martyn Smith committed
963
964
 *
 */
Martyn Smith's avatar
Martyn Smith committed
965
function param_integer($name) {
966
967
968
969
970
971
    $args = func_get_args();

    list ($value, $defaultused) = call_user_func_array('_param_retrieve', $args);

    if ($defaultused) {
        return $value;
Martyn Smith's avatar
Martyn Smith committed
972
973
974
975
976
    }

    if (preg_match('/^\d+$/',$value)) {
        return (int)$value;
    }
977
978
979
    else if ($value == '' && isset($args[1])) {
        return $args[1];
    }
Martyn Smith's avatar
Martyn Smith committed
980

981
    throw new ParameterException("The '$name' parameter is not an integer");
Martyn Smith's avatar
Martyn Smith committed
982
983
}

984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
/**
 * This function returns a GET or POST parameter as an integer with optional
 * default.  If the default isn't specified and the parameter hasn't been sent,
 * a ParameterException exception is thrown. Likewise, if the parameter isn't a
 * valid integer(allows signed integers), a ParameterException exception is thrown
 *
 * @param string The GET or POST parameter you want returned
 * @param mixed [optional] the default value for this parameter
 *
 * @return int The value of the parameter
 *
 */
function param_signed_integer($name) {
    $args = func_get_args();

    list ($value, $defaultused) = call_user_func_array('_param_retrieve', $args);

    if ($defaultused) {
        return $value;
    }

    if (preg_match('#[+-]?[0-9]+#', $value)) {
        return (int)$value;
    }
    else if ($value == '' && isset($args[1])) {
        return $args[1];
    }

    throw new ParameterException("The '$name' parameter is not an integer");
}

Martyn Smith's avatar
Martyn Smith committed
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
/**
 * This function returns a GET or POST parameter as an alpha string with optional
 * default.  If the default isn't specified and the parameter hasn't been sent,
 * a ParameterException exception is thrown. Likewise, if the parameter isn't a
 * valid alpha string, a ParameterException exception is thrown
 *
 * Valid characters are a-z and A-Z
 *
 * @param string The GET or POST parameter you want returned
 * @param mixed [optional] the default value for this parameter
 *
 * @return string The value of the parameter
 *
 */
function param_alpha($name) {
1030
1031
1032
1033
1034
1035
    $args = func_get_args();

    list ($value, $defaultused) = call_user_func_array('_param_retrieve', $args);

    if ($defaultused) {
        return $value;
Martyn Smith's avatar
Martyn Smith committed
1036
1037
    }

Richard Mansfield's avatar
Richard Mansfield committed
1038
    if (preg_match('/^[a-zA-Z]+$/',$value)) {
Martyn Smith's avatar
Martyn Smith committed
1039
1040
1041
        return $value;
    }

1042
    throw new ParameterException("The '$name' parameter is not alphabetical only");
Martyn Smith's avatar
Martyn Smith committed
1043
1044
}

1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
/**
 * This function returns a GET or POST parameter as an alphanumeric string with optional
 * default.  If the default isn't specified and the parameter hasn't been sent,
 * a ParameterException exception is thrown. Likewise, if the parameter isn't a
 * valid alpha string, a ParameterException exception is thrown
 *
 * Valid characters are a-z and A-Z and 0-9
 *
 * @param string The GET or POST parameter you want returned
 * @param mixed [optional] the default value for this parameter
 *
 * @return string The value of the parameter
 *
 */
function param_alphanum($name) {
    $args = func_get_args();

    list ($value, $defaultused) = call_user_func_array('_param_retrieve', $args);

    if ($defaultused) {
        return $value;
    }

    if (preg_match('/^[a-zA-Z0-9]+$/',$value)) {
        return $value;
    }

1072
    throw new ParameterException("The '$name' parameter is not alphanumeric only");