Dotclear

source: inc/admin/lib.dc.page.php @ 4006:4de7ea99ccac

Revision 4006:4de7ea99ccac, 41.2 KB checked in by franck <carnet.franck.paul@…>, 6 years ago (diff)

If context cannot be conserved when switching blog, test dashboard access permission before killing the session

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

Sites map