Dotclear

source: inc/admin/lib.dc.page.php @ 3946:61ad7b88857b

Revision 3946:61ad7b88857b, 40.1 KB checked in by franck <carnet.franck.paul@…>, 7 years ago (diff)

Try to keep same context when switching blog

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

Sites map