Dotclear

Changeset 3709:c88e69474c34 for inc


Ignore:
Timestamp:
02/18/18 18:16:29 (7 years ago)
Author:
franck <carnet.franck.paul@…>
Branch:
default
Message:

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • inc/admin/lib.dc.page.php

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

Sites map