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.
Passed
Pull Request — master (#2835)
by
unknown
06:01
created

PageManager::fetchAllPagesPageTypes()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 10
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 17
rs 9.4285
1
<?php
2
3
/**
4
 * @package toolkit
5
 */
6
/**
7
 * The `PageManager` class is responsible for providing basic CRUD operations
8
 * for Symphony frontend pages. These pages are stored in the database in
9
 * `tbl_pages` and are resolved to an instance of `FrontendPage` class from a URL.
10
 * Additionally, this manager provides functions to access the Page's types,
11
 * and any linked datasources or events.
12
 *
13
 * @since Symphony 2.3
14
 */
15
class PageManager
16
{
17
    /**
18
     * Given an associative array of data, where the key is the column name
19
     * in `tbl_pages` and the value is the data, this function will create a new
20
     * Page and return a Page ID on success.
21
     *
22
     * @param array $fields
23
     *  Associative array of field names => values for the Page
24
     * @throws DatabaseException
25
     * @return integer|boolean
26
     *  Returns the Page ID of the created Page on success, false otherwise.
27
     */
28
    public static function add(array $fields)
29
    {
30
        if (!isset($fields['sortorder'])) {
31
            $fields['sortorder'] = self::fetchNextSortOrder();
32
        }
33
34
        $inserted = Symphony::Database()
0 ignored issues
show
Deprecated Code introduced by
The function Database::insert() has been deprecated: Symphony 3.0.0 If $table is an array, it is treated as the fields values Use DatabaseInsert::values() ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

34
        $inserted = /** @scrutinizer ignore-deprecated */ Symphony::Database()

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
35
            ->insert('tbl_pages')
36
            ->values($fields)
37
            ->execute()
38
            ->success();
39
40
        return $inserted ? Symphony::Database()->getInsertID() : 0;
41
    }
42
43
    /**
44
     * Return a Page title by the handle
45
     *
46
     * @param string $handle
47
     *  The handle of the page
48
     * @return integer
49
     *  The Page title
50
     */
51
    public static function fetchTitleFromHandle($handle)
52
    {
53
        return Symphony::Database()
54
            ->select(['title'])
55
            ->from('tbl_pages')
56
            ->where(['handle' => $handle])
57
            ->limit(1)
58
            ->execute()
59
            ->variable('title');
60
    }
61
62
    /**
63
     * Return a Page ID by the handle
64
     *
65
     * @param string $handle
66
     *  The handle of the page
67
     * @return integer
68
     *  The Page ID
69
     */
70
    public static function fetchIDFromHandle($handle)
71
    {
72
        return (int)Symphony::Database()
73
            ->select(['id'])
74
            ->from('tbl_pages')
75
            ->where(['handle' => $handle])
76
            ->limit(1)
77
            ->execute()
78
            ->variable('id');
79
    }
80
81
    /**
82
     * Given a Page ID and an array of types, this function will add Page types
83
     * to that Page. If a Page types are stored in `tbl_pages_types`.
84
     *
85
     * @param integer $page_id
86
     *  The Page ID to add the Types to
87
     * @param array $types
88
     *  An array of page types
89
     * @throws DatabaseException
90
     * @return boolean
91
     */
92
    public static function addPageTypesToPage($page_id = null, array $types)
93
    {
94
        if (!$page_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $page_id of type null|integer is loosely compared to false; this is ambiguous if the integer can be 0. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
95
            return false;
96
        }
97
98
        PageManager::deletePageTypes($page_id);
99
100
        foreach ($types as $type) {
101
            Symphony::Database()
0 ignored issues
show
Deprecated Code introduced by
The function Database::insert() has been deprecated: Symphony 3.0.0 If $table is an array, it is treated as the fields values Use DatabaseInsert::values() ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

101
            /** @scrutinizer ignore-deprecated */ Symphony::Database()

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
102
                ->insert('tbl_pages_types')
103
                ->values([
104
                    'page_id' => $page_id,
105
                    'type' => $type
106
                ])
107
                ->execute();
108
        }
109
110
        return true;
111
    }
112
113
    /**
114
     * Returns the path to the page-template by looking at the
115
     * `WORKSPACE/template/` directory, then at the `TEMPLATES`
116
     * directory for `$name.xsl`. If the template is not found,
117
     * false is returned
118
     *
119
     * @param string $name
120
     *  Name of the template
121
     * @return mixed
122
     *  String, which is the path to the template if the template is found,
123
     *  false otherwise
124
     */
125
    public static function getTemplate($name)
126
    {
127
        $format = '%s/%s.xsl';
128
129
        if (file_exists($template = sprintf($format, WORKSPACE . '/template', $name))) {
0 ignored issues
show
Bug introduced by
The constant WORKSPACE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
130
            return $template;
131
        } elseif (file_exists($template = sprintf($format, TEMPLATE, $name))) {
0 ignored issues
show
Bug introduced by
The constant TEMPLATE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
132
            return $template;
133
        } else {
134
            return false;
135
        }
136
    }
137
138
    /**
139
     * This function creates the initial `.xsl` template for the page, whether
140
     * that be from the `TEMPLATES/blueprints.page.xsl` file, or from an existing
141
     * template with the same name. This function will handle the renaming of a page
142
     * by creating the new files using the old files as the templates then removing
143
     * the old template. If a template already exists for a Page, it will not
144
     * be overridden and the function will return true.
145
     *
146
     * @see toolkit.PageManager#resolvePageFileLocation()
147
     * @see toolkit.PageManager#createHandle()
148
     * @param string $new_path
149
     *  The path of the Page, which is the handles of the Page parents. If the
150
     *  page has multiple parents, they will be separated by a forward slash.
151
     *  eg. article/read. If a page has no parents, this parameter should be null.
152
     * @param string $new_handle
153
     *  The new Page handle, generated using `PageManager::createHandle`.
154
     * @param string $old_path (optional)
155
     *  This parameter is only required when renaming a Page. It should be the 'old
156
     *  path' before the Page was renamed.
157
     * @param string $old_handle (optional)
158
     *  This parameter is only required when renaming a Page. It should be the 'old
159
     *  handle' before the Page was renamed.
160
     * @throws Exception
161
     * @return boolean
162
     *  true when the page files have been created successfully, false otherwise.
163
     */
164
    public static function createPageFiles($new_path, $new_handle, $old_path = null, $old_handle = null)
165
    {
166
        $new = PageManager::resolvePageFileLocation($new_path, $new_handle);
167
        $old = PageManager::resolvePageFileLocation($old_path, $old_handle);
168
169
        // Nothing to do:
170
        if (file_exists($new) && $new == $old) {
171
            return true;
172
        }
173
174
        // Old file doesn't exist, use template:
175
        if (!file_exists($old)) {
176
            $data = file_get_contents(self::getTemplate('blueprints.page'));
0 ignored issues
show
Bug introduced by
It seems like self::getTemplate('blueprints.page') can also be of type false; however, parameter $filename of file_get_contents() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

176
            $data = file_get_contents(/** @scrutinizer ignore-type */ self::getTemplate('blueprints.page'));
Loading history...
177
        } else {
178
            $data = file_get_contents($old);
179
        }
180
181
        /**
182
         * Just before a Page Template is about to be created & written to disk
183
         *
184
         * @delegate PageTemplatePreCreate
185
         * @since Symphony 2.2.2
186
         * @param string $context
187
         * '/blueprints/pages/'
188
         * @param string $file
189
         *  The path to the Page Template file
190
         * @param string $contents
191
         *  The contents of the `$data`, passed by reference
192
         */
193
        Symphony::ExtensionManager()->notifyMembers('PageTemplatePreCreate', '/blueprints/pages/', array('file' => $new, 'contents' => &$data));
194
195
        if (PageManager::writePageFiles($new, $data)) {
196
            // Remove the old file, in the case of a rename
197
            General::deleteFile($old);
198
199
            /**
200
             * Just after a Page Template is saved after been created.
201
             *
202
             * @delegate PageTemplatePostCreate
203
             * @since Symphony 2.2.2
204
             * @param string $context
205
             * '/blueprints/pages/'
206
             * @param string $file
207
             *  The path to the Page Template file
208
             */
209
            Symphony::ExtensionManager()->notifyMembers('PageTemplatePostCreate', '/blueprints/pages/', array('file' => $new));
210
211
            return true;
212
        }
213
214
        return false;
215
    }
216
217
    /**
218
     * A wrapper for `General::writeFile`, this function takes a `$path`
219
     * and a `$data` and writes the new template to disk.
220
     *
221
     * @param string $path
222
     *  The path to write the template to
223
     * @param string $data
224
     *  The contents of the template
225
     * @return boolean
226
     *  true when written successfully, false otherwise
227
     */
228
    public static function writePageFiles($path, $data)
229
    {
230
        return General::writeFile($path, $data, Symphony::Configuration()->get('write_mode', 'file'));
0 ignored issues
show
Bug introduced by
It seems like Symphony::Configuration(...t('write_mode', 'file') can also be of type array; however, parameter $perm of General::writeFile() does only seem to accept integer|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

230
        return General::writeFile($path, $data, /** @scrutinizer ignore-type */ Symphony::Configuration()->get('write_mode', 'file'));
Loading history...
231
    }
232
233
    /**
234
     * This function will update a Page in `tbl_pages` given a `$page_id`
235
     * and an associative array of `$fields`. A third parameter, `$delete_types`
236
     * will also delete the Page's associated Page Types if passed true.
237
     *
238
     * @see toolkit.PageManager#addPageTypesToPage()
239
     * @param integer $page_id
240
     *  The ID of the Page that should be updated
241
     * @param array $fields
242
     *  Associative array of field names => values for the Page.
243
     *  This array does need to contain every value for the Page, it
244
     *  can just be the changed values.
245
     * @param boolean $delete_types
246
     *  If true, this parameter will cause the Page Types of the Page to
247
     *  be deleted. By default this is false.
248
     * @return boolean
249
     */
250
    public static function edit($page_id, array $fields, $delete_types = false)
251
    {
252
        if (!is_numeric($page_id)) {
0 ignored issues
show
introduced by
The condition is_numeric($page_id) is always true.
Loading history...
253
            return false;
254
        }
255
256
        if (isset($fields['id'])) {
257
            unset($fields['id']);
258
        }
259
260
        if (Symphony::Database()
0 ignored issues
show
Deprecated Code introduced by
The function Database::update() has been deprecated: Symphony 3.0.0 This parameter is deprecated and will be removed. Use DatabaseUpdate::where() ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

260
        if (/** @scrutinizer ignore-deprecated */ Symphony::Database()

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
261
                ->update('tbl_pages')
262
                ->set($fields)
263
                ->where(['id' => General::intval($page_id)])
264
                ->execute()
265
                ->success()
266
        ) {
267
            // If set, this will clear the page's types.
268
            if ($delete_types) {
269
                PageManager::deletePageTypes($page_id);
270
            }
271
272
            return true;
273
        }
274
        return false;
275
    }
276
277
    /**
278
     * This function will update all children of a particular page (if any)
279
     * by renaming/moving all related files to their new path and updating
280
     * their database information. This is a recursive function and will work
281
     * to any depth.
282
     *
283
     * @param integer $page_id
284
     *  The ID of the Page whose children need to be updated
285
     * @param string $page_path
286
     *  The path of the Page, which is the handles of the Page parents. If the
287
     *  page has multiple parents, they will be separated by a forward slash.
288
     *  eg. article/read. If a page has no parents, this parameter should be null.
289
     * @throws Exception
290
     * @return boolean
291
     */
292
    public static function editPageChildren($page_id = null, $page_path = null)
293
    {
294
        if (!is_int($page_id)) {
295
            return false;
296
        }
297
298
        $page_path = trim($page_path, '/');
299
        $children = PageManager::fetchChildPages($page_id);
300
301
        foreach ($children as $child) {
302
            $child_id = (int)$child['id'];
303
            $fields = array(
304
                'path' => $page_path
305
            );
306
307
            if (!PageManager::createPageFiles($page_path, $child['handle'], $child['path'], $child['handle'])) {
308
                $success = false;
309
            }
310
311
            if (!PageManager::edit($child_id, $fields)) {
312
                $success = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $success is dead and can be removed.
Loading history...
313
            }
314
315
            $success = PageManager::editPageChildren($child_id, $page_path . '/' . $child['handle']);
316
        }
317
318
        return $success;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $success seems to be defined by a foreach iteration on line 301. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
319
    }
320
321
    /**
322
     * This function takes a Page ID and removes the Page from the database
323
     * in `tbl_pages` and it's associated Page Types in `tbl_pages_types`.
324
     * This function does not delete any of the Page's children.
325
     *
326
     * @see toolkit.PageManager#deletePageTypes
327
     * @see toolkit.PageManager#deletePageFiles
328
     * @param integer $page_id
329
     *  The ID of the Page that should be deleted.
330
     * @param boolean $delete_files
331
     *  If true, this parameter will remove the Page's templates from the
332
     *  the filesystem. By default this is true.
333
     * @throws DatabaseException
334
     * @throws Exception
335
     * @return boolean
336
     */
337
    public static function delete($page_id = null, $delete_files = true)
338
    {
339
        if (!is_int($page_id)) {
340
            return false;
341
        }
342
343
        $can_proceed = true;
344
345
        // Delete Files (if told to)
346
        if ($delete_files) {
347
            $page = PageManager::fetchPageByID($page_id, array('path', 'handle'));
348
349
            if (empty($page)) {
350
                return false;
351
            }
352
353
            $can_proceed = PageManager::deletePageFiles($page['path'], $page['handle']);
354
        }
355
356
        // Delete from tbl_pages/tbl_page_types
357
        if ($can_proceed) {
358
            PageManager::deletePageTypes($page_id);
359
            Symphony::Database()
0 ignored issues
show
Deprecated Code introduced by
The function Database::delete() has been deprecated: Symphony 3.0.0 This parameter is deprecated and will be removed. Use DatabaseDelete::where() ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

359
            /** @scrutinizer ignore-deprecated */ Symphony::Database()

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
360
                ->delete('tbl_pages')
361
                ->where(['id' => General::intval($page_id)])
362
                ->execute();
363
            Symphony::Database()
0 ignored issues
show
Deprecated Code introduced by
The function Database::update() has been deprecated: Symphony 3.0.0 This parameter is deprecated and will be removed. Use DatabaseUpdate::where() ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

363
            /** @scrutinizer ignore-deprecated */ Symphony::Database()

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
364
                ->update('tbl_pages')
365
                ->set(['sortorder' => '$sortorder - 1'])
366
                ->where(['sortorder' => ['>' => $page['sortorder']]])
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $page does not seem to be defined for all execution paths leading up to this point.
Loading history...
367
                ->execute();
368
        }
369
370
        return $can_proceed;
371
    }
372
373
    /**
374
     * Given a `$page_id`, this function will remove all associated
375
     * Page Types from `tbl_pages_types`.
376
     *
377
     * @param integer $page_id
378
     *  The ID of the Page that should be deleted.
379
     * @throws DatabaseException
380
     * @return boolean
381
     */
382
    public static function deletePageTypes($page_id = null)
383
    {
384
        if (!$page_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $page_id of type null|integer is loosely compared to false; this is ambiguous if the integer can be 0. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
385
            return false;
386
        }
387
388
        return Symphony::Database()
0 ignored issues
show
Deprecated Code introduced by
The function Database::delete() has been deprecated: Symphony 3.0.0 This parameter is deprecated and will be removed. Use DatabaseDelete::where() ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

388
        return /** @scrutinizer ignore-deprecated */ Symphony::Database()

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
389
            ->delete('tbl_pages_types')
390
            ->where(['page_id' => General::intval($page_id)])
391
            ->execute()
392
            ->success();
393
    }
394
395
    /**
396
     * Given a Page's `$path` and `$handle`, this function will remove
397
     * it's templates from the `PAGES` directory returning boolean on
398
     * completion
399
     *
400
     * @param string $page_path
401
     *  The path of the Page, which is the handles of the Page parents. If the
402
     *  page has multiple parents, they will be separated by a forward slash.
403
     *  eg. article/read. If a page has no parents, this parameter should be null.
404
     * @param string $handle
405
     *  A Page handle, generated using `PageManager::createHandle`.
406
     * @throws Exception
407
     * @return boolean
408
     */
409
    public static function deletePageFiles($page_path, $handle)
410
    {
411
        $file = PageManager::resolvePageFileLocation($page_path, $handle);
412
413
        // Nothing to do:
414
        if (!file_exists($file)) {
415
            return true;
416
        }
417
418
        // Delete it:
419
        if (General::deleteFile($file)) {
420
            return true;
421
        }
422
423
        return false;
424
    }
425
426
    /**
427
     * This function will return an associative array of Page information. The
428
     * information returned is defined by the `$include_types` and `$select`
429
     * parameters, which will return the Page Types for the Page and allow
430
     * a developer to restrict what information is returned about the Page.
431
     * Optionally, `$where` and `$order_by` parameters allow a developer to
432
     * further refine their query.
433
     *
434
     * @deprecated Symphony 3.0.0
435
     *  Use select() instead
436
     * @param boolean $include_types
437
     *  Whether to include the resulting Page's Page Types in the return array,
438
     *  under the key `type`. Defaults to true.
439
     * @param array $select (optional)
440
     *  Accepts an array of columns to return from `tbl_pages`. If omitted,
441
     *  all columns from the table will be returned.
442
     * @param array $where (optional)
443
     *  Accepts an array of WHERE statements that will be appended with AND.
444
     *  If omitted, all pages will be returned.
445
     * @param string $order_by (optional)
446
     *  Allows a developer to return the Pages in a particular order. The string
447
     *  passed will be appended to `ORDER BY`. If omitted this will return
448
     *  Pages ordered by `sortorder`.
449
     * @param boolean $hierarchical (optional)
450
     *  If true, builds a multidimensional array representing the pages hierarchy.
451
     *  Defaults to false.
452
     * @return array|null
453
     *  An associative array of Page information with the key being the column
454
     *  name from `tbl_pages` and the value being the data. If requested, the array
455
     *  can be made multidimensional to reflect the pages hierarchy. If no Pages are
456
     *  found, null is returned.
457
     */
458
    public static function fetch($include_types = true, array $select = array(), array $where = array(), $order_by = null, $hierarchical = false)
459
    {
460
        if (Symphony::Log()) {
461
            Symphony::Log()->pushDeprecateWarningToLog('PageManager::fetch()', 'PageManager::select()');
462
        }
463
464
        if (empty($select)) {
465
            $select = ['*'];
466
        }
467
468
        if (!$order_by) {
469
            $order_by = ['sortorder' => 'ASC'];
470
        }
471
472
        $query = (new PageManager)->select($select);
473
474
        if ($hierarchical && !in_array('*', $select)) {
475
            $query->projection(['id', 'parent']);
476
        }
477
        if (is_array($where)) {
0 ignored issues
show
introduced by
The condition is_array($where) is always true.
Loading history...
478
            foreach ($where as $w) {
479
                $where = $query->replaceTablePrefix($w);
0 ignored issues
show
Unused Code introduced by
The assignment to $where is dead and can be removed.
Loading history...
480
                $op = $query->containsSQLParts('where') ? 'AND' : 'WHERE';
481
                $query->unsafe()->unsafeAppendSQLPart('where', "$op ($w)");
482
            }
483
        }
484
        if (is_array($order_by)) {
485
            $query->orderBy($order_by);
486
        } elseif (is_string($order_by)) {
0 ignored issues
show
introduced by
The condition is_string($order_by) is always true.
Loading history...
487
            $order_by = $query->replaceTablePrefix($order_by);
488
            $query->unsafe()->unsafeAppendSQLPart('order by', "ORDER BY $order_by");
489
        }
490
491
        // Fetch the Page Types for each page, if required
492
        if ($include_types) {
493
            $query->includeTypes();
494
        }
495
496
        $pages = $query->execute();
497
498
        if ($hierarchical) {
499
            return $pages->tree();
500
        }
501
502
        return $pages->rows();
503
    }
504
505
    /**
506
     * Returns Pages that match the given `$page_id`. Developers can optionally
507
     * choose to specify what Page information is returned using the `$select`
508
     * parameter.
509
     *
510
     * @param integer|array $page_id
511
     *  The ID of the Page, or an array of ID's
512
     * @param array $select (optional)
513
     *  Accepts an array of columns to return from `tbl_pages`. If omitted,
514
     *  all columns from the table will be returned.
515
     * @return array|null
516
     *  An associative array of Page information with the key being the column
517
     *  name from `tbl_pages` and the value being the data. If multiple Pages
518
     *  are found, an array of Pages will be returned. If no Pages are found
519
     *  null is returned.
520
     */
521
    public static function fetchPageByID($page_id = null, array $select = array())
522
    {
523
        if (!$page_id) {
524
            return null;
525
        }
526
527
        if (!is_array($page_id)) {
528
            $page_id = [$page_id];
529
        }
530
531
        if (empty($select)) {
532
            $select = ['*'];
533
        }
534
535
        $page = (new PageManager)
536
            ->select($select)
537
            ->includeTypes()
538
            ->pages($page_id)
539
            ->execute()
540
            ->rows();
541
542
        return count($page) == 1 ? current($page) : $page;
543
    }
544
545
    /**
546
     * Returns the first Page that match the given `$type`.
547
     *
548
     * @since Symphony 3.0.0
549
     *  It returns only the first page of the specified type.
550
     * @param string $type
551
     *  Where the type is one of the available Page Types.
552
     * @return array|null
553
     *  An associative array of Page information with the key being the column
554
     *  name from `tbl_pages` and the value being the data. If multiple Pages
555
     *  are found, an array of Pages will be returned. If no Pages are found
556
     *  null is returned.
557
     */
558
    public static function fetchPageByType($type)
559
    {
560
        General::ensureType([
561
            'type' => ['var' => $type, 'type' => 'string'],
562
        ]);
563
        $pageQuery = (new PageManager)
564
            ->select()
565
            ->innerJoin('tbl_pages_types')
566
            ->alias('pt')
567
            ->on(['p.id' => '$pt.page_id'])
568
            ->where(['pt.type' => $type])
569
            ->limit(1);
570
        return $pageQuery->execute()->next();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $pageQuery->execute()->next() also could return the type object which is incompatible with the documented return type null|array.
Loading history...
571
    }
572
573
    /**
574
     * Returns the child Pages (if any) of the given `$page_id`.
575
     *
576
     * @param integer $page_id
577
     *  The ID of the Page.
578
     * @param array $select (optional)
579
     *  Accepts an array of columns to return from `tbl_pages`. If omitted,
580
     *  all columns from the table will be returned.
581
     * @return array|null
582
     *  An associative array of Page information with the key being the column
583
     *  name from `tbl_pages` and the value being the data. If multiple Pages
584
     *  are found, an array of Pages will be returned. If no Pages are found
585
     *  null is returned.
586
     */
587
    public static function fetchChildPages($page_id = null, array $select = array())
588
    {
589
        if (!$page_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $page_id of type null|integer is loosely compared to false; this is ambiguous if the integer can be 0. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
590
            return null;
591
        }
592
593
        if (empty($select)) {
594
            $select = ['*'];
595
        }
596
597
        return (new PageManager)
598
            ->select($select)
599
            ->where(['id' => ['!=' => $page_id]])
600
            ->where(['parent' => $page_id])
601
            ->execute()
602
            ->rows();
603
    }
604
605
    /**
606
     * This function returns a Page's Page Types. If the `$page_id`
607
     * parameter is given, the types returned will be for that Page.
608
     *
609
     * @param integer $page_id
610
     *  The ID of the Page.
611
     * @return array
612
     *  An array of the Page Types
613
     */
614
    public static function fetchPageTypes($page_id = null)
615
    {
616
        $sql = Symphony::Database()
617
            ->select(['pt.type'])
618
            ->from('tbl_pages_types')
619
            ->alias('pt')
620
            ->groupBy(['pt.type'])
621
            ->orderBy(['pt.type' => 'ASC']);
622
623
        if ($page_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $page_id of type null|integer is loosely compared to true; this is ambiguous if the integer can be 0. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
624
            $sql->where(['pt.page_id' => $page_id]);
625
        }
626
627
        return $sql->execute()->column('type');
628
    }
629
630
    /**
631
     * Returns all the page types that exist in this Symphony install.
632
     * There are 6 default system page types, and new types can be added
633
     * by Developers via the Page Editor.
634
     *
635
     * @since Symphony 2.3 introduced the JSON type.
636
     * @return array
637
     *  An array of strings of the page types used in this Symphony
638
     *  install. At the minimum, this will be an array with the values
639
     * 'index', 'XML', 'JSON', 'admin', '404' and '403'.
640
     */
641
    public static function fetchAvailablePageTypes()
642
    {
643
        $system_types = array('index', 'XML', 'JSON', 'admin', '404', '403');
644
645
        $types = PageManager::fetchPageTypes();
646
647
        return (!empty($types) ? General::array_remove_duplicates(array_merge($system_types, $types)) : $system_types);
648
    }
649
650
    /**
651
     * Work out the next available sort order for a new page
652
     *
653
     * @return integer
654
     *  Returns the next sort order
655
     */
656
    public static function fetchNextSortOrder()
657
    {
658
        $next = (int)Symphony::Database()
659
            ->select(['MAX(sortorder)'])
660
            ->from('tbl_pages')
661
            ->execute()
662
            ->variable(0);
663
664
        return $next + 1;
665
    }
666
667
    /**
668
     * Fetch an associated array with Page ID's and the types they're using.
669
     *
670
     * @throws DatabaseException
671
     * @return array
672
     *  A 2-dimensional associated array where the key is the page ID.
673
     */
674
    public static function fetchAllPagesPageTypes()
675
    {
676
        $types = Symphony::Database()
677
            ->select(['page_id', 'type'])
678
            ->from('tbl_pages_types')
679
            ->execute()
680
            ->rows();
681
682
        $page_types = [];
683
684
        if (is_array($types)) {
0 ignored issues
show
introduced by
The condition is_array($types) is always true.
Loading history...
685
            foreach ($types as $type) {
686
                $page_types[$type['page_id']][] = $type['type'];
687
            }
688
        }
689
690
        return $page_types;
691
    }
692
693
    /**
694
     * Given a name, this function will return a page handle. These handles
695
     * will only contain latin characters
696
     *
697
     * @param string $name
698
     *  The Page name to generate a handle for
699
     * @return string
700
     */
701
    public static function createHandle($name)
702
    {
703
        return Lang::createHandle($name, 255, '-', false, true, array(
704
            '@^[^a-z\d]+@i' => '',
705
            '/[^\w-\.]/i' => ''
706
        ));
707
    }
708
709
    /**
710
     * This function takes a `$path` and `$handle` and generates a flattened
711
     * string for use as a filename for a Page's template.
712
     *
713
     * @param string $path
714
     *  The path of the Page, which is the handles of the Page parents. If the
715
     *  page has multiple parents, they will be separated by a forward slash.
716
     *  eg. article/read. If a page has no parents, this parameter should be null.
717
     * @param string $handle
718
     *  A Page handle, generated using `PageManager::createHandle`.
719
     * @return string
720
     */
721
    public static function createFilePath($path, $handle)
722
    {
723
        return trim(str_replace('/', '_', $path . '_' . $handle), '_');
724
    }
725
726
    /**
727
     * This function will return the number of child pages for a given
728
     * `$page_id`. This is a recursive function and will return the absolute
729
     * count.
730
     *
731
     * @param integer $page_id
732
     *  The ID of the Page.
733
     * @return integer
734
     *  The number of child pages for the given `$page_id`
735
     */
736
    public static function getChildPagesCount($page_id = null)
737
    {
738
        if (is_null($page_id)) {
739
            return null;
740
        }
741
742
        $children = (new PageManager)
743
            ->select()
744
            ->where(['parent' => $page_id])
745
            ->execute()
746
            ->rows();
747
748
        $count = count($children);
749
750
        if ($count > 0) {
751
            foreach ($children as $c) {
752
                $count += self::getChildPagesCount($c['id']);
753
            }
754
        }
755
756
        return $count;
757
    }
758
759
    /**
760
     * Returns boolean if a the given `$type` has been used by Symphony
761
     * for a Page that is not `$page_id`.
762
     *
763
     * @param integer $page_id
764
     *  The ID of the Page to exclude from the query.
765
     * @param string $type
766
     *  The Page Type to look for in `tbl_page_types`.
767
     * @return boolean
768
     *  true if the type is used, false otherwise
769
     */
770
    public static function hasPageTypeBeenUsed($page_id = null, $type)
771
    {
772
        return count(Symphony::Database()
773
            ->select(['pt.id'])
774
            ->from('tbl_pages_types', 'pt')
775
            ->where(['pt.page_id' => ['!=' => $page_id]])
776
            ->where(['pt.type' => $type])
777
            ->limit(1)
778
            ->execute()
779
            ->rows()) === 1;
780
    }
781
782
    /**
783
     * Given a `$page_id`, this function returns boolean if the page
784
     * has child pages.
785
     *
786
     * @param integer $page_id
787
     *  The ID of the Page to check
788
     * @return boolean
789
     *  true if the page has children, false otherwise
790
     */
791
    public static function hasChildPages($page_id)
792
    {
793
        return count(Symphony::Database()
794
            ->select(['p.id'])
795
            ->from('tbl_pages', 'p')
796
            ->where(['p.parent' => $page_id])
797
            ->limit(1)
798
            ->execute()
799
            ->rows()) === 1;
800
    }
801
802
    /**
803
     * Resolves the path to this page's XSLT file. The Symphony convention
804
     * is that they are stored in the `PAGES` folder. If this page has a parent
805
     * it will be as if all the / in the URL have been replaced with _. ie.
806
     * /articles/read/ will produce a file `articles_read.xsl`
807
     *
808
     * @see toolkit.PageManager#createFilePath()
809
     * @param string $path
810
     *  The URL path to this page, excluding the current page. ie, /articles/read
811
     *  would make `$path` become articles/
812
     * @param string $handle
813
     *  The handle of the page.
814
     * @return string
815
     *  The path to the XSLT of the page
816
     */
817
    public static function resolvePageFileLocation($path, $handle)
818
    {
819
        return PAGES . '/' . PageManager::createFilePath($path, $handle) . '.xsl';
0 ignored issues
show
Bug introduced by
The constant PAGES was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
820
    }
821
822
    /**
823
     * Given the `$page_id` and a `$column`, this function will return an
824
     * array of the given `$column` for the Page, including all parents.
825
     *
826
     * @param mixed $page_id
827
     *  The ID of the Page that currently being viewed, or the handle of the
828
     *  current Page
829
     * @param string $column
830
     *  The column to return
831
     * @return array
832
     *  An array of the current Page, containing the `$column`
833
     *  requested. The current page will be the last item the array, as all
834
     *  parent pages are prepended to the start of the array
835
     */
836
    public static function resolvePage($page_id, $column)
837
    {
838
        $query = (new PageManager)
839
            ->select(['p.parent', "p.$column"])
840
            ->limit(1);
841
842
        if (General::intval($page_id) > 0) {
843
            $query->page($page_id);
844
        } else {
845
            $query->handle($page_id);
846
        }
847
848
        $page = $query->execute()->next();
849
850
        if (empty($page)) {
851
            return $page;
852
        }
853
854
        $path = array($page[$column]);
855
856
        if (!empty($page['parent'])) {
857
            $next_parent = $page['parent'];
858
859
            while ($next_parent &&
860
                $parent = (new PageManager)
861
                    ->select(['p.parent', "p.$column"])
862
                    ->page($next_parent)
863
                    ->limit(1)
864
                    ->execute()
865
                    ->next()
866
            ) {
867
                array_unshift($path, $parent[$column]);
868
                $next_parent = $parent['parent'];
869
            }
870
        }
871
872
        return $path;
873
    }
874
875
    /**
876
     * Given the `$page_id`, return the complete title of the
877
     * current page. Each part of the Page's title will be
878
     * separated by ': '.
879
     *
880
     * @param mixed $page_id
881
     *  The ID of the Page that currently being viewed, or the handle of the
882
     *  current Page
883
     * @return string
884
     *  The title of the current Page. If the page is a child of another
885
     *  it will be prepended by the parent and a colon, ie. Articles: Read
886
     */
887
    public static function resolvePageTitle($page_id)
888
    {
889
        $path = PageManager::resolvePage($page_id, 'title');
890
891
        return implode(': ', $path);
892
    }
893
894
    /**
895
     * Given the `$page_id`, return the complete path to the
896
     * current page. Each part of the Page's path will be
897
     * separated by '/'.
898
     *
899
     * @param mixed $page_id
900
     *  The ID of the Page that currently being viewed, or the handle of the
901
     *  current Page
902
     * @return string
903
     *  The complete path to the current Page including any parent
904
     *  Pages, ie. /articles/read
905
     */
906
    public static function resolvePagePath($page_id)
907
    {
908
        $path = PageManager::resolvePage($page_id, 'handle');
909
910
        return implode('/', $path);
911
    }
912
913
    /**
914
     * Resolve a page by it's handle and path
915
     *
916
     * @param string $handle
917
     *  The handle of the page
918
     * @param boolean $path
919
     *  The path to the page
920
     * @return array
921
     *  array if found, null if not
922
     */
923
    public static function resolvePageByPath($handle, $path = false)
924
    {
925
        return (new PageManager)
926
            ->select()
927
            ->handle($handle)
0 ignored issues
show
Bug introduced by
$handle of type string is incompatible with the type integer expected by parameter $handle of PageQuery::handle(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

927
            ->handle(/** @scrutinizer ignore-type */ $handle)
Loading history...
928
            ->path(!$path ? null : $path)
0 ignored issues
show
Bug introduced by
It seems like ! $path ? null : $path can also be of type true; however, parameter $path of PageQuery::path() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

928
            ->path(/** @scrutinizer ignore-type */ !$path ? null : $path)
Loading history...
929
            ->limit(1)
930
            ->execute()
931
            ->next();
932
    }
933
934
    /**
935
     * Check whether a data source is used or not
936
     *
937
     * @param string $handle
938
     *  The data source handle
939
     * @return boolean
940
     *  true if used, false if not
941
     */
942
    public static function isDataSourceUsed($handle)
943
    {
944
        return (new PageManager)
945
            ->select()
946
            ->count()
947
            ->where(['p.data_sources' => ['regexp' => "[[:<:]]{$handle}[[:>:]]"]])
948
            ->variable(0) > 0;
0 ignored issues
show
Bug introduced by
The method variable() does not exist on PageQuery. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

948
            ->/** @scrutinizer ignore-call */ variable(0) > 0;

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
949
    }
950
951
    /**
952
     * Check whether a event is used or not
953
     *
954
     * @param string $handle
955
     *  The event handle
956
     * @return boolean
957
     *  true if used, false if not
958
     */
959
    public static function isEventUsed($handle)
960
    {
961
        return (new PageManager)
962
            ->select()
963
            ->count()
964
            ->where(['p.events' => ['regexp' => "[[:<:]]{$handle}[[:>:]]"]])
965
            ->variable(0) > 0;
966
    }
967
968
    /**
969
     * Factory method that creates a new PageQuery.
970
     *
971
     * @since Symphony 3.0.0
972
     * @param array $projection
973
     *  The projection to select.
974
     *  If no projection gets added, it defaults to `PageQuery::getDefaultProjection()`.
975
     * @return PageQuery
976
     */
977
    public function select(array $projection = [])
978
    {
979
        return new PageQuery(Symphony::Database(), $projection);
980
    }
981
}
982