web.php 100 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
                $adv_buttons = array(
                    "bold,italic,underline,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,bullist,numlist,separator,link,unlink,separator,code,fullscreen",
110
                    "bold,italic,underline,strikethrough,separator,forecolor,backcolor,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,hr,emotions,image,spellchecker,cleanup,separator,link,unlink,separator,code",
111
112
113
114
115
                    "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,spellchecker,inlinepopups,paste,autoresize",
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,autoresize",
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],iframe[src|width|height|align|title|class|type|frameborder|allowfullscreen]",
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
    relative_urls: false,
    setup: function(ed) {
        ed.onInit.add(function(ed) {
169
            if (typeof(editor_to_focus) == 'string' && ed.editorId == editor_to_focus) {
170
171
172
                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
211
212
213
214
215
        // Load jquery first, so that it doesn't break Mochikit
        if (($key = array_search('jquery', $check)) !== false) {
            $jquery = (get_config('developermode') & DEVMODE_UNPACKEDJS) ? 'jquery-1.3.2.js' : 'jquery-1.3.2.min.js';
            array_unshift($javascript_array, $jsroot . 'jquery/' . $jquery);
            // Make jQuery accessible with $j (Mochikit has $)
            $headers[] = '<script type="text/javascript">$j=jQuery;</script>';
            unset($check[$key]);
        }
216
    }
217

218
    if (get_config('developermode') & DEVMODE_UNPACKEDJS) {
219
        $javascript_array[] = $jsroot . 'MochiKit/MochiKit.js';
220
221
222
223
        $javascript_array[] = $jsroot . 'MochiKit/Position.js';
        $javascript_array[] = $jsroot . 'MochiKit/Color.js';
        $javascript_array[] = $jsroot . 'MochiKit/Visual.js';
        $javascript_array[] = $jsroot . 'MochiKit/DragAndDrop.js';
224
        $javascript_array[] = $jsroot . 'MochiKit/Format.js';
225
226
227
228
    }
    else {
        $javascript_array[] = $jsroot . 'MochiKit/Packed.js';
    }
Martyn Smith's avatar
Martyn Smith committed
229
    $javascript_array[] = $jsroot . 'keyboardNavigation.js';
230

231
    $strings = array();
232
233
234
235
236
237
238
239
240
    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);
        }
241
242
    }

243
    $jsstrings = jsstrings();
Martyn Smith's avatar
Martyn Smith committed
244
    $themepaths = themepaths();
245

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

    $javascript_array[] = $jsroot . 'mahara.js';
308
    if (get_config('developermode') & DEVMODE_DEBUGJS) {
309
        $javascript_array[] = $jsroot . 'debug.js';
310
    }
311

312
313
314
    foreach ($jsstrings['mahara'] as $section => $tags) {
        foreach ($tags as $tag) {
            $strings[$tag] = get_raw_string($tag, $section);
315
316
        }
    }
317
318
    if (isset($extraconfig['themepaths']) && is_array($extraconfig['themepaths'])) {
        foreach ($extraconfig['themepaths'] as $themepath) {
Nigel McNie's avatar
Nigel McNie committed
319
            $theme_list[$themepath] = $THEME->get_url($themepath);
320
321
        }
    }
322

323
    $stringjs = '<script type="text/javascript">';
324
    $stringjs .= 'var strings = ' . json_encode($strings) . ';';
325
326
    $stringjs .= '</script>';

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

343
344
345
    // look for extra stylesheets
    if (isset($extraconfig['stylesheets']) && is_array($extraconfig['stylesheets'])) {
        foreach ($extraconfig['stylesheets'] as $extrasheet) {
346
            if ($sheets = $THEME->get_url($extrasheet, true)) {
Nigel McNie's avatar
Nigel McNie committed
347
                $stylesheets = array_merge($stylesheets, array_reverse(array_values($sheets)));
348
349
350
351
            }
        }
    }

352
    // Include rtl.css for right-to-left langs
353
    if ($langdirection == 'rtl') {
354
        $smarty->assign('LANGDIRECTION', 'rtl');
355
356
357
358
359
        if ($rtlsheets = $THEME->get_url('style/rtl.css', true)) {
            $stylesheets = array_merge($stylesheets, array_reverse($rtlsheets));
        }
    }

360
    $smarty->assign('STRINGJS', $stringjs);
361

362
    $smarty->assign('STYLESHEETLIST', $stylesheets);
363
364
365
366
    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))); 
    }
367

368

369
370
371
372
    $sitename = get_config('sitename');
    if (!$sitename) {
       $sitename = 'Mahara';
    }
373
    $smarty->assign('sitename', $sitename);
374

Martyn Smith's avatar
Martyn Smith committed
375
    if (defined('TITLE')) {
376
        $smarty->assign('PAGETITLE', TITLE . ' - ' . $sitename);
377
        $smarty->assign('heading', TITLE);
Martyn Smith's avatar
Martyn Smith committed
378
379
    }
    else {
380
        $smarty->assign('PAGETITLE', $sitename);
Martyn Smith's avatar
Martyn Smith committed
381
382
    }

383
384
385
    if (function_exists('local_header_top_content')) {
        $smarty->assign('SITETOP', local_header_top_content());
    }
386
387
388
    if (defined('PUBLIC')) {
        $smarty->assign('PUBLIC', true);
    }
389
390
391
    if (defined('ADMIN')) {
        $smarty->assign('ADMIN', true);
    }
392
393
394
    if (defined('INSTITUTIONALADMIN')) {
        $smarty->assign('INSTITUTIONALADMIN', true);
    }
395

396
397
    $smarty->assign('LOGGEDIN', $USER->is_logged_in());
    if ($USER->is_logged_in()) {
398
        global $SELECTEDSUBNAV; // It's evil, but rightnav & mainnav stuff are now in different templates.
399
        $smarty->assign('MAINNAV', main_nav());
400
        $smarty->assign('RIGHTNAV', right_nav());
401
        $smarty->assign('SELECTEDSUBNAV', $SELECTEDSUBNAV);
402
    }
403
404
    else {
        $smarty->assign('sitedefaultlang', get_string('sitedefault', 'admin') . ' (' . 
405
                        get_string_from_language(get_config('lang'), 'thislanguage') . ')');
406
407
        $smarty->assign('LANGUAGES', get_languages());
    }
408
    $smarty->assign('FOOTERMENU', footer_menu());
409

410
    $smarty->assign_by_ref('USER', $USER);
411
    $smarty->assign('SESSKEY', $USER->get('sesskey'));
412
    $smarty->assign_by_ref('JAVASCRIPT', $javascript_array);
413
    $smarty->assign_by_ref('HEADERS', $headers);
414
415
    $siteclosedforupgrade = get_config('siteclosed');
    if ($siteclosedforupgrade && get_config('disablelogin')) {
416
        $smarty->assign('SITECLOSED', 'logindisabled');
417
418
    }
    else if ($siteclosedforupgrade || get_config('siteclosedbyadmin')) {
419
        $smarty->assign('SITECLOSED', 'loginallowed');
420
    }
421

422
423
    if ((!isset($extraconfig['pagehelp']) || $extraconfig['pagehelp'] !== false)
        and $help = has_page_help()) {
424
425
426
        $smarty->assign('PAGEHELPNAME', $help[0]);
        $smarty->assign('PAGEHELPICON', $help[1]);
    }
427
    if (defined('GROUP')) {
428
        require_once('group.php');
429
430
        $group = group_current_group();
        $smarty->assign('GROUP', $group);
431
432
433
434
        if (!defined('NOGROUPMENU')) {
            $smarty->assign('SUBPAGENAV', group_get_menu_tabs());
            $smarty->assign('PAGEHEADING', $group->name);
        }
435
    }
436

Martyn Smith's avatar
Martyn Smith committed
437
    // ---------- sideblock stuff ----------
438
439
    $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
440
        if (get_config('installed') && !defined('ADMIN') && !defined('INSTITUTIONALADMIN')) {
441
442
443
444
            $data = site_menu();
            if (!empty($data)) {
                $smarty->assign('SITEMENU', site_menu());
                $SIDEBLOCKS[] = array(
445
                    'name'   => 'linksandresources',
446
447
448
449
450
451
                    'weight' => 10,
                    'data'   => $data,
                );
            }
        }

452
453
        if ($USER->is_logged_in() && defined('MENUITEM') &&
            (substr(MENUITEM, 0, 11) == 'myportfolio' || substr(MENUITEM, 0, 7) == 'content')) {
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
            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
469
        }
Clare Lenihan's avatar
Clare Lenihan committed
470

Richard Mansfield's avatar
Richard Mansfield committed
471
        if($USER->is_logged_in() && !defined('ADMIN') && !defined('INSTITUTIONALADMIN')) {
472
473
            $SIDEBLOCKS[] = array(
                'name'   => 'profile',
474
                'id'     => 'sb-profile',
475
476
477
                'weight' => -20,
                'data'   => profile_sideblock()
            );
478
479
480
            if (get_config('showonlineuserssideblock')) {
                $SIDEBLOCKS[] = array(
                    'name'   => 'onlineusers',
481
                    'id'     => 'sb-onlineusers',
482
483
484
485
                    'weight' => -10,
                    'data'   => onlineusers_sideblock(),
                );
            }
486
        }
Martyn Smith's avatar
Martyn Smith committed
487

Richard Mansfield's avatar
Richard Mansfield committed
488
489
490
        if(defined('GROUP')) {
            $SIDEBLOCKS[] = array(
                'name'   => 'group',
491
                'id'     => 'sb-groupnav',
Richard Mansfield's avatar
Richard Mansfield committed
492
493
494
495
496
                'weight' => -10,
                'data'   => group_sideblock()
            );
        }

497
        if (!$USER->is_logged_in() && !(get_config('siteclosed') && get_config('disablelogin'))) {
498
            $SIDEBLOCKS[] = array(
499
500
                'name'   => 'login',
                'weight' => -10,
501
                'id'     => 'sb-loginbox',
502
503
504
                'data'   => array(
                    'loginform' => auth_generate_login_form(),
                ),
505
506
            );
        }
507

508
509
510
511
512
513
514
515
516
        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
517
518
        }

519
520
521
522
523
        if (isset($extraconfig['sideblocks']) && is_array($extraconfig['sideblocks'])) {
            foreach ($extraconfig['sideblocks'] as $sideblock) {
                $SIDEBLOCKS[] = $sideblock;
            }
        }
Martyn Smith's avatar
Martyn Smith committed
524

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

527
528
529
        // 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.
530
        $sidebars = $sidebars && !empty($SIDEBLOCKS);
531
        $SIDEBLOCKS = array('left' => array(), 'right' => $SIDEBLOCKS);
532

533
        $smarty->assign('userauthinstance', $SESSION->get('authinstance'));
534
        $smarty->assign('MNETUSER', $SESSION->get('mnetuser'));
535
        $smarty->assign('SIDEBLOCKS', $SIDEBLOCKS);
536
        $smarty->assign('SIDEBARS', $sidebars);
537

538
539
540
541
    }

    if ($USER->get('parentuser')) {
        $smarty->assign('USERMASQUERADING', true);
542
        $smarty->assign('masqueradedetails', get_string('youaremasqueradingas', 'mahara', display_name($USER)));
543
544
        $smarty->assign('becomeyouagain',
            ' <a href="' . hsc($wwwroot) . 'admin/users/changeuser.php?restore=1">'
545
            . get_string('becomeadminagain', 'admin', hsc($USER->get('parentuser')->name))
546
            . '</a>');
547
    }
Martyn Smith's avatar
Martyn Smith committed
548

549
550
551
    return $smarty;
}

552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575

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

576
577
578
579
580
581
582
583
584
585
    /**
     * Directories where to look for templates by default
     */
    public $templatedirs = array();

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

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
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
    /**
     * 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
648
649
650
            if (!$themename = get_config('theme')) {
                $themename = 'raw';
            }
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
        }
        $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)) {
680
            $this->displayname = $this->basename;
681
682
683
684
685
        }
        if (!isset($theme->parent) || !$theme->parent) {
            $theme->parent = 'raw';
        }

686
687
688
        $this->templatedirs[] = get_config('docroot') . 'theme/' . $this->basename . '/templates/';
        $this->inheritance[]  = $this->basename;

689
690
691
692
693
694
695
696
        // 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) {
697
                if (!isset($this->$key) || !$this->$key) {
698
699
700
                    $this->$key = $value;
                }
            }
701
702
            $this->templatedirs[] = get_config('docroot') . 'theme/' . $currenttheme . '/templates/';
            $this->inheritance[]  = $currenttheme;
703
704
705
        }
    }

706
707
708
    /**
     * stuff
     */
709
710
    public function get_url($filename, $all=false, $plugindirectory='') {
        return $this->_get_path($filename, $all, $plugindirectory, get_config('wwwroot'));
711
712
    }

713
714
    public function get_path($filename, $all=false, $plugindirectory='') {
        return $this->_get_path($filename, $all, $plugindirectory, get_config('docroot'));
715
716
    }

717
    private function _get_path($filename, $all, $plugindirectory, $returnprefix) {
718
        $list = array();
719
        $plugindirectory = ($plugindirectory && substr($plugindirectory, -1) != DIRECTORY_SEPARATOR) ? $plugindirectory . DIRECTORY_SEPARATOR : $plugindirectory;
720
721

        foreach ($this->inheritance as $themedir) {
722
            if (is_readable(get_config('docroot') . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename)) {
723
                if ($all) {
724
                    $list[$themedir] = $returnprefix . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename;
725
726
                }
                else {
727
                    return $returnprefix . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename;
728
729
730
731
732
733
734
735
                }
            }
        }
        if ($all) {
            return $list;
        }

        $extra = '';
736
737
        if ($plugindirectory) {
            $extra = ", plugindir $plugindirectory";
738
739
        }
        log_debug("Missing file in theme {$this->basename}{$extra}: $filename");
740
        return $returnprefix . $plugindirectory . 'theme/' . $themedir . '/static/' . $filename;
741
742
    }

743
744
745
}


746
747
/** 
 * Returns the lists of strings used in the .js files
748
 * @return array                   
749
750
 */

751
function jsstrings() {
Martyn Smith's avatar
Martyn Smith committed
752
    return array(
753
754
755
       'mahara' => array(                        // js file
            'mahara' => array(                   // section
                'namedfieldempty',               // string name
756
                'processing',
757
758
759
                'requiredfieldempty',
                'unknownerror',
                'loading',
Martyn Smith's avatar
Martyn Smith committed
760
                'showtags',
761
762
                'unreadmessages',
                'unreadmessage',
763
764
                'pendingfriend',
                'pendingfriends',
765
                'couldnotgethelp',
766
767
768
769
770
771
                'password',
                'username',
                'login',
                'sessiontimedout',
                'loginfailed',
                'home',
772
                'youhavenottaggedanythingyet',
773
            ),
774
775
        ),
        'tablerenderer' => array(
776
777
778
779
780
781
            'mahara' => array(
                'firstpage',
                'nextpage',
                'prevpage',
                'lastpage',
            )
782
        ),
783
        'friends' => array(
784
            'group' => array(
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
                'confirmremovefriend',
                'seeallviews',
                'noviewstosee',
                'sendmessage',
                'whymakemeyourfriend',
                'approverequest',
                'denyrequest',
                'pending',
                'removefromfriendslist',
                'views',
                'trysearchingforfriends',
                'nobodyawaitsfriendapproval',
                'sendfriendrequest',
                'addtomyfriends',
                'friendshiprequested',
                'userdoesntwantfriends',
                'existingfriend',
                'nosearchresultsfound',
803
                'reason',
804
                'requestfriendship',
805
                'cancel',
806
807
            ),
        ),
808
809
810
        'views' => array(
            'view' => array(
                'confirmdeleteblockinstance',
811
                'blocksinstructionajax',
812
                'Configure',
813
814
            ),
        ),
815
816
817
818
819
820
821
822
823
        'adminusersearch' => array(
            'admin' => array(
                'suspenduser',
                'suspensionreason',
            ),
            'mahara' => array(
                'cancel',
            ),
        ),
Martyn Smith's avatar
Martyn Smith committed
824
825
826
    );
}

Martyn Smith's avatar
Martyn Smith committed
827
function themepaths() {
828
829
830
831
832
833

    static $paths;
    if (empty($paths)) {
        $paths = array(
            'mahara' => array(
                'images/icon_close.gif',
834
                'images/edit.gif',
835
836
837
                'images/failure.gif',
                'images/loading.gif',
                'images/success.gif',
838
                'images/icon_problem.gif',
839
                'images/icon_help.gif',
840
                'style/js.css',
841
842
843
844
            ),
        );
    }
    return $paths;
Martyn Smith's avatar
Martyn Smith committed
845
846
}

847
848
849
850
851
852
/** 
 * Takes an array of string identifiers and returns an array of the
 * corresponding strings, quoted for use in inline javascript here
 * docs.
 */

853
854
855
856
function quotestrings($strings) {
    $qstrings = array();
    foreach ($strings as $section => $tags) {
        foreach ($tags as $tag) {
857
            $qstrings[$tag] = json_encode(get_string($tag, $section));
858
        }
859
    }
860
    return $qstrings;
861
862
}

863
864
865
866
867
868
869
/** 
 * 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() {
870
871
872
    global $THEME;
    log_warn("theme_setup() is deprecated - please use the global \$THEME object instead");
    return $THEME;
873
874
875
876
877
878
879
880
}

/** 
 * 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
 */
881
function theme_get_url($location, $pluginlocation='', $all = false) {
882
883
884
885
886
887
    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);
888
    }
889
    return $THEME->get_url($location, $all, $plugintype, $pluginname);
890
891
}

892
893
894
895
896
897
/** 
 * 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
 */
898
function theme_get_path($location, $pluginlocation='', $all=false) {
899
900
901
902
903
904
    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);
905
    }
906
    return $THEME->get_path($location, $all, $plugintype, $pluginname);
907
908
}

909
910
911
912
913
/**
 * This function sends headers suitable for all JSON returning scripts.
 *
 */
function json_headers() {
914
    // @todo Catalyst IT Ltd
915
916
    // header('Content-type: text/x-json');
    header('Content-type: text/plain');
Martyn Smith's avatar
Martyn Smith committed
917
    header('Pragma: no-cache');
918
919
}

Richard Mansfield's avatar
Richard Mansfield committed
920
/**
921
 * This function sends a JSON message, and ends the script.
Richard Mansfield's avatar
Richard Mansfield committed
922
 *
923
924
925
926
927
928
929
930
 * 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
931
 */
932
function json_reply($error, $message, $returncode=0) {
Richard Mansfield's avatar
Richard Mansfield committed
933
    json_headers();
934
    echo json_encode(array('error' => $error, 'message' => $message, 'returnCode' => $returncode));
935
    perf_to_log();
Richard Mansfield's avatar
Richard Mansfield committed
936
937
938
    exit;
}

939
function _param_retrieve($name) {
940
941
942
943
944
945
946
    // prefer post
    if (isset($_POST[$name])) {
        $value = $_POST[$name];
    } 
    else if (isset($_GET[$name])) {
        $value = $_GET[$name];
    }
947
    else if (func_num_args() == 2) {
948
949
950
        $php_work_around = func_get_arg(1);
        return array($php_work_around, true);
    }
951
    else {
952
953
954
        throw new ParameterException("Missing parameter '$name' and no default supplied");
    }

955
    return array($value, false);
956
957
}

Martyn Smith's avatar
Martyn Smith committed
958
959
960
961
962
963
964
965
966
967
968
/**
 * 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
969
function param_variable($name) {
970
971
972
    $args = func_get_args();
    list ($value) = call_user_func_array('_param_retrieve', $args);
    return $value;
Martyn Smith's avatar
Martyn Smith committed
973
974
975
976
977
978
979
980
981
982
983
}

/**
 * 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
984
 * @return int The value of the parameter
Martyn Smith's avatar
Martyn Smith committed
985
986
 *
 */
Martyn Smith's avatar
Martyn Smith committed
987
function param_integer($name) {
988
989
990
991
992
993
    $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
994
995
996
997
998
    }

    if (preg_match('/^\d+$/',$value)) {
        return (int)$value;
    }
999
1000
1001
    else if ($value == '' && isset($args[1])) {
        return $args[1];
    }
Martyn Smith's avatar
Martyn Smith committed
1002

1003
    throw new ParameterException("The '$name' parameter is not an integer");
Martyn Smith's avatar
Martyn Smith committed
1004
1005
}

1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
/**
 * 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
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
/**
 * 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) {