GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — integration ( 5cdb31...81c93f )
by Brendan
04:03
created

Administration::__buildPage()   F

Complexity

Conditions 22
Paths 1806

Size

Total Lines 101
Code Lines 59

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 101
rs 2
cc 22
eloc 59
nc 1806
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @package core
5
 */
6
7
/**
8
 * The Administration class is an instance of Symphony that controls
9
 * all backend pages. These pages are HTMLPages are usually generated
10
 * using XMLElement before being rendered as HTML. These pages do not
11
 * use XSLT. The Administration is only accessible by logged in Authors
12
 */
13
14
class Administration extends Symphony
15
{
16
    /**
17
     * The path of the current page, ie. '/blueprints/sections/'
18
     * @var string
19
     */
20
    private $_currentPage  = null;
21
22
    /**
23
     * An associative array of the page's callback, including the keys
24
     * 'driver', which is a lowercase version of `$this->_currentPage`
25
     * with any slashes removed, 'classname', which is the name of the class
26
     * for this page, 'pageroot', which is the root page for the given page, (ie.
27
     * excluding /saved/, /created/ or any sub pages of the current page that are
28
     * handled using the _switchboard function.
29
     *
30
     * @see toolkit.AdministrationPage#__switchboard()
31
     * @var array
32
     */
33
    private $_callback = null;
34
35
    /**
36
     * The class representation of the current Symphony backend page,
37
     * which is a subclass of the `HTMLPage` class. Symphony uses a convention
38
     * of prefixing backend page classes with 'content'. ie. 'contentBlueprintsSections'
39
     * @var HTMLPage
40
     */
41
    public $Page;
42
43
    /**
44
     * Overrides the default Symphony constructor to add XSRF checking
45
     */
46
    protected function __construct()
47
    {
48
        parent::__construct();
49
50
        // Ensure the request is legitimate. RE: #1874
51
        if (self::isXSRFEnabled()) {
52
            XSRF::validateRequest();
53
        }
54
    }
55
56
    /**
57
     * This function returns an instance of the Administration
58
     * class. It is the only way to create a new Administration, as
59
     * it implements the Singleton interface
60
     *
61
     * @return Administration
62
     */
63
    public static function instance()
64
    {
65
        if (!(self::$_instance instanceof Administration)) {
66
            self::$_instance = new Administration;
67
        }
68
69
        return self::$_instance;
70
    }
71
72
    /**
73
     * Returns the current Page path, excluding the domain and Symphony path.
74
     *
75
     * @return string
76
     *  The path of the current page, ie. '/blueprints/sections/'
77
     */
78
    public function getCurrentPageURL()
79
    {
80
        return $this->_currentPage;
81
    }
82
83
    /**
84
     * Overrides the Symphony isLoggedIn function to allow Authors
85
     * to become logged into the backend when `$_REQUEST['auth-token']`
86
     * is present. This logs an Author in using the loginFromToken function.
87
     * A token may be 6 or 8 characters in length in the backend. A 6 or 16 character token
88
     * is used for forget password requests, whereas the 8 character token is used to login
89
     * an Author into the page
90
     *
91
     * @see core.Symphony#loginFromToken()
92
     * @return boolean
93
     */
94
    public static function isLoggedIn()
95
    {
96
        if (isset($_REQUEST['auth-token']) && $_REQUEST['auth-token'] && in_array(strlen($_REQUEST['auth-token']), array(6, 8, 16))) {
97
            return self::loginFromToken($_REQUEST['auth-token']);
98
        }
99
100
        return parent::isLoggedIn();
101
    }
102
103
    /**
104
     * Given the URL path of a Symphony backend page, this function will
105
     * attempt to resolve the URL to a Symphony content page in the backend
106
     * or a page provided by an extension. This function checks to ensure a user
107
     * is logged in, otherwise it will direct them to the login page
108
     *
109
     * @param string $page
110
     *  The URL path after the root of the Symphony installation, including a starting
111
     *  slash, such as '/login/'
112
     * @throws SymphonyErrorPage
113
     * @throws Exception
114
     * @return HTMLPage
115
     */
116
    private function __buildPage($page)
117
    {
118
        $is_logged_in = self::isLoggedIn();
119
120
        if (empty($page) || is_null($page)) {
121
            if (!$is_logged_in) {
122
                $page  = "/login";
123
            } else {
124
                // Will redirect an Author to their default area of the Backend
125
                // Integers are indicative of section's, text is treated as the path
126
                // to the page after `SYMPHONY_URL`
127
                $default_area = null;
128
129
                if (is_numeric(Symphony::Author()->get('default_area'))) {
130
                    $default_section = SectionManager::fetch(Symphony::Author()->get('default_area'));
131
132
                    if ($default_section instanceof Section) {
133
                        $section_handle = $default_section->get('handle');
134
                    }
135
136
                    if (!$section_handle) {
0 ignored issues
show
Bug introduced by
The variable $section_handle does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
137
                        $all_sections = SectionManager::fetch();
138
139
                        if (!empty($all_sections)) {
140
                            $section_handle = $all_sections[0]->get('handle');
141
                        } else {
142
                            $section_handle = null;
143
                        }
144
                    }
145
146
                    if (!is_null($section_handle)) {
147
                        $default_area = "/publish/{$section_handle}/";
148
                    }
149
                } elseif (!is_null(Symphony::Author()->get('default_area'))) {
150
                    $default_area = preg_replace('/^' . preg_quote(SYMPHONY_URL, '/') . '/i', '', Symphony::Author()->get('default_area'));
151
                }
152
153
                if (is_null($default_area)) {
154
                    if (Symphony::Author()->isDeveloper()) {
155
                        $all_sections = SectionManager::fetch();
156
                        $section_handle = !empty($all_sections) ? $all_sections[0]->get('handle') : null;
157
158
                        if (!is_null($section_handle)) {
159
                            // If there are sections created, redirect to the first one (sortorder)
160
                            redirect(SYMPHONY_URL . "/publish/{$section_handle}/");
161
                        } else {
162
                            // If there are no sections created, default to the Section page
163
                            redirect(SYMPHONY_URL . '/blueprints/sections/');
164
                        }
165
                    } else {
166
                        redirect(SYMPHONY_URL . "/system/authors/edit/".Symphony::Author()->get('id')."/");
167
                    }
168
                } else {
169
                    redirect(SYMPHONY_URL . $default_area);
170
                }
171
            }
172
        }
173
174
        if (!$this->_callback = $this->getPageCallback($page)) {
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getPageCallback($page) can also be of type false. However, the property $_callback is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
175
            if ($page === '/publish/') {
176
                $sections = SectionManager::fetch(null, 'ASC', 'sortorder');
177
                $section = current($sections);
178
                redirect(SYMPHONY_URL . '/publish/' . $section->get('handle'));
179
            } else {
180
                $this->errorPageNotFound();
181
            }
182
        }
183
184
        require_once($this->_callback['driver_location']);
185
        $this->Page = new $this->_callback['classname'];
186
187
        if (!$is_logged_in && $this->_callback['driver'] !== 'login') {
188
            if (is_callable(array($this->Page, 'handleFailedAuthorisation'))) {
189
                $this->Page->handleFailedAuthorisation();
190
            } else {
191
                include_once(CONTENT . '/content.login.php');
192
                $this->Page = new contentLogin;
193
194
                // Include the query string for the login, RE: #2324
195
                if ($queryString = $this->Page->__buildQueryString(array('symphony-page', 'mode'), FILTER_SANITIZE_STRING)) {
196
                    $page .= '?' . $queryString;
197
                }
198
                $this->Page->build(array('redirect' => $page));
199
            }
200
        } else {
201
            if (!is_array($this->_callback['context'])) {
202
                $this->_callback['context'] = array();
203
            }
204
205
            if ($this->__canAccessAlerts()) {
206
                // Can the core be updated?
207
                $this->checkCoreForUpdates();
208
                // Do any extensions need updating?
209
                $this->checkExtensionsForUpdates();
210
            }
211
212
            $this->Page->build($this->_callback['context']);
213
        }
214
215
        return $this->Page;
216
    }
217
218
    /**
219
     * Scan the install directory to look for new migrations that can be applied
220
     * to update this version of Symphony. If one if found, a new Alert is added
221
     * to the page.
222
     *
223
     * @since Symphony 2.5.2
224
     * @return boolean
225
     *  Returns true if there is an update available, false otherwise.
226
     */
227
    public function checkCoreForUpdates()
228
    {
229
        // Is there even an install directory to check?
230
        if ($this->isInstallerAvailable() === false) {
231
            return false;
232
        }
233
234
        try {
235
            // The updater contains a version higher than the current Symphony version.
236
            if ($this->isUpgradeAvailable()) {
237
                $message = __('An update has been found in your installation to upgrade Symphony to %s.', array($this->getMigrationVersion())) . ' <a href="' . URL . '/install/">' . __('View update.') . '</a>';
238
239
                // The updater contains a version lower than the current Symphony version.
240
                // The updater is the same version as the current Symphony install.
241
            } else {
242
                $message = __('Your Symphony installation is up to date, but the installer was still detected. For security reasons, it should be removed.') . ' <a href="' . URL . '/install/?action=remove">' . __('Remove installer?') . '</a>';
243
            }
244
245
            // Can't detect update Symphony version
246
        } catch (Exception $e) {
247
            $message = __('An update script has been found in your installation.') . ' <a href="' . URL . '/install/">' . __('View update.') . '</a>';
248
        }
249
250
        $this->Page->pageAlert($message, Alert::NOTICE);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class HTMLPage as the method pageAlert() does only exist in the following sub-classes of HTMLPage: AdministrationPage, ResourcesPage, contentBlueprintsDatasources, contentBlueprintsEvents, contentBlueprintsPages, contentBlueprintsSections, contentPublish, contentSystemAuthors, contentSystemExtensions, contentSystemPreferences. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
251
252
        return true;
253
    }
254
255
    /**
256
     * Checks all installed extensions to see any have an outstanding update. If any do
257
     * an Alert will be added to the current page directing the Author to the Extension page
258
     *
259
     * @since Symphony 2.5.2
260
     */
261
    public function checkExtensionsForUpdates()
262
    {
263
        $extensions = Symphony::ExtensionManager()->listInstalledHandles();
264
265
        if (is_array($extensions) && !empty($extensions)) {
266
            foreach ($extensions as $name) {
267
                $about = Symphony::ExtensionManager()->about($name);
268
269
                if (array_key_exists('status', $about) && in_array(EXTENSION_REQUIRES_UPDATE, $about['status'])) {
270
                    $this->Page->pageAlert(
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class HTMLPage as the method pageAlert() does only exist in the following sub-classes of HTMLPage: AdministrationPage, ResourcesPage, contentBlueprintsDatasources, contentBlueprintsEvents, contentBlueprintsPages, contentBlueprintsSections, contentPublish, contentSystemAuthors, contentSystemExtensions, contentSystemPreferences. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
271
                        __('An extension requires updating.') . ' <a href="' . SYMPHONY_URL . '/system/extensions/">' . __('View extensions') . '</a>'
272
                    );
273
                    break;
274
                }
275
            }
276
        }
277
    }
278
279
    /**
280
     * This function determines whether an administrative alert can be
281
     * displayed on the current page. It ensures that the page exists,
282
     * and the user is logged in and a developer
283
     *
284
     * @since Symphony 2.2
285
     * @return boolean
286
     */
287
    private function __canAccessAlerts()
288
    {
289
        if ($this->Page instanceof AdministrationPage && self::isLoggedIn() && Symphony::Author()->isDeveloper()) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return $this->Page insta...uthor()->isDeveloper();.
Loading history...
290
            return true;
291
        }
292
293
        return false;
294
    }
295
296
    /**
297
     * This function resolves the string of the page to the relevant
298
     * backend page class. The path to the backend page is split on
299
     * the slashes and the resulting pieces used to determine if the page
300
     * is provided by an extension, is a section (index or entry creation)
301
     * or finally a standard Symphony content page. If no page driver can
302
     * be found, this function will return false.
303
     *
304
     * @uses AdminPagePostCallback
305
     * @param string $page
306
     *  The full path (including the domain) of the Symphony backend page
307
     * @return array|boolean
308
     *  If successful, this function will return an associative array that at the
309
     *  very least will return the page's classname, pageroot, driver, driver_location
310
     *  and context, otherwise this will return false.
311
     */
312
    public function getPageCallback($page = null)
313
    {
314
        if (!$page && $this->_callback) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $page of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug Best Practice introduced by
The expression $this->_callback of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
315
            return $this->_callback;
316
        } elseif (!$page && !$this->_callback) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $page of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug Best Practice introduced by
The expression $this->_callback of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
317
            trigger_error(__('Cannot request a page callback without first specifying the page.'));
318
        }
319
320
        $this->_currentPage = SYMPHONY_URL . preg_replace('/\/{2,}/', '/', $page);
321
        $bits = preg_split('/\//', trim($page, '/'), 3, PREG_SPLIT_NO_EMPTY);
322
        $callback = array(
323
            'driver' => null,
324
            'driver_location' => null,
325
            'context' => array(),
326
            'classname' => null,
327
            'pageroot' => null
328
        );
329
330
        // Login page, /symphony/login/
331
        if ($bits[0] === 'login') {
332
            $callback['driver'] = 'login';
333
            $callback['driver_location'] = CONTENT . '/content.login.php';
334
            $callback['classname'] = 'contentLogin';
335
            $callback['pageroot'] = '/login/';
336
337
        // Extension page, /symphony/extension/{extension_name}/
338
        } elseif ($bits[0] === 'extension' && isset($bits[1])) {
339
            $extension_name = $bits[1];
340
            $bits = preg_split('/\//', trim($bits[2], '/'), 2, PREG_SPLIT_NO_EMPTY);
341
342
            // check if extension is enabled, if it's not, pretend the extension doesn't
343
            // even exist. #2367
344
            if (!ExtensionManager::isInstalled($extension_name)) {
345
                return false;
346
            }
347
348
            $callback['driver'] = 'index';
349
            $callback['classname'] = 'contentExtension' . ucfirst($extension_name) . 'Index';
350
            $callback['pageroot'] = '/extension/' . $extension_name. '/';
351
            $callback['extension'] = $extension_name;
352
353
            if (isset($bits[0])) {
354
                $callback['driver'] = $bits[0];
355
                $callback['classname'] = 'contentExtension' . ucfirst($extension_name) . ucfirst($bits[0]);
356
                $callback['pageroot'] .= $bits[0] . '/';
357
            }
358
359 View Code Duplication
            if (isset($bits[1])) {
360
                $callback['context'] = preg_split('/\//', $bits[1], -1, PREG_SPLIT_NO_EMPTY);
361
            }
362
363
            $callback['driver_location'] = EXTENSIONS . '/' . $extension_name . '/content/content.' . $callback['driver'] . '.php';
364
            // Extensions won't be part of the autoloader chain, so first try to require them if they are available.
365
            if (!is_file($callback['driver_location'])) {
366
                return false;
367
            } else {
368
                require_once $callback['driver_location'];
369
            }
370
371
        // Publish page, /symphony/publish/{section_handle}/
372
        } elseif ($bits[0] === 'publish') {
373
            if (!isset($bits[1])) {
374
                return false;
375
            }
376
377
            $callback['driver'] = 'publish';
378
            $callback['driver_location'] = CONTENT . '/content.publish.php';
379
            $callback['pageroot'] = '/' . $bits[0] . '/' . $bits[1] . '/';
380
            $callback['classname'] = 'contentPublish';
381
382
        // Everything else
383
        } else {
384
            $callback['driver'] = ucfirst($bits[0]);
385
            $callback['pageroot'] = '/' . $bits[0] . '/';
386
387
            if (isset($bits[1])) {
388
                $callback['driver'] = $callback['driver'] . ucfirst($bits[1]);
389
                $callback['pageroot'] .= $bits[1] . '/';
390
            }
391
392
            $callback['classname'] = 'content' . $callback['driver'];
393
            $callback['driver'] = strtolower($callback['driver']);
394
            $callback['driver_location'] = CONTENT . '/content.' . $callback['driver'] . '.php';
395
        }
396
397
        // Parse the context
398
        if (isset($callback['classname'])) {
399
            $page = new $callback['classname'];
400
401
            // Named context
402
            if (method_exists($page, 'parseContext')) {
403
                $page->parseContext($callback['context'], $bits);
404
405
            // Default context
406 View Code Duplication
            } elseif (isset($bits[2])) {
407
                $callback['context'] = preg_split('/\//', $bits[2], -1, PREG_SPLIT_NO_EMPTY);
408
            }
409
        }
410
411
        /**
412
         * Immediately after determining which class will resolve the current page, this
413
         * delegate allows extension to modify the routing or provide additional information.
414
         *
415
         * @since Symphony 2.3.1
416
         * @delegate AdminPagePostCallback
417
         * @param string $context
418
         *  '/backend/'
419
         * @param string $page
420
         *  The current URL string, after the SYMPHONY_URL constant (which is `/symphony/`
421
         *  at the moment.
422
         * @param array $parts
423
         *  An array representation of `$page`
424
         * @param array $callback
425
         *  An associative array that contains `driver`, `pageroot`, `classname` and
426
         *  `context` keys. The `driver_location` is the path to the class to render this
427
         *  page, `driver` should be the view to render, the `classname` the name of the
428
         *  class, `pageroot` the rootpage, before any extra URL params and `context` can
429
         *  provide additional information about the page
430
         */
431
        Symphony::ExtensionManager()->notifyMembers('AdminPagePostCallback', '/backend/', array(
432
            'page' => $this->_currentPage,
433
            'parts' => $bits,
434
            'callback' => &$callback
435
        ));
436
437
        if (!isset($callback['driver_location']) || !is_file($callback['driver_location'])) {
438
            return false;
439
        }
440
441
        return $callback;
442
    }
443
444
    /**
445
     * Called by index.php, this function is responsible for rendering the current
446
     * page on the Frontend. Two delegates are fired, AdminPagePreGenerate and
447
     * AdminPagePostGenerate. This function runs the Profiler for the page build
448
     * process.
449
     *
450
     * @uses AdminPagePreBuild
451
     * @uses AdminPagePreGenerate
452
     * @uses AdminPagePostGenerate
453
     * @see core.Symphony#__buildPage()
454
     * @see boot.getCurrentPage()
455
     * @param string $page
456
     *  The result of getCurrentPage, which returns the $_GET['symphony-page']
457
     *  variable.
458
     * @throws Exception
459
     * @throws SymphonyErrorPage
460
     * @return string
461
     *  The HTML of the page to return
462
     */
463
    public function display($page)
464
    {
465
        Symphony::Profiler()->sample('Page build process started');
466
467
        /**
468
         * Immediately before building the admin page. Provided with the page parameter
469
         * @delegate AdminPagePreBuild
470
         * @since Symphony 2.6.0
471
         * @param string $context
472
         *  '/backend/'
473
         * @param string $page
474
         *  The result of getCurrentPage, which returns the $_GET['symphony-page']
475
         *  variable.
476
         */
477
        Symphony::ExtensionManager()->notifyMembers('AdminPagePreBuild', '/backend/', array('page' => $page));
478
479
        $this->__buildPage($page);
480
481
        // Add XSRF token to form's in the backend
482
        if (self::isXSRFEnabled() && isset($this->Page->Form)) {
483
            $this->Page->Form->prependChild(XSRF::formToken());
484
        }
485
486
        /**
487
         * Immediately before generating the admin page. Provided with the page object
488
         * @delegate AdminPagePreGenerate
489
         * @param string $context
490
         *  '/backend/'
491
         * @param HTMLPage $oPage
492
         *  An instance of the current page to be rendered, this will usually be a class that
493
         *  extends HTMLPage. The Symphony backend uses a convention of contentPageName
494
         *  as the class that extends the HTMLPage
495
         */
496
        Symphony::ExtensionManager()->notifyMembers('AdminPagePreGenerate', '/backend/', array('oPage' => &$this->Page));
497
498
        $output = $this->Page->generate();
499
500
        /**
501
         * Immediately after generating the admin page. Provided with string containing page source
502
         * @delegate AdminPagePostGenerate
503
         * @param string $context
504
         *  '/backend/'
505
         * @param string $output
506
         *  The resulting backend page HTML as a string, passed by reference
507
         */
508
        Symphony::ExtensionManager()->notifyMembers('AdminPagePostGenerate', '/backend/', array('output' => &$output));
509
510
        Symphony::Profiler()->sample('Page built');
511
512
        return $output;
513
    }
514
515
    /**
516
     * If a page is not found in the Symphony backend, this function should
517
     * be called which will raise a customError to display the default Symphony
518
     * page not found template
519
     */
520
    public function errorPageNotFound()
521
    {
522
        $this->throwCustomError(
523
            __('The page you requested does not exist.'),
524
            __('Page Not Found'),
525
            Page::HTTP_STATUS_NOT_FOUND
526
        );
527
    }
528
}
529