Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like AdministrationPage often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use AdministrationPage, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 15 | class AdministrationPage extends HTMLPage  | 
            ||
| 16 | { | 
            ||
| 17 | /**  | 
            ||
| 18 | * An array of `Alert` objects used to display page level  | 
            ||
| 19 | * messages to Symphony backend users one by one. Prior to Symphony 2.3  | 
            ||
| 20 | * this variable only held a single `Alert` object.  | 
            ||
| 21 | * @var array  | 
            ||
| 22 | */  | 
            ||
| 23 | public $Alert = array();  | 
            ||
| 24 | |||
| 25 | /**  | 
            ||
| 26 | * As the name suggests, a `<div>` that holds the following `$Header`,  | 
            ||
| 27 | * `$Contents` and `$Footer`.  | 
            ||
| 28 | * @var XMLElement  | 
            ||
| 29 | */  | 
            ||
| 30 | public $Wrapper = null;  | 
            ||
| 31 | |||
| 32 | /**  | 
            ||
| 33 | * A `<div>` that contains the header of a Symphony backend page, which  | 
            ||
| 34 | * typically contains the Site title and the navigation.  | 
            ||
| 35 | * @var XMLElement  | 
            ||
| 36 | */  | 
            ||
| 37 | public $Header = null;  | 
            ||
| 38 | |||
| 39 | /**  | 
            ||
| 40 | * A `<div>` that contains the breadcrumbs, the page title and some contextual  | 
            ||
| 41 | * actions (e.g. "Create new").  | 
            ||
| 42 | * @since Symphony 2.3  | 
            ||
| 43 | * @var XMLElement  | 
            ||
| 44 | */  | 
            ||
| 45 | public $Context = null;  | 
            ||
| 46 | |||
| 47 | /**  | 
            ||
| 48 | * An object that stores the markup for the breadcrumbs and is only used  | 
            ||
| 49 | * internally.  | 
            ||
| 50 | * @since Symphony 2.3  | 
            ||
| 51 | * @var XMLElement  | 
            ||
| 52 | */  | 
            ||
| 53 | public $Breadcrumbs = null;  | 
            ||
| 54 | |||
| 55 | /**  | 
            ||
| 56 | * An array of Drawer widgets for the current page  | 
            ||
| 57 | * @since Symphony 2.3  | 
            ||
| 58 | * @var array  | 
            ||
| 59 | */  | 
            ||
| 60 | public $Drawer = array();  | 
            ||
| 61 | |||
| 62 | /**  | 
            ||
| 63 | * A `<div>` that contains the content of a Symphony backend page.  | 
            ||
| 64 | * @var XMLElement  | 
            ||
| 65 | */  | 
            ||
| 66 | public $Contents = null;  | 
            ||
| 67 | |||
| 68 | /**  | 
            ||
| 69 | * An associative array of the navigation where the key is the group  | 
            ||
| 70 | * index, and the value is an associative array of 'name', 'index' and  | 
            ||
| 71 | * 'children'. Name is the name of the this group, index is the same as  | 
            ||
| 72 | * the key and children is an associative array of navigation items containing  | 
            ||
| 73 | * the keys 'link', 'name' and 'visible'. In Symphony, all navigation items  | 
            ||
| 74 | * are contained within a group, and the group has no 'default' link, therefore  | 
            ||
| 75 | * it is up to the children to provide the link to pages. This link should be  | 
            ||
| 76 | * relative to the Symphony path, although it is possible to provide an  | 
            ||
| 77 | * absolute link by providing a key, 'relative' with the value false.  | 
            ||
| 78 | * @var array  | 
            ||
| 79 | */  | 
            ||
| 80 | public $_navigation = array();  | 
            ||
| 81 | |||
| 82 | /**  | 
            ||
| 83 | * An associative array describing this pages context. This  | 
            ||
| 84 | * can include the section handle, the current entry_id, the page  | 
            ||
| 85 | * name and any flags such as 'saved' or 'created'. This variable  | 
            ||
| 86 | * often provided in delegates so extensions can manipulate based  | 
            ||
| 87 | * off the current context or add new keys.  | 
            ||
| 88 | * @var array  | 
            ||
| 89 | */  | 
            ||
| 90 | public $_context = null;  | 
            ||
| 91 | |||
| 92 | /**  | 
            ||
| 93 | * The class attribute of the `<body>` element for this page. Defaults  | 
            ||
| 94 | * to an empty string  | 
            ||
| 95 | * @var string  | 
            ||
| 96 | */  | 
            ||
| 97 | private $_body_class = '';  | 
            ||
| 98 | |||
| 99 | /**  | 
            ||
| 100 | * Constructor calls the parent constructor to set up  | 
            ||
| 101 | * the basic HTML, Head and Body `XMLElement`'s. This function  | 
            ||
| 102 | * also sets the `XMLElement` element style to be HTML, instead of XML  | 
            ||
| 103 | */  | 
            ||
| 104 | public function __construct()  | 
            ||
| 105 |     { | 
            ||
| 106 | parent::__construct();  | 
            ||
| 107 | |||
| 108 |         $this->Html->setElementStyle('html'); | 
            ||
| 109 | }  | 
            ||
| 110 | |||
| 111 | /**  | 
            ||
| 112 | * Specifies the type of page that being created. This is used to  | 
            ||
| 113 | * trigger various styling hooks. If your page is mainly a form,  | 
            ||
| 114 | * pass 'form' as the parameter, if it's displaying a single entry,  | 
            ||
| 115 | * pass 'single'. If any other parameter is passed, the 'index'  | 
            ||
| 116 | * styling will be applied.  | 
            ||
| 117 | *  | 
            ||
| 118 | * @param string $type  | 
            ||
| 119 | * Accepts 'form' or 'single', any other `$type` will trigger 'index'  | 
            ||
| 120 | * styling.  | 
            ||
| 121 | */  | 
            ||
| 122 | public function setPageType($type = 'form')  | 
            ||
| 123 |     { | 
            ||
| 124 | $this->setBodyClass($type == 'form' || $type == 'single' ? 'single' : 'index');  | 
            ||
| 125 | }  | 
            ||
| 126 | |||
| 127 | /**  | 
            ||
| 128 | * Setter function to set the class attribute on the `<body>` element.  | 
            ||
| 129 | * This function will respect any previous classes that have been added  | 
            ||
| 130 | * to this `<body>`  | 
            ||
| 131 | *  | 
            ||
| 132 | * @param string $class  | 
            ||
| 133 | * The string of the classname, multiple classes can be specified by  | 
            ||
| 134 | * uses a space separator  | 
            ||
| 135 | */  | 
            ||
| 136 | public function setBodyClass($class)  | 
            ||
| 137 |     { | 
            ||
| 138 | // Prevents duplicate "index" classes  | 
            ||
| 139 |         if (!isset($this->_context['page']) || $this->_context['page'] !== 'index' || $class !== 'index') { | 
            ||
| 140 | $this->_body_class .= $class;  | 
            ||
| 141 | }  | 
            ||
| 142 | }  | 
            ||
| 143 | |||
| 144 | /**  | 
            ||
| 145 | * Accessor for `$this->_context` which includes contextual information  | 
            ||
| 146 | * about the current page such as the class, file location or page root.  | 
            ||
| 147 | * This information varies depending on if the page is provided by an  | 
            ||
| 148 | * extension, is for the publish area, is the login page or any other page  | 
            ||
| 149 | *  | 
            ||
| 150 | * @since Symphony 2.3  | 
            ||
| 151 | * @return array  | 
            ||
| 152 | */  | 
            ||
| 153 | public function getContext()  | 
            ||
| 156 | }  | 
            ||
| 157 | |||
| 158 | /**  | 
            ||
| 159 | * Given a `$message` and an optional `$type`, this function will  | 
            ||
| 160 | * add an Alert instance into this page's `$this->Alert` property.  | 
            ||
| 161 | * Since Symphony 2.3, there may be more than one `Alert` per page.  | 
            ||
| 162 | * Unless the Alert is an Error, it is required the `$message` be  | 
            ||
| 163 | * passed to this function.  | 
            ||
| 164 | *  | 
            ||
| 165 | * @param string $message  | 
            ||
| 166 | * The message to display to users  | 
            ||
| 167 | * @param string $type  | 
            ||
| 168 | * An Alert constant, being `Alert::NOTICE`, `Alert::ERROR` or  | 
            ||
| 169 | * `Alert::SUCCESS`. The differing types will show the error  | 
            ||
| 170 | * in a different style in the backend. If omitted, this defaults  | 
            ||
| 171 | * to `Alert::NOTICE`.  | 
            ||
| 172 | * @throws Exception  | 
            ||
| 173 | */  | 
            ||
| 174 | public function pageAlert($message = null, $type = Alert::NOTICE)  | 
            ||
| 175 |     { | 
            ||
| 176 |         if (is_null($message) && $type == Alert::ERROR) { | 
            ||
| 177 |             $message = __('There was a problem rendering this page. Please check the activity log for more details.'); | 
            ||
| 178 |         } else { | 
            ||
| 179 | $message = __($message);  | 
            ||
| 180 | }  | 
            ||
| 181 | |||
| 182 |         if (strlen(trim($message)) == 0) { | 
            ||
| 183 |             throw new Exception(__('A message must be supplied unless the alert is of type Alert::ERROR')); | 
            ||
| 184 | }  | 
            ||
| 185 | |||
| 186 | $this->Alert[] = new Alert($message, $type);  | 
            ||
| 187 | }  | 
            ||
| 188 | |||
| 189 | /**  | 
            ||
| 190 | * Appends the heading of this Symphony page to the Context element.  | 
            ||
| 191 | * Action buttons can be provided (e.g. "Create new") as second parameter.  | 
            ||
| 192 | *  | 
            ||
| 193 | * @since Symphony 2.3  | 
            ||
| 194 | * @param string $value  | 
            ||
| 195 | * The heading text  | 
            ||
| 196 | * @param array|XMLElement|string $actions  | 
            ||
| 197 | * Some contextual actions to append to the heading, they can be provided as  | 
            ||
| 198 | * an array of XMLElements or strings. Traditionally Symphony uses this to append  | 
            ||
| 199 | * a "Create new" link to the Context div.  | 
            ||
| 200 | */  | 
            ||
| 201 | public function appendSubheading($value, $actions = null)  | 
            ||
| 202 |     { | 
            ||
| 203 |         if (!is_array($actions) && $actions) { // Backward compatibility | 
            ||
| 204 | $actions = array($actions);  | 
            ||
| 205 | }  | 
            ||
| 206 | |||
| 207 |         if (!empty($actions)) { | 
            ||
| 208 |             foreach ($actions as $a) { | 
            ||
| 209 | $this->insertAction($a);  | 
            ||
| 210 | }  | 
            ||
| 211 | }  | 
            ||
| 212 | |||
| 213 |         $this->Breadcrumbs->appendChild(new XMLElement('h2', $value, array('role' => 'heading', 'id' => 'symphony-subheading'))); | 
            ||
| 214 | }  | 
            ||
| 215 | |||
| 216 | /**  | 
            ||
| 217 | * This function allows a user to insert an Action button to the page.  | 
            ||
| 218 | * It accepts an `XMLElement` (which should be of the `Anchor` type),  | 
            ||
| 219 | * an optional parameter `$prepend`, which when `true` will add this  | 
            ||
| 220 | * action before any existing actions.  | 
            ||
| 221 | *  | 
            ||
| 222 | * @since Symphony 2.3  | 
            ||
| 223 | * @see core.Widget#Anchor  | 
            ||
| 224 | * @param XMLElement $action  | 
            ||
| 225 | * An Anchor element to add to the top of the page.  | 
            ||
| 226 | * @param boolean $append  | 
            ||
| 227 | * If true, this will add the `$action` after existing actions, otherwise  | 
            ||
| 228 | * it will be added before existing actions. By default this is `true`,  | 
            ||
| 229 | * which will add the `$action` after current actions.  | 
            ||
| 230 | */  | 
            ||
| 231 | public function insertAction(XMLElement $action, $append = true)  | 
            ||
| 232 |     { | 
            ||
| 233 |         $actions = $this->Context->getChildrenByName('ul'); | 
            ||
| 234 | |||
| 235 | // Actions haven't be added yet, create the element  | 
            ||
| 236 |         if (empty($actions)) { | 
            ||
| 237 |             $ul = new XMLElement('ul', null, array('class' => 'actions')); | 
            ||
| 238 | $this->Context->appendChild($ul);  | 
            ||
| 239 |         } else { | 
            ||
| 240 | $ul = current($actions);  | 
            ||
| 241 | $this->Context->replaceChildAt(1, $ul);  | 
            ||
| 242 | }  | 
            ||
| 243 | |||
| 244 |         $li = new XMLElement('li', $action); | 
            ||
| 245 | |||
| 246 |         if ($append) { | 
            ||
| 247 | $ul->prependChild($li);  | 
            ||
| 248 |         } else { | 
            ||
| 249 | $ul->appendChild($li);  | 
            ||
| 250 | }  | 
            ||
| 251 | }  | 
            ||
| 252 | |||
| 253 | /**  | 
            ||
| 254 | * Allows developers to specify a list of nav items that build the  | 
            ||
| 255 | * path to the current page or, in jargon, "breadcrumbs".  | 
            ||
| 256 | *  | 
            ||
| 257 | * @since Symphony 2.3  | 
            ||
| 258 | * @param array $values  | 
            ||
| 259 | * An array of `XMLElement`'s or strings that compose the path. If breadcrumbs  | 
            ||
| 260 | * already exist, any new item will be appended to the rightmost part of the  | 
            ||
| 261 | * path.  | 
            ||
| 262 | */  | 
            ||
| 263 | public function insertBreadcrumbs(array $values)  | 
            ||
| 285 | }  | 
            ||
| 286 | }  | 
            ||
| 287 | |||
| 288 | /**  | 
            ||
| 289 | * Allows a Drawer element to added to the backend page in one of three  | 
            ||
| 290 | * positions, `horizontal`, `vertical-left` or `vertical-right`. The button  | 
            ||
| 291 | * to trigger the visibility of the drawer will be added after existing  | 
            ||
| 292 | * actions by default.  | 
            ||
| 293 | *  | 
            ||
| 294 | * @since Symphony 2.3  | 
            ||
| 295 | * @see core.Widget#Drawer  | 
            ||
| 296 | * @param XMLElement $drawer  | 
            ||
| 297 | * An XMLElement representing the drawer, use `Widget::Drawer` to construct  | 
            ||
| 298 | * @param string $position  | 
            ||
| 299 | * Where `$position` can be `horizontal`, `vertical-left` or  | 
            ||
| 300 | * `vertical-right`. Defaults to `horizontal`.  | 
            ||
| 301 | * @param string $button  | 
            ||
| 302 | * If not passed, a button to open/close the drawer will not be added  | 
            ||
| 303 | * to the interface. Accepts 'prepend' or 'append' values, which will  | 
            ||
| 304 | * add the button before or after existing buttons. Defaults to `prepend`.  | 
            ||
| 305 | * If any other value is passed, no button will be added.  | 
            ||
| 306 | * @throws InvalidArgumentException  | 
            ||
| 307 | */  | 
            ||
| 308 | public function insertDrawer(XMLElement $drawer, $position = 'horizontal', $button = 'append')  | 
            ||
| 309 |     { | 
            ||
| 310 | $drawer->addClass($position);  | 
            ||
| 311 |         $drawer->setAttribute('data-position', $position); | 
            ||
| 312 |         $drawer->setAttribute('role', 'complementary'); | 
            ||
| 313 | $this->Drawer[$position][] = $drawer;  | 
            ||
| 314 | |||
| 315 |         if (in_array($button, array('prepend', 'append'))) { | 
            ||
| 316 | $this->insertAction(  | 
            ||
| 317 | Widget::Anchor(  | 
            ||
| 318 |                     $drawer->getAttribute('data-label'), | 
            ||
| 319 |                     '#' . $drawer->getAttribute('id'), | 
            ||
| 320 | null,  | 
            ||
| 321 | 'button drawer ' . $position  | 
            ||
| 322 | ),  | 
            ||
| 323 | ($button === 'append' ? true : false)  | 
            ||
| 324 | );  | 
            ||
| 325 | }  | 
            ||
| 326 | }  | 
            ||
| 327 | |||
| 328 | /**  | 
            ||
| 329 | * This function initialises a lot of the basic elements that make up a Symphony  | 
            ||
| 330 | * backend page such as the default stylesheets and scripts, the navigation and  | 
            ||
| 331 | * the footer. Any alerts are also appended by this function. `view()` is called to  | 
            ||
| 332 | * build the actual content of the page. The `InitialiseAdminPageHead` delegate  | 
            ||
| 333 | * allows extensions to add elements to the `<head>`. The `CanAccessPage` delegate  | 
            ||
| 334 | * allows extensions to restrict access to pages.  | 
            ||
| 335 | *  | 
            ||
| 336 | * @see view()  | 
            ||
| 337 | * @uses InitialiseAdminPageHead  | 
            ||
| 338 | * @uses CanAccessPage  | 
            ||
| 339 | * @param array $context  | 
            ||
| 340 | * An associative array describing this pages context. This  | 
            ||
| 341 | * can include the section handle, the current entry_id, the page  | 
            ||
| 342 | * name and any flags such as 'saved' or 'created'. This list is not exhaustive  | 
            ||
| 343 | * and extensions can add their own keys to the array.  | 
            ||
| 344 | * @throws InvalidArgumentException  | 
            ||
| 345 | * @throws SymphonyErrorPage  | 
            ||
| 346 | */  | 
            ||
| 347 | public function build(array $context = array())  | 
            ||
| 348 |     { | 
            ||
| 349 | $this->_context = $context;  | 
            ||
| 350 | |||
| 351 |         if (!$this->canAccessPage()) { | 
            ||
| 352 | Administration::instance()->throwCustomError(  | 
            ||
| 353 |                 __('You are not authorised to access this page.'), | 
            ||
| 354 |                 __('Access Denied'), | 
            ||
| 355 | Page::HTTP_STATUS_UNAUTHORIZED  | 
            ||
| 356 | );  | 
            ||
| 357 | }  | 
            ||
| 358 | |||
| 359 |         $this->Html->setDTD('<!DOCTYPE html>'); | 
            ||
| 
                                                                                                    
                        
                         | 
                |||
| 360 |         $this->Html->setAttribute('lang', Lang::get()); | 
            ||
| 361 |         $this->addElementToHead(new XMLElement('meta', null, array('charset' => 'UTF-8')), 0); | 
            ||
| 362 |         $this->addElementToHead(new XMLElement('meta', null, array('http-equiv' => 'X-UA-Compatible', 'content' => 'IE=edge,chrome=1')), 1); | 
            ||
| 363 |         $this->addElementToHead(new XMLElement('meta', null, array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1')), 2); | 
            ||
| 364 | |||
| 365 | // Add styles  | 
            ||
| 366 | $this->addStylesheetToHead(ASSETS_URL . '/css/symphony.min.css', 'screen', 2, false);  | 
            ||
| 367 | |||
| 368 | // Calculate timezone offset from UTC  | 
            ||
| 369 |         $timezone = new DateTimeZone(DateTimeObj::getSetting('timezone')); | 
            ||
| 370 |         $datetime = new DateTime('now', $timezone); | 
            ||
| 371 | $timezoneOffset = intval($timezone->getOffset($datetime)) / 60;  | 
            ||
| 372 | |||
| 373 | // Add scripts  | 
            ||
| 374 | $environment = array(  | 
            ||
| 375 | |||
| 376 | 'root' => URL,  | 
            ||
| 377 | 'symphony' => SYMPHONY_URL,  | 
            ||
| 378 |             'path'     => '/' . Symphony::Configuration()->get('admin-path', 'symphony'), | 
            ||
| 379 | 'route' => getCurrentPage(),  | 
            ||
| 380 |             'version'  => Symphony::Configuration()->get('version', 'symphony'), | 
            ||
| 381 | 'lang' => Lang::get(),  | 
            ||
| 382 | 'user' => array(  | 
            ||
| 383 | |||
| 384 | 'fullname' => Symphony::Author()->getFullName(),  | 
            ||
| 385 |                 'name'     => Symphony::Author()->get('first_name'), | 
            ||
| 386 |                 'type'     => Symphony::Author()->get('user_type'), | 
            ||
| 387 |                 'id'       => Symphony::Author()->get('id') | 
            ||
| 388 | ),  | 
            ||
| 389 | 'datetime' => array(  | 
            ||
| 390 | |||
| 391 | 'formats' => DateTimeObj::getDateFormatMappings(),  | 
            ||
| 392 | 'timezone-offset' => $timezoneOffset  | 
            ||
| 393 | ),  | 
            ||
| 394 | 'env' => array_merge(  | 
            ||
| 395 | |||
| 396 |                 array('page-namespace' => Symphony::getPageNamespace()), | 
            ||
| 397 | $this->_context  | 
            ||
| 398 | )  | 
            ||
| 399 | );  | 
            ||
| 400 | |||
| 401 | $this->addElementToHead(  | 
            ||
| 402 |             new XMLElement('script', json_encode($environment), array( | 
            ||
| 403 | 'type' => 'application/json',  | 
            ||
| 404 | 'id' => 'environment'  | 
            ||
| 405 | )),  | 
            ||
| 406 | 4  | 
            ||
| 407 | );  | 
            ||
| 408 | |||
| 409 | $this->addScriptToHead(ASSETS_URL . '/js/symphony.min.js', 6, false);  | 
            ||
| 410 | |||
| 411 | // Initialise page containers  | 
            ||
| 412 |         $this->Wrapper = new XMLElement('div', null, array('id' => 'wrapper')); | 
            ||
| 413 |         $this->Header = new XMLElement('header', null, array('id' => 'header')); | 
            ||
| 414 |         $this->Context = new XMLElement('div', null, array('id' => 'context')); | 
            ||
| 415 |         $this->Breadcrumbs = new XMLElement('div', null, array('id' => 'breadcrumbs')); | 
            ||
| 416 |         $this->Contents = new XMLElement('div', null, array('id' => 'contents', 'role' => 'main')); | 
            ||
| 417 |         $this->Form = Widget::Form(Administration::instance()->getCurrentPageURL(), 'post', null, null, array('role' => 'form')); | 
            ||
| 418 | |||
| 419 | /**  | 
            ||
| 420 | * Allows developers to insert items into the page HEAD. Use  | 
            ||
| 421 | * `Administration::instance()->Page` for access to the page object.  | 
            ||
| 422 | *  | 
            ||
| 423 | * @since In Symphony 2.3.2 this delegate was renamed from  | 
            ||
| 424 | * `InitaliseAdminPageHead` to the correct spelling of  | 
            ||
| 425 | * `InitialiseAdminPageHead`. The old delegate is supported  | 
            ||
| 426 | * until Symphony 3.0  | 
            ||
| 427 | *  | 
            ||
| 428 | * @delegate InitialiseAdminPageHead  | 
            ||
| 429 | * @param string $context  | 
            ||
| 430 | * '/backend/'  | 
            ||
| 431 | */  | 
            ||
| 432 |         Symphony::ExtensionManager()->notifyMembers('InitialiseAdminPageHead', '/backend/'); | 
            ||
| 433 |         Symphony::ExtensionManager()->notifyMembers('InitaliseAdminPageHead', '/backend/'); | 
            ||
| 434 | |||
| 435 |         $this->addHeaderToPage('Content-Type', 'text/html; charset=UTF-8'); | 
            ||
| 436 |         $this->addHeaderToPage('Cache-Control', 'no-cache, must-revalidate, max-age=0'); | 
            ||
| 437 |         $this->addHeaderToPage('Expires', 'Mon, 12 Dec 1982 06:14:00 GMT'); | 
            ||
| 438 |         $this->addHeaderToPage('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT'); | 
            ||
| 439 |         $this->addHeaderToPage('Pragma', 'no-cache'); | 
            ||
| 440 | |||
| 441 | // If not set by another extension, lock down the backend  | 
            ||
| 442 |         if (!array_key_exists('x-frame-options', $this->headers())) { | 
            ||
| 443 |             $this->addHeaderToPage('X-Frame-Options', 'SAMEORIGIN'); | 
            ||
| 444 | }  | 
            ||
| 445 | |||
| 446 |         if (!array_key_exists('x-content-type-options', $this->headers())) { | 
            ||
| 447 |             $this->addHeaderToPage('X-Content-Type-Options', 'nosniff'); | 
            ||
| 448 | }  | 
            ||
| 449 | |||
| 450 |         if (!array_key_exists('x-xss-protection', $this->headers())) { | 
            ||
| 451 |             $this->addHeaderToPage('X-XSS-Protection', '1; mode=block'); | 
            ||
| 452 | }  | 
            ||
| 453 | |||
| 454 |         if (!array_key_exists('referrer-policy', $this->headers())) { | 
            ||
| 455 |             $this->addHeaderToPage('Referrer-Policy', 'same-origin'); | 
            ||
| 456 | }  | 
            ||
| 457 | |||
| 458 |         if (isset($_REQUEST['action'])) { | 
            ||
| 459 | $this->action();  | 
            ||
| 460 |             Symphony::Profiler()->sample('Page action run', PROFILE_LAP); | 
            ||
| 461 | }  | 
            ||
| 462 | |||
| 463 |         $h1 = new XMLElement('h1'); | 
            ||
| 464 |         $h1->appendChild(Widget::Anchor(Symphony::Configuration()->get('sitename', 'general'), rtrim(URL, '/') . '/')); | 
            ||
| 465 | $this->Header->appendChild($h1);  | 
            ||
| 466 | |||
| 467 | $this->appendUserLinks();  | 
            ||
| 468 | $this->appendNavigation();  | 
            ||
| 469 | |||
| 470 | // Add Breadcrumbs  | 
            ||
| 471 | $this->Context->prependChild($this->Breadcrumbs);  | 
            ||
| 472 | $this->Contents->appendChild($this->Form);  | 
            ||
| 473 | |||
| 474 | // Validate date time config  | 
            ||
| 475 |         $dateFormat = defined('__SYM_DATE_FORMAT__') ? __SYM_DATE_FORMAT__ : null; | 
            ||
| 476 |         if (empty($dateFormat)) { | 
            ||
| 477 | $this->pageAlert(  | 
            ||
| 478 |                 __('Your <code>%s</code> file does not define a date format', array(basename(CONFIG))), | 
            ||
| 479 | Alert::NOTICE  | 
            ||
| 480 | );  | 
            ||
| 481 | }  | 
            ||
| 482 |         $timeFormat = defined('__SYM_TIME_FORMAT__') ? __SYM_TIME_FORMAT__ : null; | 
            ||
| 483 |         if (empty($timeFormat)) { | 
            ||
| 484 | $this->pageAlert(  | 
            ||
| 485 |                 __('Your <code>%s</code> file does not define a time format.', array(basename(CONFIG))), | 
            ||
| 486 | Alert::NOTICE  | 
            ||
| 487 | );  | 
            ||
| 488 | }  | 
            ||
| 489 | |||
| 490 | $this->view();  | 
            ||
| 491 | |||
| 492 | $this->appendAlert();  | 
            ||
| 493 | |||
| 494 |         Symphony::Profiler()->sample('Page content created', PROFILE_LAP); | 
            ||
| 495 | }  | 
            ||
| 496 | |||
| 497 | /**  | 
            ||
| 498 | * Checks the current Symphony Author can access the current page.  | 
            ||
| 499 | * This check uses the `ASSETS . /xml/navigation.xml` file to determine  | 
            ||
| 500 | * if the current page (or the current page namespace) can be viewed  | 
            ||
| 501 | * by the currently logged in Author.  | 
            ||
| 502 | *  | 
            ||
| 503 | * @since Symphony 2.7.0  | 
            ||
| 504 | * It fires a delegate, CanAccessPage, to allow extensions to restrict access  | 
            ||
| 505 | * to the current page  | 
            ||
| 506 | *  | 
            ||
| 507 | * @uses CanAccessPage  | 
            ||
| 508 | *  | 
            ||
| 509 | * @link http://github.com/symphonycms/symphony-2/blob/master/symphony/assets/xml/navigation.xml  | 
            ||
| 510 | * @return boolean  | 
            ||
| 511 | * true if the Author can access the current page, false otherwise  | 
            ||
| 512 | */  | 
            ||
| 513 | public function canAccessPage()  | 
            ||
| 514 |     { | 
            ||
| 515 | $nav = $this->getNavigationArray();  | 
            ||
| 516 | $page = '/' . trim(getCurrentPage(), '/') . '/';  | 
            ||
| 517 | |||
| 518 | $page_limit = 'author';  | 
            ||
| 519 | |||
| 520 |         foreach ($nav as $item) { | 
            ||
| 521 | if (  | 
            ||
| 522 | // If page directly matches one of the children  | 
            ||
| 523 | General::in_array_multi($page, $item['children'])  | 
            ||
| 524 | // If the page namespace matches one of the children (this will usually drop query  | 
            ||
| 525 | // string parameters such as /edit/1/)  | 
            ||
| 526 | || General::in_array_multi(Symphony::getPageNamespace() . '/', $item['children'])  | 
            ||
| 527 |             ) { | 
            ||
| 528 |                 if (is_array($item['children'])) { | 
            ||
| 529 |                     foreach ($item['children'] as $c) { | 
            ||
| 530 |                         if ($c['link'] === $page && isset($c['limit'])) { | 
            ||
| 531 | $page_limit = $c['limit'];  | 
            ||
| 532 | // TODO: break out of the loop here in Symphony 3.0.0  | 
            ||
| 533 | }  | 
            ||
| 534 | }  | 
            ||
| 535 | }  | 
            ||
| 536 | |||
| 537 |                 if (isset($item['limit']) && $page_limit !== 'primary') { | 
            ||
| 538 |                     if ($page_limit === 'author' && $item['limit'] === 'developer') { | 
            ||
| 539 | $page_limit = 'developer';  | 
            ||
| 540 | }  | 
            ||
| 541 | }  | 
            ||
| 542 |             } elseif (isset($item['link']) && $page === $item['link'] && isset($item['limit'])) { | 
            ||
| 543 | $page_limit = $item['limit'];  | 
            ||
| 544 | }  | 
            ||
| 545 | }  | 
            ||
| 546 | |||
| 547 | $hasAccess = $this->doesAuthorHaveAccess($page_limit);  | 
            ||
| 548 | |||
| 549 |         if ($hasAccess) { | 
            ||
| 550 | $page_context = $this->getContext();  | 
            ||
| 551 | $section_handle = !isset($page_context['section_handle']) ? null : $page_context['section_handle'];  | 
            ||
| 552 | /**  | 
            ||
| 553 | * Immediately after the core access rules allowed access to this page  | 
            ||
| 554 | * (i.e. not called if the core rules denied it).  | 
            ||
| 555 | * Extension developers must only further restrict access to it.  | 
            ||
| 556 | * Extension developers must also take care of checking the current value  | 
            ||
| 557 | * of the allowed parameter in order to prevent conflicts with other extensions.  | 
            ||
| 558 | * `$context['allowed'] = $context['allowed'] && customLogic();`  | 
            ||
| 559 | *  | 
            ||
| 560 | * @delegate CanAccessPage  | 
            ||
| 561 | * @since Symphony 2.7.0  | 
            ||
| 562 | * @see doesAuthorHaveAccess()  | 
            ||
| 563 | * @param string $context  | 
            ||
| 564 | * '/backend/'  | 
            ||
| 565 | * @param bool $allowed  | 
            ||
| 566 | * A flag to further restrict access to the page, passed by reference  | 
            ||
| 567 | * @param string $page_limit  | 
            ||
| 568 | * The computed page limit for the current page  | 
            ||
| 569 | * @param string $page_url  | 
            ||
| 570 | * The computed page url for the current page  | 
            ||
| 571 | * @param int $section.id  | 
            ||
| 572 | * The id of the section for this url  | 
            ||
| 573 | * @param string $section.handle  | 
            ||
| 574 | * The handle of the section for this url  | 
            ||
| 575 | */  | 
            ||
| 576 |             Symphony::ExtensionManager()->notifyMembers('CanAccessPage', '/backend/', array( | 
            ||
| 577 | 'allowed' => &$hasAccess,  | 
            ||
| 578 | 'page_limit' => $page_limit,  | 
            ||
| 579 | 'page_url' => $page,  | 
            ||
| 580 | 'section' => array(  | 
            ||
| 581 | 'id' => !$section_handle ? 0 : SectionManager::fetchIDFromHandle($section_handle),  | 
            ||
| 582 | 'handle' => $section_handle  | 
            ||
| 583 | ),  | 
            ||
| 584 | ));  | 
            ||
| 585 | }  | 
            ||
| 586 | |||
| 587 | return $hasAccess;  | 
            ||
| 588 | }  | 
            ||
| 589 | |||
| 590 | /**  | 
            ||
| 591 | * Given the limit of the current navigation item or page, this function  | 
            ||
| 592 | * returns if the current Author can access that item or not.  | 
            ||
| 593 | *  | 
            ||
| 594 | * @since Symphony 2.5.1  | 
            ||
| 595 | * @param string $item_limit  | 
            ||
| 596 | * @return boolean  | 
            ||
| 597 | */  | 
            ||
| 598 | public function doesAuthorHaveAccess($item_limit = null)  | 
            ||
| 599 |     { | 
            ||
| 600 | $can_access = false;  | 
            ||
| 601 | |||
| 602 |         if (!isset($item_limit) || $item_limit === 'author') { | 
            ||
| 603 | $can_access = true;  | 
            ||
| 604 |         } elseif ($item_limit === 'developer' && Symphony::Author()->isDeveloper()) { | 
            ||
| 605 | $can_access = true;  | 
            ||
| 606 |         } elseif ($item_limit === 'manager' && (Symphony::Author()->isManager() || Symphony::Author()->isDeveloper())) { | 
            ||
| 607 | $can_access = true;  | 
            ||
| 608 |         } elseif ($item_limit === 'primary' && Symphony::Author()->isPrimaryAccount()) { | 
            ||
| 609 | $can_access = true;  | 
            ||
| 610 | }  | 
            ||
| 611 | |||
| 612 | return $can_access;  | 
            ||
| 613 | }  | 
            ||
| 614 | |||
| 615 | /**  | 
            ||
| 616 | * Appends the `$this->Header`, `$this->Context` and `$this->Contents`  | 
            ||
| 617 | * to `$this->Wrapper` before adding the ID and class attributes for  | 
            ||
| 618 | * the `<body>` element. This function will also place any Drawer elements  | 
            ||
| 619 | * in their relevant positions in the page. After this has completed the  | 
            ||
| 620 | * parent `generate()` is called which will convert the `XMLElement`'s  | 
            ||
| 621 | * into strings ready for output.  | 
            ||
| 622 | *  | 
            ||
| 623 | * @see core.HTMLPage#generate()  | 
            ||
| 624 | * @param null $page  | 
            ||
| 625 | * @return string  | 
            ||
| 626 | */  | 
            ||
| 627 | public function generate($page = null)  | 
            ||
| 628 |     { | 
            ||
| 629 | $this->Wrapper->appendChild($this->Header);  | 
            ||
| 630 | |||
| 631 | // Add horizontal drawers (inside #context)  | 
            ||
| 632 |         if (isset($this->Drawer['horizontal'])) { | 
            ||
| 633 | $this->Context->appendChildArray($this->Drawer['horizontal']);  | 
            ||
| 634 | }  | 
            ||
| 635 | |||
| 636 | $this->Wrapper->appendChild($this->Context);  | 
            ||
| 637 | |||
| 638 | // Add vertical-left drawers (between #context and #contents)  | 
            ||
| 639 |         if (isset($this->Drawer['vertical-left'])) { | 
            ||
| 640 | $this->Contents->appendChildArray($this->Drawer['vertical-left']);  | 
            ||
| 641 | }  | 
            ||
| 642 | |||
| 643 | // Add vertical-right drawers (after #contents)  | 
            ||
| 644 |         if (isset($this->Drawer['vertical-right'])) { | 
            ||
| 645 | $this->Contents->appendChildArray($this->Drawer['vertical-right']);  | 
            ||
| 646 | }  | 
            ||
| 647 | |||
| 648 | $this->Wrapper->appendChild($this->Contents);  | 
            ||
| 649 | |||
| 650 | $this->Body->appendChild($this->Wrapper);  | 
            ||
| 651 | |||
| 652 | $this->__appendBodyId();  | 
            ||
| 653 | $this->__appendBodyClass($this->_context);  | 
            ||
| 654 | |||
| 655 | /**  | 
            ||
| 656 | * This is just prior to the page headers being rendered, and is suitable for changing them  | 
            ||
| 657 | * @delegate PreRenderHeaders  | 
            ||
| 658 | * @since Symphony 2.7.0  | 
            ||
| 659 | * @param string $context  | 
            ||
| 660 | * '/backend/'  | 
            ||
| 661 | */  | 
            ||
| 662 |         Symphony::ExtensionManager()->notifyMembers('PreRenderHeaders', '/backend/'); | 
            ||
| 663 | |||
| 664 | return parent::generate($page);  | 
            ||
| 665 | }  | 
            ||
| 666 | |||
| 667 | /**  | 
            ||
| 668 | * Uses this pages PHP classname as the `<body>` ID attribute.  | 
            ||
| 669 | * This function removes 'content' from the start of the classname  | 
            ||
| 670 | * and converts all uppercase letters to lowercase and prefixes them  | 
            ||
| 671 | * with a hyphen.  | 
            ||
| 672 | */  | 
            ||
| 673 | private function __appendBodyId()  | 
            ||
| 674 |     { | 
            ||
| 675 | // trim "content" from beginning of class name  | 
            ||
| 676 |         $body_id = preg_replace("/^content/", '', get_class($this)); | 
            ||
| 677 | |||
| 678 | // lowercase any uppercase letters and prefix with a hyphen  | 
            ||
| 679 | $body_id = trim(  | 
            ||
| 680 | preg_replace_callback(  | 
            ||
| 681 | "/([A-Z])/",  | 
            ||
| 682 |                 function($id) { | 
            ||
| 683 | return "-" . strtolower($id[0]);  | 
            ||
| 684 | },  | 
            ||
| 685 | $body_id  | 
            ||
| 686 | ),  | 
            ||
| 687 | '-'  | 
            ||
| 688 | );  | 
            ||
| 689 | |||
| 690 |         if (!empty($body_id)) { | 
            ||
| 691 |             $this->Body->setAttribute('id', trim($body_id)); | 
            ||
| 692 | }  | 
            ||
| 693 | }  | 
            ||
| 694 | |||
| 695 | /**  | 
            ||
| 696 | * Given the context of the current page, which is an associative  | 
            ||
| 697 | * array, this function will append the values to the page's body as  | 
            ||
| 698 | * classes. If an context value is numeric it will be prepended by 'id-',  | 
            ||
| 699 | * otherwise all classes will be prefixed by the context key.  | 
            ||
| 700 | *  | 
            ||
| 701 | * @param array $context  | 
            ||
| 702 | */  | 
            ||
| 703 | private function __appendBodyClass(array $context = array())  | 
            ||
| 704 |     { | 
            ||
| 705 | $body_class = '';  | 
            ||
| 706 | |||
| 707 |         foreach ($context as $key => $value) { | 
            ||
| 708 |             if (is_numeric($value)) { | 
            ||
| 709 | $value = 'id-' . $value;  | 
            ||
| 710 | |||
| 711 | // Add prefixes to all context values by making the  | 
            ||
| 712 |                 // class be {key}-{value}. #1397 ^BA | 
            ||
| 713 |             } elseif (!is_numeric($key) && isset($value)) { | 
            ||
| 714 | // Skip arrays  | 
            ||
| 715 |                 if (is_array($value)) { | 
            ||
| 716 | $value = null;  | 
            ||
| 717 |                 } else { | 
            ||
| 718 |                     $value = str_replace('_', '-', $key) . '-'. $value; | 
            ||
| 719 | }  | 
            ||
| 720 | }  | 
            ||
| 721 | |||
| 722 |             if ($value !== null) { | 
            ||
| 723 | $body_class .= trim($value) . ' ';  | 
            ||
| 724 | }  | 
            ||
| 725 | }  | 
            ||
| 726 | |||
| 727 |         $classes = array_merge(explode(' ', trim($body_class)), explode(' ', trim($this->_body_class))); | 
            ||
| 728 |         $body_class = trim(implode(' ', $classes)); | 
            ||
| 729 | |||
| 730 |         if (!empty($body_class)) { | 
            ||
| 731 |             $this->Body->setAttribute('class', $body_class); | 
            ||
| 732 | }  | 
            ||
| 733 | }  | 
            ||
| 734 | |||
| 735 | /**  | 
            ||
| 736 | * Called to build the content for the page. This function immediately calls  | 
            ||
| 737 | * `__switchboard()` which acts a bit of a controller to show content based on  | 
            ||
| 738 | * off a type, such as 'view' or 'action'. `AdministrationPages` can override this  | 
            ||
| 739 | * function to just display content if they do not need the switchboard functionality  | 
            ||
| 740 | *  | 
            ||
| 741 | * @see __switchboard()  | 
            ||
| 742 | */  | 
            ||
| 743 | public function view()  | 
            ||
| 744 |     { | 
            ||
| 745 | $this->__switchboard();  | 
            ||
| 746 | }  | 
            ||
| 747 | |||
| 748 | /**  | 
            ||
| 749 | * This function is called when `$_REQUEST` contains a key of 'action'.  | 
            ||
| 750 | * Any logic that needs to occur immediately for the action to complete  | 
            ||
| 751 | * should be contained within this function. By default this calls the  | 
            ||
| 752 | * `__switchboard` with the type set to 'action'.  | 
            ||
| 753 | *  | 
            ||
| 754 | * @see __switchboard()  | 
            ||
| 755 | */  | 
            ||
| 756 | public function action()  | 
            ||
| 757 |     { | 
            ||
| 758 |         $this->__switchboard('action'); | 
            ||
| 759 | }  | 
            ||
| 760 | |||
| 761 | /**  | 
            ||
| 762 | * The `__switchboard` function acts as a controller to display content  | 
            ||
| 763 | * based off the $type. By default, the `$type` is 'view' but it can be set  | 
            ||
| 764 | * also set to 'action'. The `$type` is prepended by __ and the context is  | 
            ||
| 765 | * append to the $type to create the name of the function that will provide  | 
            ||
| 766 | * that logic. For example, if the $type was action and the context of the  | 
            ||
| 767 | * current page was new, the resulting function to be called would be named  | 
            ||
| 768 | * `__actionNew()`. If an action function is not provided by the Page, this function  | 
            ||
| 769 | * returns nothing, however if a view function is not provided, a 404 page  | 
            ||
| 770 | * will be returned.  | 
            ||
| 771 | *  | 
            ||
| 772 | * @param string $type  | 
            ||
| 773 | * Either 'view' or 'action', by default this will be 'view'  | 
            ||
| 774 | * @throws SymphonyErrorPage  | 
            ||
| 775 | */  | 
            ||
| 776 | public function __switchboard($type = 'view')  | 
            ||
| 777 |     { | 
            ||
| 778 |         if (!isset($this->_context[0]) || trim($this->_context[0]) === '') { | 
            ||
| 779 | $context = 'index';  | 
            ||
| 780 |         } else { | 
            ||
| 781 | $context = $this->_context[0];  | 
            ||
| 782 | }  | 
            ||
| 783 | |||
| 784 | $function = ($type == 'action' ? '__action' : '__view') . ucfirst($context);  | 
            ||
| 785 | |||
| 786 |         if (!method_exists($this, $function)) { | 
            ||
| 787 | // If there is no action function, just return without doing anything  | 
            ||
| 788 |             if ($type == 'action') { | 
            ||
| 789 | return;  | 
            ||
| 790 | }  | 
            ||
| 791 | |||
| 792 | Administration::instance()->errorPageNotFound();  | 
            ||
| 793 | }  | 
            ||
| 794 | |||
| 795 | $this->$function(null);  | 
            ||
| 796 | }  | 
            ||
| 797 | |||
| 798 | /**  | 
            ||
| 799 | * If `$this->Alert` is set, it will be added to this page. The  | 
            ||
| 800 | * `AppendPageAlert` delegate is fired to allow extensions to provide their  | 
            ||
| 801 | * their own Alert messages for this page. Since Symphony 2.3, there may be  | 
            ||
| 802 | * more than one `Alert` per page. Alerts are displayed in the order of  | 
            ||
| 803 | * severity, with Errors first, then Success alerts followed by Notices.  | 
            ||
| 804 | *  | 
            ||
| 805 | * @uses AppendPageAlert  | 
            ||
| 806 | */  | 
            ||
| 807 | public function appendAlert()  | 
            ||
| 808 |     { | 
            ||
| 809 | /**  | 
            ||
| 810 | * Allows for appending of alerts. Administration::instance()->Page->Alert is way to tell what  | 
            ||
| 811 | * is currently in the system  | 
            ||
| 812 | *  | 
            ||
| 813 | * @delegate AppendPageAlert  | 
            ||
| 814 | * @param string $context  | 
            ||
| 815 | * '/backend/'  | 
            ||
| 816 | */  | 
            ||
| 817 |         Symphony::ExtensionManager()->notifyMembers('AppendPageAlert', '/backend/'); | 
            ||
| 818 | |||
| 819 | |||
| 820 |         if (!is_array($this->Alert) || empty($this->Alert)) { | 
            ||
| 821 | return;  | 
            ||
| 822 | }  | 
            ||
| 823 | |||
| 824 | usort($this->Alert, array($this, 'sortAlerts'));  | 
            ||
| 825 | |||
| 826 | // Using prependChild ruins our order (it's backwards, but with most  | 
            ||
| 827 | // recent notices coming after oldest notices), so reversing the array  | 
            ||
| 828 | // fixes this. We need to prepend so that without Javascript the notices  | 
            ||
| 829 | // are at the top of the markup. See #1312  | 
            ||
| 830 | $this->Alert = array_reverse($this->Alert);  | 
            ||
| 831 | |||
| 832 |         foreach ($this->Alert as $alert) { | 
            ||
| 833 | $this->Header->prependChild($alert->asXML());  | 
            ||
| 834 | }  | 
            ||
| 835 | }  | 
            ||
| 836 | |||
| 837 | // Errors first, success next, then notices.  | 
            ||
| 838 | public function sortAlerts($a, $b)  | 
            ||
| 839 |     { | 
            ||
| 840 |         if ($a->{'type'} === $b->{'type'}) { | 
            ||
| 841 | return 0;  | 
            ||
| 842 | }  | 
            ||
| 843 | |||
| 844 | if (  | 
            ||
| 845 |             ($a->{'type'} === Alert::ERROR && $a->{'type'} !== $b->{'type'}) | 
            ||
| 846 |             || ($a->{'type'} === Alert::SUCCESS && $b->{'type'} === Alert::NOTICE) | 
            ||
| 847 |         ) { | 
            ||
| 848 | return -1;  | 
            ||
| 849 | }  | 
            ||
| 850 | |||
| 851 | return 1;  | 
            ||
| 852 | }  | 
            ||
| 853 | |||
| 854 | /**  | 
            ||
| 855 | * This function will append the Navigation to the AdministrationPage.  | 
            ||
| 856 | * It fires a delegate, NavigationPreRender, to allow extensions to manipulate  | 
            ||
| 857 | * the navigation. Extensions should not use this to add their own navigation,  | 
            ||
| 858 | * they should provide the navigation through their fetchNavigation function.  | 
            ||
| 859 | * Note with the Section navigation groups, if there is only one section in a group  | 
            ||
| 860 | * and that section is set to visible, the group will not appear in the navigation.  | 
            ||
| 861 | *  | 
            ||
| 862 | * @uses NavigationPreRender  | 
            ||
| 863 | * @see getNavigationArray()  | 
            ||
| 864 | * @see toolkit.Extension#fetchNavigation()  | 
            ||
| 865 | */  | 
            ||
| 866 | public function appendNavigation()  | 
            ||
| 867 |     { | 
            ||
| 868 | $nav = $this->getNavigationArray();  | 
            ||
| 869 | |||
| 870 | /**  | 
            ||
| 871 | * Immediately before displaying the admin navigation. Provided with the  | 
            ||
| 872 | * navigation array. Manipulating it will alter the navigation for all pages.  | 
            ||
| 873 | *  | 
            ||
| 874 | * @delegate NavigationPreRender  | 
            ||
| 875 | * @param string $context  | 
            ||
| 876 | * '/backend/'  | 
            ||
| 877 | * @param array $nav  | 
            ||
| 878 | * An associative array of the current navigation, passed by reference  | 
            ||
| 879 | */  | 
            ||
| 880 |         Symphony::ExtensionManager()->notifyMembers('NavigationPreRender', '/backend/', array( | 
            ||
| 881 | 'navigation' => &$nav,  | 
            ||
| 882 | ));  | 
            ||
| 883 | |||
| 884 |         $navElement = new XMLElement('nav', null, array('id' => 'nav', 'role' => 'navigation')); | 
            ||
| 885 |         $contentNav = new XMLElement('ul', null, array('class' => 'content', 'role' => 'menubar')); | 
            ||
| 886 |         $structureNav = new XMLElement('ul', null, array('class' => 'structure', 'role' => 'menubar')); | 
            ||
| 887 | |||
| 888 |         foreach ($nav as $n) { | 
            ||
| 889 |             if (isset($n['visible']) && $n['visible'] === 'no') { | 
            ||
| 890 | continue;  | 
            ||
| 891 | }  | 
            ||
| 892 | |||
| 893 | $item_limit = isset($n['limit']) ? $n['limit'] : null;  | 
            ||
| 894 | |||
| 895 |             if ($this->doesAuthorHaveAccess($item_limit)) { | 
            ||
| 896 |                 $xGroup = new XMLElement('li', General::sanitize($n['name']), array('role' => 'presentation')); | 
            ||
| 897 | |||
| 898 |                 if (isset($n['class']) && trim($n['name']) !== '') { | 
            ||
| 899 |                     $xGroup->setAttribute('class', $n['class']); | 
            ||
| 900 | }  | 
            ||
| 901 | |||
| 902 | $hasChildren = false;  | 
            ||
| 903 |                 $xChildren = new XMLElement('ul', null, array('role' => 'menu')); | 
            ||
| 904 | |||
| 905 |                 if (is_array($n['children']) && !empty($n['children'])) { | 
            ||
| 906 |                     foreach ($n['children'] as $c) { | 
            ||
| 907 | // adapt for Yes and yes  | 
            ||
| 908 |                         if (strtolower($c['visible']) !== 'yes') { | 
            ||
| 909 | continue;  | 
            ||
| 910 | }  | 
            ||
| 911 | |||
| 912 | $child_item_limit = isset($c['limit']) ? $c['limit'] : null;  | 
            ||
| 913 | |||
| 914 |                         if ($this->doesAuthorHaveAccess($child_item_limit)) { | 
            ||
| 915 |                             $xChild = new XMLElement('li'); | 
            ||
| 916 |                             $xChild->setAttribute('role', 'menuitem'); | 
            ||
| 917 | $linkChild = Widget::Anchor(General::sanitize($c['name']), SYMPHONY_URL . $c['link']);  | 
            ||
| 918 |                             if (isset($c['target'])) { | 
            ||
| 919 |                                 $linkChild->setAttribute('target', $c['target']); | 
            ||
| 920 | }  | 
            ||
| 921 | $xChild->appendChild($linkChild);  | 
            ||
| 922 | $xChildren->appendChild($xChild);  | 
            ||
| 923 | $hasChildren = true;  | 
            ||
| 924 | }  | 
            ||
| 925 | }  | 
            ||
| 926 | |||
| 927 |                     if ($hasChildren) { | 
            ||
| 928 |                         $xGroup->setAttribute('aria-haspopup', 'true'); | 
            ||
| 929 | $xGroup->appendChild($xChildren);  | 
            ||
| 930 | |||
| 931 |                         if ($n['type'] === 'content') { | 
            ||
| 932 | $contentNav->appendChild($xGroup);  | 
            ||
| 933 |                         } elseif ($n['type'] === 'structure') { | 
            ||
| 934 | $structureNav->prependChild($xGroup);  | 
            ||
| 935 | }  | 
            ||
| 936 | }  | 
            ||
| 937 | }  | 
            ||
| 938 | }  | 
            ||
| 939 | }  | 
            ||
| 940 | |||
| 941 | $navElement->appendChild($contentNav);  | 
            ||
| 942 | $navElement->appendChild($structureNav);  | 
            ||
| 943 | $this->Header->appendChild($navElement);  | 
            ||
| 944 |         Symphony::Profiler()->sample('Navigation Built', PROFILE_LAP); | 
            ||
| 945 | }  | 
            ||
| 946 | |||
| 947 | /**  | 
            ||
| 948 | * Returns the `$_navigation` variable of this Page. If it is empty,  | 
            ||
| 949 | * it will be built by `__buildNavigation`  | 
            ||
| 950 | *  | 
            ||
| 951 | * When it calls `__buildNavigation`, it fires a delegate, NavigationPostBuild,  | 
            ||
| 952 | * to allow extensions to manipulate the navigation.  | 
            ||
| 953 | *  | 
            ||
| 954 | * @uses NavigationPostBuild  | 
            ||
| 955 | * @see __buildNavigation()  | 
            ||
| 956 | * @return array  | 
            ||
| 957 | */  | 
            ||
| 958 | public function getNavigationArray()  | 
            ||
| 959 |     { | 
            ||
| 960 |         if (empty($this->_navigation)) { | 
            ||
| 961 | $this->__buildNavigation();  | 
            ||
| 962 | }  | 
            ||
| 963 | |||
| 964 | return $this->_navigation;  | 
            ||
| 965 | }  | 
            ||
| 966 | |||
| 967 | /**  | 
            ||
| 968 | * This method fills the `$nav` array with value  | 
            ||
| 969 | * from the `ASSETS/xml/navigation.xml` file  | 
            ||
| 970 | *  | 
            ||
| 971 | * @link http://github.com/symphonycms/symphony-2/blob/master/symphony/assets/xml/navigation.xml  | 
            ||
| 972 | *  | 
            ||
| 973 | * @since Symphony 2.3.2  | 
            ||
| 974 | *  | 
            ||
| 975 | * @param array $nav  | 
            ||
| 976 | * The navigation array that will receive nav nodes  | 
            ||
| 977 | */  | 
            ||
| 978 | private function buildXmlNavigation(&$nav)  | 
            ||
| 979 |     { | 
            ||
| 980 | $xml = simplexml_load_file(ASSETS . '/xml/navigation.xml');  | 
            ||
| 981 | |||
| 982 | // Loop over the default Symphony navigation file, converting  | 
            ||
| 983 | // it into an associative array representation  | 
            ||
| 984 |         foreach ($xml->xpath('/navigation/group') as $n) { | 
            ||
| 985 | $index = (string)$n->attributes()->index;  | 
            ||
| 986 |             $children = $n->xpath('children/item'); | 
            ||
| 987 | $content = $n->attributes();  | 
            ||
| 988 | |||
| 989 | // If the index is already set, increment the index and check again.  | 
            ||
| 990 | // Rinse and repeat until the index is not set.  | 
            ||
| 991 |             if (isset($nav[$index])) { | 
            ||
| 992 |                 do { | 
            ||
| 993 | $index++;  | 
            ||
| 994 | } while (isset($nav[$index]));  | 
            ||
| 995 | }  | 
            ||
| 996 | |||
| 997 | $nav[$index] = array(  | 
            ||
| 998 | 'name' => __(strval($content->name)),  | 
            ||
| 999 | 'type' => 'structure',  | 
            ||
| 1000 | 'index' => $index,  | 
            ||
| 1001 | 'children' => array()  | 
            ||
| 1002 | );  | 
            ||
| 1003 | |||
| 1004 |             if (strlen(trim((string)$content->limit)) > 0) { | 
            ||
| 1005 | $nav[$index]['limit'] = (string)$content->limit;  | 
            ||
| 1006 | }  | 
            ||
| 1007 | |||
| 1008 |             if (count($children) > 0) { | 
            ||
| 1009 |                 foreach ($children as $child) { | 
            ||
| 1010 | $item = array(  | 
            ||
| 1011 | 'link' => (string)$child->attributes()->link,  | 
            ||
| 1012 | 'name' => __(strval($child->attributes()->name)),  | 
            ||
| 1013 | 'visible' => ((string)$child->attributes()->visible == 'no' ? 'no' : 'yes'),  | 
            ||
| 1014 | );  | 
            ||
| 1015 | |||
| 1016 | $limit = (string)$child->attributes()->limit;  | 
            ||
| 1017 | |||
| 1018 |                     if (strlen(trim($limit)) > 0) { | 
            ||
| 1019 | $item['limit'] = $limit;  | 
            ||
| 1020 | }  | 
            ||
| 1021 | |||
| 1022 | $nav[$index]['children'][] = $item;  | 
            ||
| 1023 | }  | 
            ||
| 1024 | }  | 
            ||
| 1025 | }  | 
            ||
| 1026 | }  | 
            ||
| 1027 | |||
| 1028 | /**  | 
            ||
| 1029 | * This method fills the `$nav` array with value  | 
            ||
| 1030 | * from each Section  | 
            ||
| 1031 | *  | 
            ||
| 1032 | * @since Symphony 2.3.2  | 
            ||
| 1033 | *  | 
            ||
| 1034 | * @param array $nav  | 
            ||
| 1035 | * The navigation array that will receive nav nodes  | 
            ||
| 1036 | */  | 
            ||
| 1037 | private function buildSectionNavigation(&$nav)  | 
            ||
| 1038 |     { | 
            ||
| 1039 | // Build the section navigation, grouped by their navigation groups  | 
            ||
| 1040 |         $sections = (new SectionManager)->select()->sort('sortorder')->execute()->rows(); | 
            ||
| 1041 | |||
| 1042 |         foreach ($sections as $s) { | 
            ||
| 1043 |             $group_index = self::__navigationFindGroupIndex($nav, $s->get('navigation_group')); | 
            ||
| 1044 | |||
| 1045 |             if ($group_index === false) { | 
            ||
| 1046 | $group_index = General::array_find_available_index($nav, 0);  | 
            ||
| 1047 | |||
| 1048 | $nav[$group_index] = array(  | 
            ||
| 1049 |                     'name' => $s->get('navigation_group'), | 
            ||
| 1050 | 'type' => 'content',  | 
            ||
| 1051 | 'index' => $group_index,  | 
            ||
| 1052 | 'children' => array()  | 
            ||
| 1053 | );  | 
            ||
| 1054 | }  | 
            ||
| 1055 | |||
| 1056 | $hasAccess = true;  | 
            ||
| 1057 |             $url = '/publish/' . $s->get('handle') . '/'; | 
            ||
| 1058 | /**  | 
            ||
| 1059 | * Immediately after the core access rules allowed access to this page  | 
            ||
| 1060 | * (i.e. not called if the core rules denied it).  | 
            ||
| 1061 | * Extension developers must only further restrict access to it.  | 
            ||
| 1062 | * Extension developers must also take care of checking the current value  | 
            ||
| 1063 | * of the allowed parameter in order to prevent conflicts with other extensions.  | 
            ||
| 1064 | * `$context['allowed'] = $context['allowed'] && customLogic();`  | 
            ||
| 1065 | *  | 
            ||
| 1066 | * @delegate CanAccessPage  | 
            ||
| 1067 | * @since Symphony 2.7.0  | 
            ||
| 1068 | * @see doesAuthorHaveAccess()  | 
            ||
| 1069 | * @param string $context  | 
            ||
| 1070 | * '/backend/'  | 
            ||
| 1071 | * @param bool $allowed  | 
            ||
| 1072 | * A flag to further restrict access to the page, passed by reference  | 
            ||
| 1073 | * @param string $page_limit  | 
            ||
| 1074 | * The computed page limit for the current page  | 
            ||
| 1075 | * @param string $page_url  | 
            ||
| 1076 | * The computed page url for the current page  | 
            ||
| 1077 | * @param int $section.id  | 
            ||
| 1078 | * The id of the section for this url  | 
            ||
| 1079 | * @param string $section.handle  | 
            ||
| 1080 | * The handle of the section for this url  | 
            ||
| 1081 | */  | 
            ||
| 1082 |             Symphony::ExtensionManager()->notifyMembers('CanAccessPage', '/backend/', array( | 
            ||
| 1083 | 'allowed' => &$hasAccess,  | 
            ||
| 1084 | 'page_limit' => 'author',  | 
            ||
| 1085 | 'page_url' => $url,  | 
            ||
| 1086 | 'section' => array(  | 
            ||
| 1087 |                     'id' => $s->get('id'), | 
            ||
| 1088 |                     'handle' => $s->get('handle') | 
            ||
| 1089 | ),  | 
            ||
| 1090 | ));  | 
            ||
| 1091 | |||
| 1092 |             if ($hasAccess) { | 
            ||
| 1093 | $nav[$group_index]['children'][] = array(  | 
            ||
| 1094 | 'link' => $url,  | 
            ||
| 1095 |                     'name' => $s->get('name'), | 
            ||
| 1096 | 'type' => 'section',  | 
            ||
| 1097 | 'section' => array(  | 
            ||
| 1098 |                         'id' => $s->get('id'), | 
            ||
| 1099 |                         'handle' => $s->get('handle') | 
            ||
| 1100 | ),  | 
            ||
| 1101 |                     'visible' => ($s->get('hidden') == 'no' ? 'yes' : 'no') | 
            ||
| 1102 | );  | 
            ||
| 1103 | }  | 
            ||
| 1104 | }  | 
            ||
| 1105 | }  | 
            ||
| 1106 | |||
| 1107 | /**  | 
            ||
| 1108 | * This method fills the `$nav` array with value  | 
            ||
| 1109 | * from each Extension's `fetchNavigation` method  | 
            ||
| 1110 | *  | 
            ||
| 1111 | * @since Symphony 2.3.2  | 
            ||
| 1112 | *  | 
            ||
| 1113 | * @param array $nav  | 
            ||
| 1114 | * The navigation array that will receive nav nodes  | 
            ||
| 1115 | * @throws Exception  | 
            ||
| 1116 | * @throws SymphonyErrorPage  | 
            ||
| 1117 | */  | 
            ||
| 1118 | private function buildExtensionsNavigation(&$nav)  | 
            ||
| 1119 |     { | 
            ||
| 1120 | // Loop over all the installed extensions to add in other navigation items  | 
            ||
| 1121 | $extensions = Symphony::ExtensionManager()->listInstalledHandles();  | 
            ||
| 1122 | |||
| 1123 |         foreach ($extensions as $e) { | 
            ||
| 1124 | $extension = Symphony::ExtensionManager()->getInstance($e);  | 
            ||
| 1125 | $extension_navigation = $extension->fetchNavigation();  | 
            ||
| 1126 | |||
| 1127 |             if (is_array($extension_navigation) && !empty($extension_navigation)) { | 
            ||
| 1128 |                 foreach ($extension_navigation as $item) { | 
            ||
| 1129 | $type = isset($item['children']) ? Extension::NAV_GROUP : Extension::NAV_CHILD;  | 
            ||
| 1130 | |||
| 1131 |                     switch ($type) { | 
            ||
| 1132 | case Extension::NAV_GROUP:  | 
            ||
| 1133 | $index = General::array_find_available_index($nav, $item['location']);  | 
            ||
| 1134 | |||
| 1135 | // Actual group  | 
            ||
| 1136 | $nav[$index] = self::createParentNavItem($index, $item);  | 
            ||
| 1137 | |||
| 1138 | // Render its children  | 
            ||
| 1139 |                             foreach ($item['children'] as $child) { | 
            ||
| 1140 | $nav[$index]['children'][] = self::createChildNavItem($child, $e);  | 
            ||
| 1141 | }  | 
            ||
| 1142 | |||
| 1143 | break;  | 
            ||
| 1144 | |||
| 1145 | case Extension::NAV_CHILD:  | 
            ||
| 1146 |                             if (!is_numeric($item['location'])) { | 
            ||
| 1147 | // is a navigation group  | 
            ||
| 1148 | $group_name = $item['location'];  | 
            ||
| 1149 | $group_index = self::__navigationFindGroupIndex($nav, $item['location']);  | 
            ||
| 1150 |                             } else { | 
            ||
| 1151 | // is a legacy numeric index  | 
            ||
| 1152 | $group_index = $item['location'];  | 
            ||
| 1153 | }  | 
            ||
| 1154 | |||
| 1155 | $child = self::createChildNavItem($item, $e);  | 
            ||
| 1156 | |||
| 1157 |                             if ($group_index === false) { | 
            ||
| 1158 | $group_index = General::array_find_available_index($nav, 0);  | 
            ||
| 1159 | |||
| 1160 | $nav_parent = self::createParentNavItem($group_index, $item);  | 
            ||
| 1161 | $nav_parent['name'] = $group_name;  | 
            ||
| 1162 | $nav_parent['children'] = array($child);  | 
            ||
| 1163 | |||
| 1164 | // add new navigation group  | 
            ||
| 1165 | $nav[$group_index] = $nav_parent;  | 
            ||
| 1166 |                             } else { | 
            ||
| 1167 | // add new location by index  | 
            ||
| 1168 | $nav[$group_index]['children'][] = $child;  | 
            ||
| 1169 | }  | 
            ||
| 1170 | |||
| 1171 | break;  | 
            ||
| 1172 | }  | 
            ||
| 1173 | }  | 
            ||
| 1174 | }  | 
            ||
| 1175 | }  | 
            ||
| 1176 | }  | 
            ||
| 1177 | |||
| 1178 | /**  | 
            ||
| 1179 | * This function builds out a navigation menu item for parents. Parents display  | 
            ||
| 1180 | * in the top level navigation of the backend and may have children (dropdown menus)  | 
            ||
| 1181 | *  | 
            ||
| 1182 | * @since Symphony 2.5.1  | 
            ||
| 1183 | * @param integer $index  | 
            ||
| 1184 | * @param array $item  | 
            ||
| 1185 | * @return array  | 
            ||
| 1186 | */  | 
            ||
| 1187 | private static function createParentNavItem($index, $item)  | 
            ||
| 1188 |     { | 
            ||
| 1189 | $nav_item = array(  | 
            ||
| 1190 | 'name' => $item['name'],  | 
            ||
| 1191 | 'type' => isset($item['type']) ? $item['type'] : 'structure',  | 
            ||
| 1192 | 'index' => $index,  | 
            ||
| 1193 | 'children' => array(),  | 
            ||
| 1194 | 'limit' => isset($item['limit']) ? $item['limit'] : null  | 
            ||
| 1195 | );  | 
            ||
| 1196 | |||
| 1197 | return $nav_item;  | 
            ||
| 1198 | }  | 
            ||
| 1199 | |||
| 1200 | /**  | 
            ||
| 1201 | * This function builds out a navigation menu item for children. Children  | 
            ||
| 1202 | * live under a parent navigation item and are shown on hover.  | 
            ||
| 1203 | *  | 
            ||
| 1204 | * @since Symphony 2.5.1  | 
            ||
| 1205 | * @param array $item  | 
            ||
| 1206 | * @param string $extension_handle  | 
            ||
| 1207 | * @return array  | 
            ||
| 1208 | */  | 
            ||
| 1209 | private static function createChildNavItem($item, $extension_handle)  | 
            ||
| 1210 |     { | 
            ||
| 1211 |         if (!isset($item['relative']) || $item['relative'] === true) { | 
            ||
| 1212 | $link = '/extension/' . $extension_handle . '/' . ltrim($item['link'], '/');  | 
            ||
| 1213 |         } else { | 
            ||
| 1214 | $link = '/' . ltrim($item['link'], '/');  | 
            ||
| 1215 | }  | 
            ||
| 1216 | |||
| 1217 | $nav_item = array(  | 
            ||
| 1218 | 'link' => $link,  | 
            ||
| 1219 | 'name' => $item['name'],  | 
            ||
| 1220 | 'visible' => (isset($item['visible']) && $item['visible'] == 'no') ? 'no' : 'yes',  | 
            ||
| 1221 | 'limit' => isset($item['limit']) ? $item['limit'] : null,  | 
            ||
| 1222 | 'target' => isset($item['target']) ? $item['target'] : null  | 
            ||
| 1223 | );  | 
            ||
| 1224 | |||
| 1225 | return $nav_item;  | 
            ||
| 1226 | }  | 
            ||
| 1227 | |||
| 1228 | /**  | 
            ||
| 1229 | * This function populates the `$_navigation` array with an associative array  | 
            ||
| 1230 | * of all the navigation groups and their links. Symphony only supports one  | 
            ||
| 1231 | * level of navigation, so children links cannot have children links. The default  | 
            ||
| 1232 | * Symphony navigation is found in the `ASSETS/xml/navigation.xml` folder. This is  | 
            ||
| 1233 | * loaded first, and then the Section navigation is built, followed by the Extension  | 
            ||
| 1234 | * navigation. Additionally, this function will set the active group of the navigation  | 
            ||
| 1235 | * by checking the current page against the array of links.  | 
            ||
| 1236 | *  | 
            ||
| 1237 | * It fires a delegate, NavigationPostBuild, to allow extensions to manipulate  | 
            ||
| 1238 | * the navigation.  | 
            ||
| 1239 | *  | 
            ||
| 1240 | * @uses NavigationPostBuild  | 
            ||
| 1241 | * @link https://github.com/symphonycms/symphony-2/blob/master/symphony/assets/xml/navigation.xml  | 
            ||
| 1242 | * @link https://github.com/symphonycms/symphony-2/blob/master/symphony/lib/toolkit/class.extension.php  | 
            ||
| 1243 | */  | 
            ||
| 1244 | public function __buildNavigation()  | 
            ||
| 1245 |     { | 
            ||
| 1246 | $nav = array();  | 
            ||
| 1247 | |||
| 1248 | $this->buildXmlNavigation($nav);  | 
            ||
| 1249 | $this->buildSectionNavigation($nav);  | 
            ||
| 1250 | $this->buildExtensionsNavigation($nav);  | 
            ||
| 1251 | |||
| 1252 | $pageCallback = Administration::instance()->getPageCallback();  | 
            ||
| 1253 | |||
| 1254 | $pageRoot = $pageCallback['pageroot'] . (isset($pageCallback['context'][0]) ? $pageCallback['context'][0] . '/' : '');  | 
            ||
| 1255 | $found = self::__findActiveNavigationGroup($nav, $pageRoot);  | 
            ||
| 1256 | |||
| 1257 | // Normal searches failed. Use a regular expression using the page root. This is less  | 
            ||
| 1258 | // efficient and should never really get invoked unless something weird is going on  | 
            ||
| 1259 |         if (!$found) { | 
            ||
| 1260 |             self::__findActiveNavigationGroup($nav, '/^' . str_replace('/', '\/', $pageCallback['pageroot']) . '/i', true); | 
            ||
| 1261 | }  | 
            ||
| 1262 | |||
| 1263 | ksort($nav);  | 
            ||
| 1264 | $this->_navigation = $nav;  | 
            ||
| 1265 | |||
| 1266 | /**  | 
            ||
| 1267 | * Immediately after the navigation array as been built. Provided with the  | 
            ||
| 1268 | * navigation array. Manipulating it will alter the navigation for all pages.  | 
            ||
| 1269 | * Developers can also alter the 'limit' property of each page to allow more  | 
            ||
| 1270 | * or less access to them.  | 
            ||
| 1271 | * Preventing a user from accessing the page affects both the navigation and the  | 
            ||
| 1272 | * page access rights: user will get a 403 Forbidden error if not authorized.  | 
            ||
| 1273 | *  | 
            ||
| 1274 | * @delegate NavigationPostBuild  | 
            ||
| 1275 | * @since Symphony 2.7.0  | 
            ||
| 1276 | * @param string $context  | 
            ||
| 1277 | * '/backend/'  | 
            ||
| 1278 | * @param array $nav  | 
            ||
| 1279 | * An associative array of the current navigation, passed by reference  | 
            ||
| 1280 | */  | 
            ||
| 1281 |         Symphony::ExtensionManager()->notifyMembers('NavigationPostBuild', '/backend/', array( | 
            ||
| 1282 | 'navigation' => &$this->_navigation,  | 
            ||
| 1283 | ));  | 
            ||
| 1284 | }  | 
            ||
| 1285 | |||
| 1286 | /**  | 
            ||
| 1287 | * Given an associative array representing the navigation, and a group,  | 
            ||
| 1288 | * this function will attempt to return the index of the group in the navigation  | 
            ||
| 1289 | * array. If it is found, it will return the index, otherwise it will return false.  | 
            ||
| 1290 | *  | 
            ||
| 1291 | * @param array $nav  | 
            ||
| 1292 | * An associative array of the navigation where the key is the group  | 
            ||
| 1293 | * index, and the value is an associative array of 'name', 'index' and  | 
            ||
| 1294 | * 'children'. Name is the name of the this group, index is the same as  | 
            ||
| 1295 | * the key and children is an associative array of navigation items containing  | 
            ||
| 1296 | * the keys 'link', 'name' and 'visible'. The 'haystack'.  | 
            ||
| 1297 | * @param string $group  | 
            ||
| 1298 | * The group name to find, the 'needle'.  | 
            ||
| 1299 | * @return integer|boolean  | 
            ||
| 1300 | * If the group is found, the index will be returned, otherwise false.  | 
            ||
| 1301 | */  | 
            ||
| 1302 | private static function __navigationFindGroupIndex(array $nav, $group)  | 
            ||
| 1303 |     { | 
            ||
| 1304 |         foreach ($nav as $index => $item) { | 
            ||
| 1305 |             if ($item['name'] === $group) { | 
            ||
| 1306 | return $index;  | 
            ||
| 1307 | }  | 
            ||
| 1308 | }  | 
            ||
| 1309 | |||
| 1310 | return false;  | 
            ||
| 1311 | }  | 
            ||
| 1312 | |||
| 1313 | /**  | 
            ||
| 1314 | * Given the navigation array, this function will loop over all the items  | 
            ||
| 1315 | * to determine which is the 'active' navigation group, or in other words,  | 
            ||
| 1316 | * what group best represents the current page `$this->Author` is viewing.  | 
            ||
| 1317 | * This is done by checking the current page's link against all the links  | 
            ||
| 1318 | * provided in the `$nav`, and then flagging the group of the found link  | 
            ||
| 1319 | * with an 'active' CSS class. The current page's link omits any flags or  | 
            ||
| 1320 | * URL parameters and just uses the root page URL.  | 
            ||
| 1321 | *  | 
            ||
| 1322 | * @param array $nav  | 
            ||
| 1323 | * An associative array of the navigation where the key is the group  | 
            ||
| 1324 | * index, and the value is an associative array of 'name', 'index' and  | 
            ||
| 1325 | * 'children'. Name is the name of the this group, index is the same as  | 
            ||
| 1326 | * the key and children is an associative array of navigation items containing  | 
            ||
| 1327 | * the keys 'link', 'name' and 'visible'. The 'haystack'. This parameter is passed  | 
            ||
| 1328 | * by reference to this function.  | 
            ||
| 1329 | * @param string $pageroot  | 
            ||
| 1330 | * The current page the Author is the viewing, minus any flags or URL  | 
            ||
| 1331 | * parameters such as a Symphony object ID. eg. Section ID, Entry ID. This  | 
            ||
| 1332 | * parameter is also be a regular expression, but this is highly unlikely.  | 
            ||
| 1333 | * @param boolean $pattern  | 
            ||
| 1334 | * If set to true, the `$pageroot` represents a regular expression which will  | 
            ||
| 1335 | * determine if the active navigation item  | 
            ||
| 1336 | * @return boolean  | 
            ||
| 1337 | * Returns true if an active link was found, false otherwise. If true, the  | 
            ||
| 1338 | * navigation group of the active link will be given the CSS class 'active'  | 
            ||
| 1339 | */  | 
            ||
| 1340 | private static function __findActiveNavigationGroup(array &$nav, $pageroot, $pattern = false)  | 
            ||
| 1357 | }  | 
            ||
| 1358 | |||
| 1359 | /**  | 
            ||
| 1360 | * Creates the Symphony footer for an Administration page. By default  | 
            ||
| 1361 | * this includes the installed Symphony version and the currently logged  | 
            ||
| 1362 | * in Author. A delegate is provided to allow extensions to manipulate the  | 
            ||
| 1363 | * footer HTML, which is an XMLElement of a `<ul>` element.  | 
            ||
| 1364 | * Since Symphony 2.3, it no longer uses the `AddElementToFooter` delegate.  | 
            ||
| 1365 | */  | 
            ||
| 1366 | public function appendUserLinks()  | 
            ||
| 1367 |     { | 
            ||
| 1368 |         $ul = new XMLElement('ul', null, array('id' => 'session')); | 
            ||
| 1369 | |||
| 1370 |         $li = new XMLElement('li'); | 
            ||
| 1371 | $li->appendChild(  | 
            ||
| 1372 | Widget::Anchor(  | 
            ||
| 1373 | Symphony::Author()->getFullName(),  | 
            ||
| 1374 |                 SYMPHONY_URL . '/system/authors/edit/' . Symphony::Author()->get('id') . '/' | 
            ||
| 1375 | )  | 
            ||
| 1376 | );  | 
            ||
| 1377 | $ul->appendChild($li);  | 
            ||
| 1378 | |||
| 1379 |         $li = new XMLElement('li'); | 
            ||
| 1380 |         $li->appendChild(Widget::Anchor(__('Log out'), SYMPHONY_URL . '/logout/', null, null, null, array('accesskey' => 'l'))); | 
            ||
| 1381 | $ul->appendChild($li);  | 
            ||
| 1382 | |||
| 1383 | $this->Header->appendChild($ul);  | 
            ||
| 1384 | }  | 
            ||
| 1385 | |||
| 1386 | /**  | 
            ||
| 1387 | * Adds a localized Alert message for failed timestamp validations.  | 
            ||
| 1388 | * It also adds meta information about the last author and timestamp.  | 
            ||
| 1389 | *  | 
            ||
| 1390 | * @since Symphony 2.7.0  | 
            ||
| 1391 | * @param string $errorMessage  | 
            ||
| 1392 | * The error message to display.  | 
            ||
| 1393 | * @param Entry|Section $existingObject  | 
            ||
| 1394 | * The Entry or section object that failed validation.  | 
            ||
| 1395 | * @param string $action  | 
            ||
| 1396 | * The requested action.  | 
            ||
| 1397 | */  | 
            ||
| 1398 | public function addTimestampValidationPageAlert($errorMessage, $existingObject, $action)  | 
            ||
| 1437 | }  | 
            ||
| 1438 | }  | 
            ||
| 1439 |