Dotclear

source: inc/admin/lib.dc.page.php @ 3919:f0be8a8c7715

Revision 3919:f0be8a8c7715, 45.2 KB checked in by franck <carnet.franck.paul@…>, 7 years ago (diff)

Switching from inline JS variables to JSON script. Includes a copy of getData() function in common.js

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

Sites map