Dotclear

source: inc/admin/lib.dc.page.php @ 3709:c88e69474c34

Revision 3709:c88e69474c34, 44.8 KB checked in by franck <carnet.franck.paul@…>, 7 years ago (diff)

use strict and no more linter warnings/errors as far as possible, switch from inline js to separate loaded file

Line 
1<?php
2# -- BEGIN LICENSE BLOCK ---------------------------------------
3#
4# This file is part of Dotclear 2.
5#
6# Copyright (c) 2003-2013 Olivier Meunier & Association Dotclear
7# Licensed under the GPL version 2.0 license.
8# See LICENSE file or
9# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10#
11# -- END LICENSE BLOCK -----------------------------------------
12if (!defined('DC_RC_PATH')) {return;}
13
14define('DC_AUTH_PAGE', 'auth.php');
15
16class dcPage
17{
18    private static $loaded_js     = array();
19    private static $loaded_css    = array();
20    private static $xframe_loaded = false;
21    private static $N_TYPES       = array(
22        "success" => "success",
23        "warning" => "warning-msg",
24        "error"   => "error",
25        "message" => "message",
26        "static"  => "static-msg");
27
28    # Auth check
29    public static function check($permissions)
30    {
31        global $core;
32
33        if ($core->blog && $core->auth->check($permissions, $core->blog->id)) {
34            return;
35        }
36
37        if (session_id()) {
38            $core->session->destroy();
39        }
40        http::redirect(DC_AUTH_PAGE);
41    }
42
43    # Check super admin
44    public static function checkSuper()
45    {
46        global $core;
47
48        if (!$core->auth->isSuperAdmin()) {
49            if (session_id()) {
50                $core->session->destroy();
51            }
52            http::redirect(DC_AUTH_PAGE);
53        }
54    }
55
56    # Top of admin page
57    public static function open($title = '', $head = '', $breadcrumb = '', $options = array())
58    {
59        global $core;
60
61        # List of user's blogs
62        if ($core->auth->getBlogCount() == 1 || $core->auth->getBlogCount() > 20) {
63            $blog_box =
64            '<p>' . __('Blog:') . ' <strong title="' . html::escapeHTML($core->blog->url) . '">' .
65            html::escapeHTML($core->blog->name) . '</strong>';
66
67            if ($core->auth->getBlogCount() > 20) {
68                $blog_box .= ' - <a href="' . $core->adminurl->get("admin.blogs") . '">' . __('Change blog') . '</a>';
69            }
70            $blog_box .= '</p>';
71        } else {
72            $rs_blogs = $core->getBlogs(array('order' => 'LOWER(blog_name)', 'limit' => 20));
73            $blogs    = array();
74            while ($rs_blogs->fetch()) {
75                $blogs[html::escapeHTML($rs_blogs->blog_name . ' - ' . $rs_blogs->blog_url)] = $rs_blogs->blog_id;
76            }
77            $blog_box =
78            '<p><label for="switchblog" class="classic">' .
79            __('Blogs:') . '</label> ' .
80            $core->formNonce() .
81            form::combo('switchblog', $blogs, $core->blog->id) .
82            '<input type="submit" value="' . __('ok') . '" class="hidden-if-js" /></p>';
83        }
84
85        $safe_mode = isset($_SESSION['sess_safe_mode']) && $_SESSION['sess_safe_mode'];
86
87        # Display
88        $headers = new ArrayObject(array());
89
90        # Content-Type
91        $headers['content-type'] = 'Content-Type: text/html; charset=UTF-8';
92
93        # Referrer Policy for admin pages
94        $headers['referrer'] = 'Referrer-Policy: strict-origin';
95
96        # Prevents Clickjacking as far as possible
97        if (isset($options['x-frame-allow'])) {
98            self::setXFrameOptions($headers, $options['x-frame-allow']);
99        } else {
100            self::setXFrameOptions($headers);
101        }
102
103        # Content-Security-Policy (only if safe mode if not active, it may help)
104        if (!$safe_mode && $core->blog->settings->system->csp_admin_on) {
105            // Get directives from settings if exist, else set defaults
106            $csp = new ArrayObject(array());
107
108                                                                                // SQlite Clearbricks driver does not allow using single quote at beginning or end of a field value
109                                                                                // so we have to use neutral values (localhost and 127.0.0.1) for some CSP directives
110            $csp_prefix = $core->con->driver() == 'sqlite' ? 'localhost ' : ''; // Hack for SQlite Clearbricks driver
111            $csp_suffix = $core->con->driver() == 'sqlite' ? ' 127.0.0.1' : ''; // Hack for SQlite Clearbricks driver
112
113            $csp['default-src'] = $core->blog->settings->system->csp_admin_default ?:
114            $csp_prefix . "'self'" . $csp_suffix;
115            $csp['script-src'] = $core->blog->settings->system->csp_admin_script ?:
116            $csp_prefix . "'self' 'unsafe-inline' 'unsafe-eval'" . $csp_suffix;
117            $csp['style-src'] = $core->blog->settings->system->csp_admin_style ?:
118            $csp_prefix . "'self' 'unsafe-inline'" . $csp_suffix;
119            $csp['img-src'] = $core->blog->settings->system->csp_admin_img ?:
120            $csp_prefix . "'self' data: http://media.dotaddict.org blob:";
121
122            # Cope with blog post preview (via public URL in iframe)
123            if (!is_null($core->blog->host)) {
124                $csp['default-src'] .= ' ' . parse_url($core->blog->host, PHP_URL_HOST);
125                $csp['script-src'] .= ' ' . parse_url($core->blog->host, PHP_URL_HOST);
126                $csp['style-src'] .= ' ' . parse_url($core->blog->host, PHP_URL_HOST);
127            }
128            # Cope with media display in media manager (via public URL)
129            if (!is_null($core->media)) {
130                $csp['img-src'] .= ' ' . parse_url($core->media->root_url, PHP_URL_HOST);
131            }
132            # Allow everything in iframe (used by editors to preview public content)
133            $csp['child-src'] = "*";
134
135            # --BEHAVIOR-- adminPageHTTPHeaderCSP
136            $core->callBehavior('adminPageHTTPHeaderCSP', $csp);
137
138            // Construct CSP header
139            $directives = array();
140            foreach ($csp as $key => $value) {
141                if ($value) {
142                    $directives[] = $key . ' ' . $value;
143                }
144            }
145            if (count($directives)) {
146                if (version_compare(phpversion(), '5.4', '>=')) {
147                    // csp_report.php needs PHP ≥ 5.4
148                    $directives[] = "report-uri " . DC_ADMIN_URL . "csp_report.php";
149                }
150                $report_only    = ($core->blog->settings->system->csp_admin_report_only) ? '-Report-Only' : '';
151                $headers['csp'] = "Content-Security-Policy" . $report_only . ": " . implode(" ; ", $directives);
152            }
153        }
154
155        # --BEHAVIOR-- adminPageHTTPHeaders
156        $core->callBehavior('adminPageHTTPHeaders', $headers);
157        foreach ($headers as $key => $value) {
158            header($value);
159        }
160
161        echo
162        '<!DOCTYPE html>' .
163        '<html lang="' . $core->auth->getInfo('user_lang') . '">' . "\n" .
164        "<head>\n" .
165        '  <meta charset="UTF-8" />' . "\n" .
166        '  <meta name="ROBOTS" content="NOARCHIVE,NOINDEX,NOFOLLOW" />' . "\n" .
167        '  <meta name="GOOGLEBOT" content="NOSNIPPET" />' . "\n" .
168        '  <meta name="viewport" content="width=device-width, initial-scale=1.0" />' . "\n" .
169        '  <title>' . $title . ' - ' . html::escapeHTML($core->blog->name) . ' - ' . html::escapeHTML(DC_VENDOR_NAME) . ' - ' . DC_VERSION . '</title>' . "\n" .
170
171        self::cssLoad('style/default.css');
172        if (l10n::getTextDirection($GLOBALS['_lang']) == 'rtl') {
173            echo self::cssLoad('style/default-rtl.css');
174        }
175
176        $core->auth->user_prefs->addWorkspace('interface');
177        if (!$core->auth->user_prefs->interface->hide_std_favicon) {
178            echo
179                '<link rel="icon" type="image/png" href="images/favicon96-login.png" />' . "\n" .
180                '<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" />' . "\n";
181        }
182
183        if ($core->auth->user_prefs->interface->htmlfontsize) {
184            echo
185            '<script type="text/javascript">' . "\n" .
186            self::jsVar('dotclear_htmlFontSize', $core->auth->user_prefs->interface->htmlfontsize) . "\n" .
187                "</script>\n";
188        }
189
190        echo
191        self::jsCommon() .
192        self::jsToggles() .
193            $head;
194
195        if ($core->auth->user_prefs->interface->hidemoreinfo) {
196            echo
197                '<script type="text/javascript">' . "\n" .
198                'dotclear.hideMoreInfo = true;' . "\n" .
199                "</script>\n";
200        }
201        if ($core->auth->user_prefs->interface->showajaxloader) {
202            echo
203                '<script type="text/javascript">' . "\n" .
204                'dotclear.showAjaxLoader = true;' . "\n" .
205                "</script>\n";
206        }
207
208        # --BEHAVIOR-- adminPageHTMLHead
209        $core->callBehavior('adminPageHTMLHead');
210
211        echo
212        "</head>\n" .
213        '<body id="dotclear-admin' .
214        ($safe_mode ? ' safe-mode' : '') . '" class="no-js' .
215        ($core->auth->user_prefs->interface->dynfontsize ? ' responsive-font' : '') . '">' . "\n" .
216
217        '<ul id="prelude">' .
218        '<li><a href="#content">' . __('Go to the content') . '</a></li>' .
219        '<li><a href="#main-menu">' . __('Go to the menu') . '</a></li>' .
220        '<li><a href="#qx">' . __('Go to search') . '</a></li>' .
221        '<li><a href="#help">' . __('Go to help') . '</a></li>' .
222        '</ul>' . "\n" .
223        '<div id="header" role="banner">' .
224        '<h1><a href="' . $core->adminurl->get("admin.home") . '"><span class="hidden">' . DC_VENDOR_NAME . '</span></a></h1>' . "\n";
225
226        echo
227        '<form action="' . $core->adminurl->get("admin.home") . '" method="post" id="top-info-blog">' .
228        $blog_box .
229        '<p><a href="' . $core->blog->url . '" class="outgoing" title="' . __('Go to site') .
230        '">' . __('Go to site') . '<img src="images/outgoing.png" alt="" /></a>' .
231        '</p></form>' .
232        '<ul id="top-info-user">' .
233        '<li><a class="' . (preg_match('/' . preg_quote($core->adminurl->get('admin.home')) . '$/', $_SERVER['REQUEST_URI']) ? ' active' : '') . '" href="' . $core->adminurl->get("admin.home") . '">' . __('My dashboard') . '</a></li>' .
234        '<li><a class="smallscreen' . (preg_match('/' . preg_quote($core->adminurl->get('admin.user.preferences')) . '(\?.*)?$/', $_SERVER['REQUEST_URI']) ? ' active' : '') .
235        '" href="' . $core->adminurl->get("admin.user.preferences") . '">' . __('My preferences') . '</a></li>' .
236        '<li><a href="' . $core->adminurl->get("admin.home", array('logout' => 1)) . '" class="logout"><span class="nomobile">' . sprintf(__('Logout %s'), $core->auth->userID()) .
237            '</span><img src="images/logout.png" alt="" /></a></li>' .
238            '</ul>' .
239            '</div>'; // end header
240
241        echo
242        '<div id="wrapper" class="clearfix">' . "\n" .
243        '<div class="hidden-if-no-js collapser-box"><button type="button" id="collapser" class="void-btn">' .
244        '<img class="collapse-mm visually-hidden" src="images/collapser-hide.png" alt="' . __('Hide main menu') . '" />' .
245        '<img class="expand-mm visually-hidden" src="images/collapser-show.png" alt="' . __('Show main menu') . '" />' .
246            '</button></div>' .
247            '<div id="main" role="main">' . "\n" .
248            '<div id="content" class="clearfix">' . "\n";
249
250        # Safe mode
251        if ($safe_mode) {
252            echo
253            '<div class="warning" role="alert"><h3>' . __('Safe mode') . '</h3>' .
254            '<p>' . __('You are in safe mode. All plugins have been temporarily disabled. Remind to log out then log in again normally to get back all functionalities') . '</p>' .
255                '</div>';
256        }
257
258        // Display breadcrumb (if given) before any error messages
259        echo $breadcrumb;
260
261        // Display notices and errors
262        echo self::notices();
263    }
264
265    public static function notices()
266    {
267        global $core;
268        static $error_displayed = false;
269
270        $res = '';
271
272        // return error messages if any
273        if ($core->error->flag() && !$error_displayed) {
274
275            # --BEHAVIOR-- adminPageNotificationError
276            $notice_error = $core->callBehavior('adminPageNotificationError', $core, $core->error);
277
278            if (isset($notice_error) && !empty($notice_error)) {
279                $res .= $notice_error;
280            } else {
281                $res .= '<div class="error"><p>' .
282                '<strong>' . (count($core->error->getErrors()) > 1 ? __('Errors:') : __('Error:')) . '</strong>' .
283                '</p>' . $core->error->toHTML() . '</div>';
284            }
285            $error_displayed = true;
286        }
287
288        // return notices if any
289        if (isset($_SESSION['notifications'])) {
290            foreach ($_SESSION['notifications'] as $notification) {
291
292                # --BEHAVIOR-- adminPageNotification
293                $notice = $core->callBehavior('adminPageNotification', $core, $notification);
294
295                $res .= (isset($notice) && !empty($notice) ? $notice : self::getNotification($notification));
296            }
297            unset($_SESSION['notifications']);
298        }
299        return $res;
300    }
301
302    public static function addNotice($type, $message, $options = array())
303    {
304        if (isset(self::$N_TYPES[$type])) {
305            $class = self::$N_TYPES[$type];
306        } else {
307            $class = $type;
308        }
309        if (isset($_SESSION['notifications']) && is_array($_SESSION['notifications'])) {
310            $notifications = $_SESSION['notifications'];
311        } else {
312            $notifications = array();
313        }
314
315        $n = array_merge($options, array('class' => $class, 'ts' => time(), 'text' => $message));
316        if ($type != "static") {
317            $notifications[] = $n;
318        } else {
319            array_unshift($notifications, $n);
320        }
321        $_SESSION['notifications'] = $notifications;
322    }
323
324    public static function addSuccessNotice($message, $options = array())
325    {
326        self::addNotice("success", $message, $options);
327    }
328
329    public static function addWarningNotice($message, $options = array())
330    {
331        self::addNotice("warning", $message, $options);
332    }
333
334    public static function addErrorNotice($message, $options = array())
335    {
336        self::addNotice("error", $message, $options);
337    }
338
339    protected static function getNotification($n)
340    {
341        global $core;
342        $tag = (isset($n['divtag']) && $n['divtag']) ? 'div' : 'p';
343        $ts  = '';
344        if (!isset($n['with_ts']) || ($n['with_ts'] == true)) {
345            $ts = dt::str(__('[%H:%M:%S]'), $n['ts'], $core->auth->getInfo('user_tz')) . ' ';
346        }
347        $res = '<' . $tag . ' class="' . $n['class'] . '" role="alert">' . $ts . $n['text'] . '</' . $tag . '>';
348        return $res;
349    }
350
351    public static function close()
352    {
353        global $core;
354
355        if (!$GLOBALS['__resources']['ctxhelp']) {
356            if (!$core->auth->user_prefs->interface->hidehelpbutton) {
357                echo
358                '<p id="help-button"><a href="' . $core->adminurl->get("admin.help") . '" class="outgoing" title="' .
359                __('Global help') . '">' . __('Global help') . '</a></p>';
360            }
361        }
362
363        $menu = &$GLOBALS['_menu'];
364
365        echo
366        "</div>\n" . // End of #content
367        "</div>\n" . // End of #main
368
369        '<div id="main-menu" role="navigation">' . "\n" .
370
371        '<form id="search-menu" action="' . $core->adminurl->get("admin.search") . '" method="get" role="search">' .
372        '<p><label for="qx" class="hidden">' . __('Search:') . ' </label>' . form::field('qx', 30, 255, '') .
373        '<input type="submit" value="' . __('OK') . '" /></p>' .
374            '</form>';
375
376        foreach ($menu as $k => $v) {
377            echo $menu[$k]->draw();
378        }
379
380        $text = sprintf(__('Thank you for using %s.'), 'Dotclear ' . DC_VERSION);
381
382        # --BEHAVIOR-- adminPageFooter
383        $textAlt = $core->callBehavior('adminPageFooter', $core, $text);
384        if ($textAlt != '') {
385            $text = $textAlt;
386        }
387        $text = html::escapeHTML($text);
388
389        echo
390        '</div>' . "\n" . // End of #main-menu
391        "</div>\n";       // End of #wrapper
392
393        echo '<p id="gototop"><a href="#wrapper">' . __('Page top') . '</a></p>' . "\n";
394
395        $figure = "
396   /\_/\
397  (='.'=)
398  (\")-(\")";
399
400        echo
401            '<div id="footer" role="contentinfo">' .
402            '<a href="http://dotclear.org/" title="' . $text . '">' .
403            '<img src="style/dc_logos/w-dotclear90.png" alt="' . $text . '" /></a></div>' . "\n" .
404            "<!-- " . "\n" .
405            $figure .
406            " -->" . "\n";
407
408        if (defined('DC_DEV') && DC_DEV === true) {
409            echo self::debugInfo();
410        }
411
412        echo
413            '</body></html>';
414    }
415
416    public static function openPopup($title = '', $head = '', $breadcrumb = '')
417    {
418        global $core;
419
420        # Display
421        header('Content-Type: text/html; charset=UTF-8');
422
423        # Referrer Policy for admin pages
424        header('Referrer-Policy: strict-origin');
425
426        # Prevents Clickjacking as far as possible
427        header('X-Frame-Options: SAMEORIGIN'); // FF 3.6.9+ Chrome 4.1+ IE 8+ Safari 4+ Opera 10.5+
428
429        echo
430        '<!DOCTYPE html>' .
431        '<html lang="' . $core->auth->getInfo('user_lang') . '">' . "\n" .
432        "<head>\n" .
433        '  <meta charset="UTF-8" />' . "\n" .
434        '  <meta name="viewport" content="width=device-width, initial-scale=1.0" />' . "\n" .
435        '  <title>' . $title . ' - ' . html::escapeHTML($core->blog->name) . ' - ' . html::escapeHTML(DC_VENDOR_NAME) . ' - ' . DC_VERSION . '</title>' . "\n" .
436
437        '  <meta name="ROBOTS" content="NOARCHIVE,NOINDEX,NOFOLLOW" />' . "\n" .
438        '  <meta name="GOOGLEBOT" content="NOSNIPPET" />' . "\n" .
439
440        self::cssLoad('style/default.css');
441        if (l10n::getTextDirection($GLOBALS['_lang']) == 'rtl') {
442            echo self::cssLoad('style/default-rtl.css');
443        }
444
445        $core->auth->user_prefs->addWorkspace('interface');
446        if ($core->auth->user_prefs->interface->htmlfontsize) {
447            echo
448            '<script type="text/javascript">' . "\n" .
449            self::jsVar('dotclear_htmlFontSize', $core->auth->user_prefs->interface->htmlfontsize) . "\n" .
450                "</script>\n";
451        }
452
453        echo
454        self::jsCommon() .
455        self::jsToggles() .
456            $head;
457
458        if ($core->auth->user_prefs->interface->hidemoreinfo) {
459            echo
460                '<script type="text/javascript">' . "\n" .
461                'dotclear.hideMoreInfo = true;' . "\n" .
462                "</script>\n";
463        }
464        if ($core->auth->user_prefs->interface->showajaxloader) {
465            echo
466                '<script type="text/javascript">' . "\n" .
467                'dotclear.showAjaxLoader = true;' . "\n" .
468                "</script>\n";
469        }
470
471        # --BEHAVIOR-- adminPageHTMLHead
472        $core->callBehavior('adminPageHTMLHead');
473
474        echo
475            "</head>\n" .
476            '<body id="dotclear-admin" class="popup' .
477            ($core->auth->user_prefs->interface->dynfontsize ? ' responsive-font' : '') . '">' . "\n" .
478
479            '<h1>' . DC_VENDOR_NAME . '</h1>' . "\n";
480
481        echo
482            '<div id="wrapper">' . "\n" .
483            '<div id="main" role="main">' . "\n" .
484            '<div id="content">' . "\n";
485
486        // display breadcrumb if given
487        echo $breadcrumb;
488
489        // Display notices and errors
490        echo self::notices();
491    }
492
493    public static function closePopup()
494    {
495        echo
496        "</div>\n" . // End of #content
497        "</div>\n" . // End of #main
498        "</div>\n" . // End of #wrapper
499
500        '<p id="gototop"><a href="#wrapper">' . __('Page top') . '</a></p>' . "\n" .
501
502            '<div id="footer" role="contentinfo"><p>&nbsp;</p></div>' . "\n" .
503            '</body></html>';
504    }
505
506    public static function breadcrumb($elements = null, $options = array())
507    {
508        global $core;
509        $with_home_link = isset($options['home_link']) ? $options['home_link'] : true;
510        $hl             = isset($options['hl']) ? $options['hl'] : true;
511        $hl_pos         = isset($options['hl_pos']) ? $options['hl_pos'] : -1;
512        // First item of array elements should be blog's name, System or Plugins
513        $res = '<h2>' . ($with_home_link ?
514            '<a class="go_home" href="' . $core->adminurl->get("admin.home") . '"><img src="style/dashboard.png" alt="' . __('Go to dashboard') . '" /></a>' :
515            '<img src="style/dashboard-alt.png" alt="" />');
516        $index = 0;
517        if ($hl_pos < 0) {
518            $hl_pos = count($elements) + $hl_pos;
519        }
520        foreach ($elements as $element => $url) {
521            if ($hl && $index == $hl_pos) {
522                $element = sprintf('<span class="page-title">%s</span>', $element);
523            }
524            $res .= ($with_home_link ? ($index == 1 ? ' : ' : ' &rsaquo; ') : ($index == 0 ? ' ' : ' &rsaquo; ')) .
525                ($url ? '<a href="' . $url . '">' : '') . $element . ($url ? '</a>' : '');
526            $index++;
527        }
528        $res .= '</h2>';
529        return $res;
530    }
531
532    public static function message($msg, $timestamp = true, $div = false, $echo = true, $class = 'message')
533    {
534        global $core;
535
536        $res = '';
537        if ($msg != '') {
538            $res = ($div ? '<div class="' . $class . '">' : '') . '<p' . ($div ? '' : ' class="' . $class . '"') . '>' .
539                ($timestamp ? dt::str(__('[%H:%M:%S]'), null, $core->auth->getInfo('user_tz')) . ' ' : '') . $msg .
540                '</p>' . ($div ? '</div>' : '');
541            if ($echo) {
542                echo $res;
543            }
544        }
545        return $res;
546    }
547
548    public static function success($msg, $timestamp = true, $div = false, $echo = true)
549    {
550        return self::message($msg, $timestamp, $div, $echo, "success");
551    }
552
553    public static function warning($msg, $timestamp = true, $div = false, $echo = true)
554    {
555        return self::message($msg, $timestamp, $div, $echo, "warning-msg");
556    }
557
558    private static function debugInfo()
559    {
560        $global_vars = implode(', ', array_keys($GLOBALS));
561
562        $res =
563        '<div id="debug"><div>' .
564        '<p>memory usage: ' . memory_get_usage() . ' (' . files::size(memory_get_usage()) . ')</p>';
565
566        if (function_exists('xdebug_get_profiler_filename')) {
567            $res .= '<p>Elapsed time: ' . xdebug_time_index() . ' seconds</p>';
568
569            $prof_file = xdebug_get_profiler_filename();
570            if ($prof_file) {
571                $res .= '<p>Profiler file : ' . xdebug_get_profiler_filename() . '</p>';
572            } else {
573                $prof_url = http::getSelfURI();
574                $prof_url .= (strpos($prof_url, '?') === false) ? '?' : '&';
575                $prof_url .= 'XDEBUG_PROFILE';
576                $res .= '<p><a href="' . html::escapeURL($prof_url) . '">Trigger profiler</a></p>';
577            }
578
579            /* xdebug configuration:
580        zend_extension = /.../xdebug.so
581        xdebug.auto_trace = On
582        xdebug.trace_format = 0
583        xdebug.trace_options = 1
584        xdebug.show_mem_delta = On
585        xdebug.profiler_enable = 0
586        xdebug.profiler_enable_trigger = 1
587        xdebug.profiler_output_dir = /tmp
588        xdebug.profiler_append = 0
589        xdebug.profiler_output_name = timestamp
590         */
591        }
592
593        $res .=
594            '<p>Global vars: ' . $global_vars . '</p>' .
595            '</div></div>';
596
597        return $res;
598    }
599
600    public static function help($page, $index = '')
601    {
602        # Deprecated but we keep this for plugins.
603    }
604
605    public static function helpBlock()
606    {
607        global $core;
608
609        if ($core->auth->user_prefs->interface->hidehelpbutton) {
610            return;
611        }
612
613        $args = func_get_args();
614        $args = new ArrayObject($args);
615
616        # --BEHAVIOR-- adminPageHelpBlock
617        $GLOBALS['core']->callBehavior('adminPageHelpBlock', $args);
618
619        if (empty($args)) {
620            return;
621        };
622
623        global $__resources;
624        if (empty($__resources['help'])) {
625            return;
626        }
627
628        $content = '';
629        foreach ($args as $v) {
630            if (is_object($v) && isset($v->content)) {
631                $content .= $v->content;
632                continue;
633            }
634
635            if (!isset($__resources['help'][$v])) {
636                continue;
637            }
638            $f = $__resources['help'][$v];
639            if (!file_exists($f) || !is_readable($f)) {
640                continue;
641            }
642
643            $fc = file_get_contents($f);
644            if (preg_match('|<body[^>]*?>(.*?)</body>|ms', $fc, $matches)) {
645                $content .= $matches[1];
646            } else {
647                $content .= $fc;
648            }
649        }
650
651        if (trim($content) == '') {
652            return;
653        }
654
655        // Set contextual help global flag
656        $GLOBALS['__resources']['ctxhelp'] = true;
657
658        echo
659        '<div id="help"><hr /><div class="help-content clear"><h3>' . __('Help about this page') . '</h3>' .
660        $content .
661        '</div>' .
662        '<div id="helplink"><hr />' .
663        '<p>' .
664        sprintf(__('See also %s'), sprintf('<a href="' . $core->adminurl->get("admin.help") . '">%s</a>', __('the global help'))) .
665            '.</p>' .
666            '</div></div>';
667    }
668
669    public static function cssLoad($src, $media = 'screen', $v = '')
670    {
671        $escaped_src = html::escapeHTML($src);
672        if (!isset(self::$loaded_css[$escaped_src])) {
673            self::$loaded_css[$escaped_src] = true;
674            $escaped_src                    = self::appendVersion($escaped_src, $v);
675
676            return '<link rel="stylesheet" href="' . $escaped_src . '" type="text/css" media="' . $media . '" />' . "\n";
677        }
678    }
679
680    public static function jsLoad($src, $v = '')
681    {
682        $escaped_src = html::escapeHTML($src);
683        if (!isset(self::$loaded_js[$escaped_src])) {
684            self::$loaded_js[$escaped_src] = true;
685            $escaped_src                   = self::appendVersion($escaped_src, $v);
686            return '<script type="text/javascript" src="' . $escaped_src . '"></script>' . "\n";
687        }
688    }
689
690    private static function appendVersion($src, $v = '')
691    {
692        $src .= (strpos($src, '?') === false ? '?' : '&amp;') . 'v=';
693        if (defined('DC_DEV') && DC_DEV === true) {
694            $src .= md5(uniqid());
695        } else {
696            $src .= ($v === '' ? DC_VERSION : $v);
697        }
698        return $src;
699    }
700
701    public static function jsVar($n, $v)
702    {
703        return $n . " = '" . html::escapeJS($v) . "';\n";
704    }
705
706    public static function jsVars($vars)
707    {
708        $ret = '<script type="text/javascript">' . "\n";
709        foreach ($vars as $var => $value) {
710            $ret .= $var . ' = ' . (is_string($value) ? "'" . html::escapeJS($value) . "'" : $value) . ';' . "\n";
711        }
712        $ret .= "</script>\n";
713
714        return $ret;
715    }
716
717    public static function jsToggles()
718    {
719        if ($GLOBALS['core']->auth->user_prefs->toggles) {
720            $unfolded_sections = explode(',', $GLOBALS['core']->auth->user_prefs->toggles->unfolded_sections);
721            foreach ($unfolded_sections as $k => &$v) {
722                if ($v == '') {
723                    unset($unfolded_sections[$k]);
724                } else {
725                    $v = "'" . html::escapeJS($v) . "':true";
726                }
727            }
728        } else {
729            $unfolded_sections = array();
730        }
731        return '<script type="text/javascript">' . "\n" .
732        'dotclear.unfolded_sections = {' . join(",", $unfolded_sections) . "};\n" .
733            "</script>\n";
734    }
735
736    public static function jsCommon()
737    {
738        $mute_or_no = '';
739        if (empty($GLOBALS['core']->blog) || $GLOBALS['core']->blog->settings->system->jquery_migrate_mute) {
740            $mute_or_no .=
741                '<script type="text/javascript">' . "\n" .
742                'jQuery.migrateMute = true;' . "\n" .
743                "</script>\n";
744        }
745
746        return
747        self::jsLoad('js/jquery/jquery.js') .
748        $mute_or_no .
749        self::jsLoad('js/jquery/jquery-migrate.js') .
750        self::jsLoad('js/jquery/jquery.biscuit.js') .
751        self::jsLoad('js/common.js') .
752        self::jsLoad('js/prelude.js') .
753
754        '<script type="text/javascript">' . "\n" .
755        'jsToolBar = {}, jsToolBar.prototype = { elements : {} };' . "\n" .
756
757        self::jsVar('dotclear.nonce', $GLOBALS['core']->getNonce()) .
758
759        self::jsVar('dotclear.img_plus_src', 'images/expand.png') .
760        self::jsVar('dotclear.img_plus_alt', __('uncover')) .
761        self::jsVar('dotclear.img_minus_src', 'images/hide.png') .
762        self::jsVar('dotclear.img_minus_alt', __('hide')) .
763        self::jsVar('dotclear.img_menu_on', 'images/menu_on.png') .
764        self::jsVar('dotclear.img_menu_off', 'images/menu_off.png') .
765
766        self::jsVar('dotclear.img_plus_theme_src', 'images/plus-theme.png') .
767        self::jsVar('dotclear.img_plus_theme_alt', __('uncover')) .
768        self::jsVar('dotclear.img_minus_theme_src', 'images/minus-theme.png') .
769        self::jsVar('dotclear.img_minus_theme_alt', __('hide')) .
770
771        self::jsVar('dotclear.msg.help',
772            __('Need help?')) .
773        self::jsVar('dotclear.msg.new_window',
774            __('new window')) .
775        self::jsVar('dotclear.msg.help_hide',
776            __('Hide')) .
777        self::jsVar('dotclear.msg.to_select',
778            __('Select:')) .
779        self::jsVar('dotclear.msg.no_selection',
780            __('no selection')) .
781        self::jsVar('dotclear.msg.select_all',
782            __('select all')) .
783        self::jsVar('dotclear.msg.invert_sel',
784            __('Invert selection')) .
785        self::jsVar('dotclear.msg.website',
786            __('Web site:')) .
787        self::jsVar('dotclear.msg.email',
788            __('Email:')) .
789        self::jsVar('dotclear.msg.ip_address',
790            __('IP address:')) .
791        self::jsVar('dotclear.msg.error',
792            __('Error:')) .
793        self::jsVar('dotclear.msg.entry_created',
794            __('Entry has been successfully created.')) .
795        self::jsVar('dotclear.msg.edit_entry',
796            __('Edit entry')) .
797        self::jsVar('dotclear.msg.view_entry',
798            __('view entry')) .
799        self::jsVar('dotclear.msg.confirm_delete_posts',
800            __("Are you sure you want to delete selected entries (%s)?")) .
801        self::jsVar('dotclear.msg.confirm_delete_medias',
802            __("Are you sure you want to delete selected medias (%d)?")) .
803        self::jsVar('dotclear.msg.confirm_delete_categories',
804            __("Are you sure you want to delete selected categories (%s)?")) .
805        self::jsVar('dotclear.msg.confirm_delete_post',
806            __("Are you sure you want to delete this entry?")) .
807        self::jsVar('dotclear.msg.click_to_unlock',
808            __("Click here to unlock the field")) .
809        self::jsVar('dotclear.msg.confirm_spam_delete',
810            __('Are you sure you want to delete all spams?')) .
811        self::jsVar('dotclear.msg.confirm_delete_comments',
812            __('Are you sure you want to delete selected comments (%s)?')) .
813        self::jsVar('dotclear.msg.confirm_delete_comment',
814            __('Are you sure you want to delete this comment?')) .
815        self::jsVar('dotclear.msg.cannot_delete_users',
816            __('Users with posts cannot be deleted.')) .
817        self::jsVar('dotclear.msg.confirm_delete_user',
818            __('Are you sure you want to delete selected users (%s)?')) .
819        self::jsVar('dotclear.msg.confirm_delete_blog',
820            __('Are you sure you want to delete selected blogs (%s)?')) .
821        self::jsVar('dotclear.msg.confirm_delete_category',
822            __('Are you sure you want to delete category "%s"?')) .
823        self::jsVar('dotclear.msg.confirm_reorder_categories',
824            __('Are you sure you want to reorder all categories?')) .
825        self::jsVar('dotclear.msg.confirm_delete_media',
826            __('Are you sure you want to remove media "%s"?')) .
827        self::jsVar('dotclear.msg.confirm_delete_directory',
828            __('Are you sure you want to remove directory "%s"?')) .
829        self::jsVar('dotclear.msg.confirm_extract_current',
830            __('Are you sure you want to extract archive in current directory?')) .
831        self::jsVar('dotclear.msg.confirm_remove_attachment',
832            __('Are you sure you want to remove attachment "%s"?')) .
833        self::jsVar('dotclear.msg.confirm_delete_lang',
834            __('Are you sure you want to delete "%s" language?')) .
835        self::jsVar('dotclear.msg.confirm_delete_plugin',
836            __('Are you sure you want to delete "%s" plugin?')) .
837        self::jsVar('dotclear.msg.confirm_delete_plugins',
838            __('Are you sure you want to delete selected plugins?')) .
839        self::jsVar('dotclear.msg.use_this_theme',
840            __('Use this theme')) .
841        self::jsVar('dotclear.msg.remove_this_theme',
842            __('Remove this theme')) .
843        self::jsVar('dotclear.msg.confirm_delete_theme',
844            __('Are you sure you want to delete "%s" theme?')) .
845        self::jsVar('dotclear.msg.confirm_delete_themes',
846            __('Are you sure you want to delete selected themes?')) .
847        self::jsVar('dotclear.msg.confirm_delete_backup',
848            __('Are you sure you want to delete this backup?')) .
849        self::jsVar('dotclear.msg.confirm_revert_backup',
850            __('Are you sure you want to revert to this backup?')) .
851        self::jsVar('dotclear.msg.zip_file_content',
852            __('Zip file content')) .
853        self::jsVar('dotclear.msg.xhtml_validator',
854            __('XHTML markup validator')) .
855        self::jsVar('dotclear.msg.xhtml_valid',
856            __('XHTML content is valid.')) .
857        self::jsVar('dotclear.msg.xhtml_not_valid',
858            __('There are XHTML markup errors.')) .
859        self::jsVar('dotclear.msg.warning_validate_no_save_content',
860            __('Attention: an audit of a content not yet registered.')) .
861        self::jsVar('dotclear.msg.confirm_change_post_format',
862            __('You have unsaved changes. Switch post format will loose these changes. Proceed anyway?')) .
863        self::jsVar('dotclear.msg.confirm_change_post_format_noconvert',
864            __("Warning: post format change will not convert existing content. You will need to apply new format by yourself. Proceed anyway?")) .
865        self::jsVar('dotclear.msg.load_enhanced_uploader',
866            __('Loading enhanced uploader, please wait.')) .
867
868        self::jsVar('dotclear.msg.module_author',
869            __('Author:')) .
870        self::jsVar('dotclear.msg.module_details',
871            __('Details')) .
872        self::jsVar('dotclear.msg.module_support',
873            __('Support')) .
874        self::jsVar('dotclear.msg.module_help',
875            __('Help:')) .
876        self::jsVar('dotclear.msg.module_section',
877            __('Section:')) .
878        self::jsVar('dotclear.msg.module_tags',
879            __('Tags:')) .
880
881        self::jsVar('dotclear.msg.close_notice',
882            __('Hide this notice')) .
883
884            "\n" .
885            "</script>\n";
886    }
887
888    /**
889    @deprecated since version 2.11
890     */
891    public static function jsLoadIE7()
892    {
893        return '';
894    }
895
896    public static function jsConfirmClose()
897    {
898        $args = func_get_args();
899        if (count($args) > 0) {
900            foreach ($args as $k => $v) {
901                $args[$k] = "'" . html::escapeJS($v) . "'";
902            }
903            $args = implode(',', $args);
904        } else {
905            $args = '';
906        }
907
908        return
909        self::jsLoad('js/confirm-close.js') .
910        '<script type="text/javascript">' . "\n" .
911        "confirmClosePage = new confirmClose(" . $args . "); " .
912        "confirmClose.prototype.prompt = '" . html::escapeJS(__('You have unsaved changes.')) . "'; " .
913            "</script>\n";
914    }
915
916    public static function jsPageTabs($default = null)
917    {
918        if ($default) {
919            $default = "'" . html::escapeJS($default) . "'";
920        }
921
922        return
923        self::jsLoad('js/jquery/jquery.pageTabs.js') .
924            '<script type="text/javascript">' . "\n" .
925            '$(function() {' . "\n" .
926            '   $.pageTabs(' . $default . ');' . "\n" .
927            '});' .
928            "</script>\n";
929    }
930
931    public static function jsModal()
932    {
933        return
934        self::jsLoad('js/jquery/jquery.magnific-popup.js');
935    }
936
937    public static function jsColorPicker()
938    {
939        return
940        self::cssLoad('style/farbtastic/farbtastic.css') .
941        self::jsLoad('js/jquery/jquery.farbtastic.js') .
942        self::jsLoad('js/color-picker.js');
943    }
944
945    public static function jsDatePicker()
946    {
947        return
948        self::cssLoad('style/date-picker.css') .
949        self::jsLoad('js/date-picker.js') .
950        '<script type="text/javascript">' . "\n" .
951
952        "datePicker.prototype.months[0] = '" . html::escapeJS(__('January')) . "'; " .
953        "datePicker.prototype.months[1] = '" . html::escapeJS(__('February')) . "'; " .
954        "datePicker.prototype.months[2] = '" . html::escapeJS(__('March')) . "'; " .
955        "datePicker.prototype.months[3] = '" . html::escapeJS(__('April')) . "'; " .
956        "datePicker.prototype.months[4] = '" . html::escapeJS(__('May')) . "'; " .
957        "datePicker.prototype.months[5] = '" . html::escapeJS(__('June')) . "'; " .
958        "datePicker.prototype.months[6] = '" . html::escapeJS(__('July')) . "'; " .
959        "datePicker.prototype.months[7] = '" . html::escapeJS(__('August')) . "'; " .
960        "datePicker.prototype.months[8] = '" . html::escapeJS(__('September')) . "'; " .
961        "datePicker.prototype.months[9] = '" . html::escapeJS(__('October')) . "'; " .
962        "datePicker.prototype.months[10] = '" . html::escapeJS(__('November')) . "'; " .
963        "datePicker.prototype.months[11] = '" . html::escapeJS(__('December')) . "'; " .
964
965        "datePicker.prototype.days[0] = '" . html::escapeJS(__('Monday')) . "'; " .
966        "datePicker.prototype.days[1] = '" . html::escapeJS(__('Tuesday')) . "'; " .
967        "datePicker.prototype.days[2] = '" . html::escapeJS(__('Wednesday')) . "'; " .
968        "datePicker.prototype.days[3] = '" . html::escapeJS(__('Thursday')) . "'; " .
969        "datePicker.prototype.days[4] = '" . html::escapeJS(__('Friday')) . "'; " .
970        "datePicker.prototype.days[5] = '" . html::escapeJS(__('Saturday')) . "'; " .
971        "datePicker.prototype.days[6] = '" . html::escapeJS(__('Sunday')) . "'; " .
972
973        "datePicker.prototype.img_src = 'images/date-picker.png'; " .
974        "datePicker.prototype.img_alt = '" . html::escapeJS(__('Choose date')) . "'; " .
975
976        "datePicker.prototype.close_msg = '" . html::escapeJS(__('close')) . "'; " .
977        "datePicker.prototype.now_msg = '" . html::escapeJS(__('now')) . "'; " .
978
979            "</script>\n";
980    }
981
982    public static function jsToolBar()
983    {
984        # Deprecated but we keep this for plugins.
985    }
986
987    public static function jsUpload($params = array(), $base_url = null)
988    {
989        if (!$base_url) {
990            $base_url = path::clean(dirname(preg_replace('/(\?.*$)?/', '', $_SERVER['REQUEST_URI']))) . '/';
991        }
992
993        $params = array_merge($params, array(
994            'sess_id=' . session_id(),
995            'sess_uid=' . $_SESSION['sess_browser_uid'],
996            'xd_check=' . $GLOBALS['core']->getNonce()
997        ));
998
999        return
1000        '<script type="text/javascript">' . "\n" .
1001        "dotclear.jsUpload = {};\n" .
1002        "dotclear.jsUpload.msg = {};\n" .
1003        self::jsVar('dotclear.msg.enhanced_uploader_activate', __('Temporarily activate enhanced uploader')) .
1004        self::jsVar('dotclear.msg.enhanced_uploader_disable', __('Temporarily disable enhanced uploader')) .
1005        self::jsVar('dotclear.jsUpload.msg.limit_exceeded', __('Limit exceeded.')) .
1006        self::jsVar('dotclear.jsUpload.msg.size_limit_exceeded', __('File size exceeds allowed limit.')) .
1007        self::jsVar('dotclear.jsUpload.msg.canceled', __('Canceled.')) .
1008        self::jsVar('dotclear.jsUpload.msg.http_error', __('HTTP Error:')) .
1009        self::jsVar('dotclear.jsUpload.msg.error', __('Error:')) .
1010        self::jsVar('dotclear.jsUpload.msg.choose_file', __('Choose file')) .
1011        self::jsVar('dotclear.jsUpload.msg.choose_files', __('Choose files')) .
1012        self::jsVar('dotclear.jsUpload.msg.cancel', __('Cancel')) .
1013        self::jsVar('dotclear.jsUpload.msg.clean', __('Clean')) .
1014        self::jsVar('dotclear.jsUpload.msg.upload', __('Upload')) .
1015        self::jsVar('dotclear.jsUpload.msg.send', __('Send')) .
1016        self::jsVar('dotclear.jsUpload.msg.file_successfully_uploaded', __('File successfully uploaded.')) .
1017        self::jsVar('dotclear.jsUpload.msg.no_file_in_queue', __('No file in queue.')) .
1018        self::jsVar('dotclear.jsUpload.msg.file_in_queue', __('1 file in queue.')) .
1019        self::jsVar('dotclear.jsUpload.msg.files_in_queue', __('%d files in queue.')) .
1020        self::jsVar('dotclear.jsUpload.msg.queue_error', __('Queue error:')) .
1021        self::jsVar('dotclear.jsUpload.base_url', $base_url) .
1022        "</script>\n" .
1023
1024        self::jsLoad('js/jquery/jquery-ui.custom.js') .
1025        self::jsLoad('js/jsUpload/tmpl.js') .
1026        self::jsLoad('js/jsUpload/template-upload.js') .
1027        self::jsLoad('js/jsUpload/template-download.js') .
1028        self::jsLoad('js/jsUpload/load-image.js') .
1029        self::jsLoad('js/jsUpload/jquery.iframe-transport.js') .
1030        self::jsLoad('js/jsUpload/jquery.fileupload.js') .
1031        self::jsLoad('js/jsUpload/jquery.fileupload-process.js') .
1032        self::jsLoad('js/jsUpload/jquery.fileupload-resize.js') .
1033        self::jsLoad('js/jsUpload/jquery.fileupload-ui.js');
1034    }
1035
1036    public static function jsMetaEditor()
1037    {
1038        return self::jsLoad('js/meta-editor.js');
1039    }
1040
1041    public static function jsFilterControl($show = true)
1042    {
1043        return
1044        self::jsLoad('js/filter-controls.js') .
1045        '<script type="text/javascript">' . "\n" .
1046        self::jsVar('dotclear.msg.show_filters', $show ? 'true' : 'false') . "\n" .
1047        self::jsVar('dotclear.msg.filter_posts_list', __('Show filters and display options')) . "\n" .
1048        self::jsVar('dotclear.msg.cancel_the_filter', __('Cancel filters and display options')) . "\n" .
1049            "</script>";
1050    }
1051
1052    public static function jsLoadCodeMirror($theme = '', $multi = true, $modes = array('css', 'htmlmixed', 'javascript', 'php', 'xml'))
1053    {
1054        $ret =
1055        self::cssLoad('js/codemirror/lib/codemirror.css') .
1056        self::jsLoad('js/codemirror/lib/codemirror.js');
1057        if ($multi) {
1058            $ret .= self::jsLoad('js/codemirror/addon/mode/multiplex.js');
1059        }
1060        foreach ($modes as $mode) {
1061            $ret .= self::jsLoad('js/codemirror/mode/' . $mode . '/' . $mode . '.js');
1062        }
1063        $ret .=
1064        self::jsLoad('js/codemirror/addon/edit/closebrackets.js') .
1065        self::jsLoad('js/codemirror/addon/edit/matchbrackets.js') .
1066        self::cssLoad('js/codemirror/addon/display/fullscreen.css') .
1067        self::jsLoad('js/codemirror/addon/display/fullscreen.js');
1068        if ($theme != '') {
1069            $ret .= self::cssLoad('js/codemirror/theme/' . $theme . '.css');
1070        }
1071        return $ret;
1072    }
1073
1074    public static function jsRunCodeMirror($name, $id, $mode, $theme = '')
1075    {
1076        $ret =
1077            '<script type="text/javascript">' . "\n" .
1078            'var ' . $name . ' = CodeMirror.fromTextArea(' . $id . ',{' . "\n" .
1079            '   mode: "' . $mode . '",' . "\n" .
1080            '   tabMode: "indent",' . "\n" .
1081            '   lineWrapping: 1,' . "\n" .
1082            '   lineNumbers: 1,' . "\n" .
1083            '   matchBrackets: 1,' . "\n" .
1084            '   autoCloseBrackets: 1,' . "\n" .
1085            '   extraKeys: {"F11": function(cm) {cm.setOption("fullScreen",!cm.getOption("fullScreen"));}}';
1086        if ($theme) {
1087            $ret .=
1088                ',' . "\n" .
1089                '   theme: "' . $theme . '"';
1090        }
1091        $ret .= "\n" .
1092            '});' . "\n" .
1093            '</script>';
1094        return $ret;
1095    }
1096
1097    public static function getCodeMirrorThemes()
1098    {
1099        $themes      = array();
1100        $themes_root = dirname(__FILE__) . '/../../admin' . '/js/codemirror/theme/';
1101        if (is_dir($themes_root) && is_readable($themes_root)) {
1102            if (($d = @dir($themes_root)) !== false) {
1103                while (($entry = $d->read()) !== false) {
1104                    if ($entry != '.' && $entry != '..' && substr($entry, 0, 1) != '.' && is_readable($themes_root . '/' . $entry)) {
1105                        $themes[] = substr($entry, 0, -4); // remove .css extension
1106                    }
1107                }
1108                sort($themes);
1109            }
1110        }
1111        return $themes;
1112    }
1113
1114    public static function getPF($file)
1115    {
1116        return $GLOBALS['core']->adminurl->get('load.plugin.file', array('pf' => $file));
1117    }
1118
1119    public static function getVF($file)
1120    {
1121        return $GLOBALS['core']->adminurl->get('load.var.file', array('vf' => $file));
1122    }
1123
1124    public static function setXFrameOptions($headers, $origin = null)
1125    {
1126        if (self::$xframe_loaded) {
1127            return;
1128        }
1129
1130        if ($origin !== null) {
1131            $url                        = parse_url($origin);
1132            $headers['x-frame-options'] = sprintf('X-Frame-Options: %s', is_array($url) && isset($url['host']) ?
1133                ("ALLOW-FROM " . (isset($url['scheme']) ? $url['scheme'] . ':' : '') . '//' . $url['host']) :
1134                'SAMEORIGIN');
1135        } else {
1136            $headers['x-frame-options'] = 'X-Frame-Options: SAMEORIGIN'; // FF 3.6.9+ Chrome 4.1+ IE 8+ Safari 4+ Opera 10.5+
1137        }
1138        self::$xframe_loaded = true;
1139    }
1140}
Note: See TracBrowser for help on using the repository browser.

Sites map