- <?php 
- /* 
-  * Copyright (C) 2017 Karmabunny Pty Ltd. 
-  * 
-  * This file is a part of SproutCMS. 
-  * 
-  * SproutCMS is free software: you can redistribute it and/or modify it under the terms 
-  * of the GNU General Public License as published by the Free Software Foundation, either 
-  * version 2 of the License, or (at your option) any later version. 
-  * 
-  * For more information, visit <http://getsproutcms.com>. 
-  */ 
-   
- namespace Sprout\Helpers; 
-   
- use Exception; 
- use InvalidArgumentException; 
-   
- use karmabunny\pdb\Exceptions\QueryException; 
- use karmabunny\pdb\Exceptions\RowMissingException; 
-   
-   
- /** 
-  * Methods for working with CMS pages 
-  */ 
- class Page 
- { 
-   
-     /** 
-     * Get the page id for a given URL 
-     **/ 
-     public static function lookupUrl($url) 
-     { 
-         $root = Navigation::getRootNode(); 
-         if (! $root) return null; 
-   
-         $matcher = new TreenodePathMatcher($url); 
-         $node = $root->findNode($matcher); 
-         if (! $node) return null; 
-   
-         return $node['id']; 
-     } 
-   
-   
-     /** 
-     * Return the URL for a page, when given the page id 
-     * The URLs are relative 
-     * 
-     * @param int $id The page id to return the URL for 
-     * @return string The friendly URL for the page 
-     **/ 
-     public static function url($id) 
-     { 
-         $id = (int) $id; 
-         if (! $id) return null; 
-   
-         $root = Navigation::getRootNode(); 
-   
-         $node = $root->findNodeValue('id', $id); 
-         if (! $node) { 
-             return 'page/view_by_id/' . $id; 
-         } 
-   
-         return $node->getFriendlyUrlNoprefix(); 
-     } 
-   
-   
-     /** 
-      * Fetches the URL for a tool page for a specific controller entrance method 
-      * @param string|Controller $class Class 
-      * @param string $method Method 
-      * @return string The URL to access the tool page 
-      * @throws Exception if there's no matching tool page in the {@see Navigation} tree 
-      */ 
-     public static function toolUrl($class, $method) 
-     { 
-         $matcher = new TreenodeFrontendMatcher($class, $method); 
-         $node = Navigation::getRootNode()->findNode($matcher); 
-         if (!$node) throw new Exception('Page for controller entrance not found'); 
-         return $node->getFriendlyUrl(); 
-     } 
-   
-   
-     /** 
-      * Set up metadata and social metadata for a tool page 
-      * 
-      * @return array $page Page record from database 
-      * @return null Current URL is not a matched node 
-      */ 
-     public static function setupToolPage() 
-     { 
-         $node = Navigation::getMatchedNode(); 
-         if (!$node) return null; 
-   
-         $page = Pdb::get('pages', $node['id']); 
-   
-         static::loadPageMeta($page); 
-         static::loadPageSocial($page, $node); 
-   
-         return $page; 
-     } 
-   
-   
-     /** 
-      * Load page metadata - description and keywords 
-      * 
-      * @param array $page Page record from database 
-      */ 
-     public-  static  function-  loadPageMeta (array $page)
 
-     { 
-         if (!empty($page['meta_description'])) { 
-             Needs::addMeta('description', $page['meta_description']); 
-         } 
-         if (!empty($page['meta_keywords'])) { 
-             Needs::addMeta('keywords', $page['meta_keywords']); 
-         } 
-     } 
-   
-   
-     /** 
-      * Load page social - title, image, description, url 
-      * 
-      * @param array $page Page record from database 
-      * @param Pagenode $node Node, for generating the URL; optional 
-      */ 
-     public-  static  function-  loadPageSocial (array $page,-  Pagenode  $node = null)
 
-     { 
-         SocialMeta::setTitle($page['name']); 
-   
-         if (!empty($page['gallery_thumb'])) { 
-             SocialMeta::setImage($page['gallery_thumb']); 
-         } else if (!empty($page['banner'])) { 
-             SocialMeta::setImage($page['banner']); 
-         } 
-   
-         if (!empty($page['meta_description'])) { 
-             SocialMeta::setDescription($page['meta_description']); 
-         } 
-   
-         if ($node !== null) { 
-             SocialMeta::setUrl($node->getFriendlyUrlNoPrefix()); 
-         } 
-     } 
-   
-   
-     /** 
-      * Inject page details -- title and browser title -- into a skin view 
-      * 
-      * @param View $skin Skin view to inject details into 
-      * @param array $page Page to pull details from 
-      */ 
-     public-  static  function-  injectPageSkin (- View  $skin, array $page)
 
-     { 
-         if (!empty($page['name'])) { 
-             $skin->page_title = $page['name']; 
-         } 
-         if (!empty($page['alt_browser_title'])) { 
-             $skin->browser_title = $page['alt_browser_title']; 
-         } 
-     } 
-   
-   
-     /** 
-      * Gets the embedded widgets (i.e. content blocks) for a page 
-      * @param int $rev_id Page revision ID from database (page_revisions.id) 
-      * @param string $include 'active' to only include active widgets, 
-      *        'all' to include disabled widgets as well 
-      * @return array Rows extracted from the database 
-      */ 
-     public static function getContentWidgets($rev_id, $include) 
-     { 
-         $rev_id = (int) $rev_id; 
-         if (!in_array($include, ['active', 'all'])) { 
-             throw new InvalidArgumentException("\$include must be 'active' or 'all'"); 
-         } 
-   
-         $active = ($include == 'active' ? 'AND active = 1' : ''); 
-   
-         $q = "SELECT id, type, settings, active, heading, template 
-             FROM ~page_widgets 
-             WHERE page_revision_id = ? AND area_id = 1 {$active} 
-             ORDER BY record_order"; 
-         return Pdb::q($q, [$rev_id], 'map-arr'); 
-     } 
-   
-   
-     /** 
-      * Get the page text for a page id, in HTML format, with widgets and everything, ready to go 
-      * 
-      * @param int $page_id Page ID from database 
-      * @param int $rev_id If specified, that revision will be used. 
-      *        Otherwise, the current live revision will be used. 
-      *        N.B. if specified, it must be a valid revision for the specified page 
-      * @param int $subsite_id Optional subsite ID - this is required for admins editing a sub-site 
-      *          that's different from the domain 
-      * @return string HTML 
-      * @throws QueryException 
-      **/ 
-     public-  static  function getText($page_id, $rev_id = 0, $subsite_id = null)
 
-     { 
-         $page_id = (int) $page_id; 
-         $rev_id = (int) $rev_id; 
-         $subsite_id = (int) $subsite_id; 
-   
-         if ($subsite_id < 1) $subsite_id = SubsiteSelector::$content_id; 
-   
-         $params = [ 
-             'id' => $page_id, 
-             'subsite_id' => $subsite_id, 
-         ]; 
-         if ($rev_id > 0) { 
-             $clause = "rev.id = :rev_id"; 
-             $params['rev_id'] = $rev_id; 
-         } else { 
-             $clause = "rev.status = :live"; 
-             $params['live'] = 'live'; 
-         } 
-         $q = "SELECT rev.id 
-             FROM ~pages AS page 
-             INNER JOIN ~page_revisions AS rev ON page.id = rev.page_id AND {$clause} 
-             WHERE page.id = :id AND page.subsite_id = :subsite_id 
-             LIMIT 1"; 
-         try { 
-             $rev_id = Pdb::query($q, $params, 'val'); 
-   
-         // No live revision means this is a new, blank page 
-         } catch (RowMissingException $ex) { 
-             return ''; 
-         } 
-   
-         $text = ''; 
-         $widgets = self::getContentWidgets($rev_id, 'active'); 
-         foreach ($widgets as $widget) { 
-             $inst = Widgets::instantiate($widget['type']); 
-             $inst->importSettings(json_decode($widget['settings'], true)); 
-             $inst->setTitle($widget['heading']); 
-             $widget_text = $inst->render(WidgetArea::ORIENTATION_WIDE); 
-             if (!$widget_text) continue; 
-   
-             // Prevent lack of whitespace between final and initial sentences of adjacent blocks 
-             if ($text) $text .= "\n"; 
-   
-             $text .= $widget_text; 
-         } 
-   
-         return ContentReplace::html($text); 
-     } 
-   
-   
-     /** 
-     * Returns an array of key-value pairs of all attributes for a page 
-     **/ 
-     public static function attrs($id) 
-     { 
-         $id = (int) $id; 
-   
-   
-         try { 
-             $q = "SELECT name, value FROM ~page_attributes WHERE page_id = ?"; 
-             $attrs = Pdb::q($q, [$id], 'map'); 
-         } catch (Exception $ex) {} 
-   
-         return $attrs; 
-     } 
-   
-   
-     /** 
-     * Returns an array pages which contain a given attribute. 
-     * 
-     * @param string $attr_name Attribute name to search for 
-     * @param string $attr_value If set, require the attribute to be this value 
-     * @return array Page IDs 
-     * @throws QueryException 
-     **/ 
-     public static function pagesWithAttr($attr_name, $attr_value = null) 
-     { 
-         $conditions['name'] = $attr_name; 
-         if ($attr_value) $conditions['value'] = $attr_value; 
-   
-         $where = Pdb::buildClause($conditions, $params); 
-   
-         $q = "SELECT page_id FROM ~page_attributes WHERE {$where} ORDER BY page_id"; 
-         return Pdb::query($q, $params, 'col'); 
-     } 
-   
-   
-     /** 
-      * Find pages with a given widget, and optionally the specified settings. 
-      * Only widgets on live revisions of active pages are checked. 
-      * @param string $class The class of the desired widget. 
-      * @param array $settings The settings to look for; all of the specified settings must match. 
-      * @return array Page IDs 
-      * @throws QueryException 
-      */ 
-     public-  static  function-  pagesWithWidget ($widget_name, array $settings = [])
 
-     { 
-         $q = "SELECT page.id, widget.settings 
-             FROM ~page_widgets AS widget 
-             INNER JOIN ~page_revisions AS rev ON widget.page_revision_id = rev.id AND rev.status = 'live' 
-             INNER JOIN ~pages AS page ON rev.page_id = page.id AND page.active = 1 
-             WHERE widget.type = ?"; 
-         if (count($settings) == 0) { 
-             return Pdb::query($q, [$widget_name], 'col'); 
-         } 
-   
-         $res = Pdb::query($q, [$widget_name], 'pdo'); 
-         $pages = []; 
-         foreach ($res as $row) { 
-             $widget_settings = json_decode($row['settings'], true); 
-                 $pages[] = $row['id']; 
-             } 
-         } 
-         $res->closeCursor(); 
-   
-         return $pages; 
-     } 
-   
-   
-     /** 
-     * Return the last-modified date of the specified page. 
-     * Returns NULL on error. 
-     * 
-     * If you don't provide a page-id, uses the id of the 
-     * Navigation::matchedNode() 
-     * 
-     * The date is formatted using the php date function. 
-     * The default date format is "d/m/Y". 
-     * 
-     * @param int $page_id The page to get the last-modified date of 
-     * @param string $date_format The date format to return the date in 
-     * @return string Date 
-     * @return null If page could not be found 
-     **/ 
-     public static function lastModified($page_id = null, $date_format = 'd/m/Y') 
-     { 
-         if ($page_id === null) { 
-             $node = Navigation::matchedNode(); 
-             if ($node === null) return null; 
-             $page_id = $node['id']; 
-         } 
-   
-         try { 
-             $q = "SELECT date_modified 
-                 FROM ~pages 
-                 WHERE id = ? 
-                 ORDER BY date_modified DESC 
-                 LIMIT 1"; 
-             $date = Pdb::query($q, [$page_id], 'val'); 
-   
-         } catch (QueryException $ex) { 
-             return null; 
-         } 
-     } 
-   
-   
-     /** 
-      * Makes a particular revision live, and changes the status of the previous live revision to 'old'. 
-      * Should be run inside a transaction. 
-      * @param $rev_id ID of revision to make live 
-      * @return void 
-      */ 
-     public static function activateRevision($rev_id) { 
-         $rev_id = (int) $rev_id; 
-   
-         $q = "SELECT page_id FROM ~page_revisions WHERE id = ?"; 
-         $page_id = Pdb::q($q, [$rev_id], 'val'); 
-   
-         $old_clause = ['page_id' => $page_id, 'status' => 'live']; 
-         Pdb::update('page_revisions', ['status' => 'old'], $old_clause); 
-   
-         Pdb::update('page_revisions', ['status' => 'live'], ['id' => $rev_id]); 
-     } 
-   
-   
-     /** 
-      * Return list of related links for given page 
-      * 
-      * @param int $page_id 
-      * @return array [href, text] pairs 
-      */ 
-     public static function determineRelatedLinks($page_id) 
-     { 
-         $list = []; 
-         $root_node = Navigation::getRootNode(); 
-         if ($root_node == null) return $list; 
-   
-   
-         $matcher = new TreenodeIdsMatcher([$page_id]); 
-         if ($matcher == null) return $list; 
-   
-         $page_node = $root_node->findNode($matcher); 
-         if ($page_node == null) return $list; 
-   
-         $ancestors = $page_node->findAncestors(); 
-         $top_anc = $ancestors[0]; 
-   
-         $top_anc->filterChildren(new TreenodeInMenuMatcher()); 
-   
-         if (count($top_anc->children) == 0) { 
-             $top_anc->removeFilter(); 
-             return $list; 
-         } 
-   
-         foreach ($top_anc->children as $page) { 
-             $list = array_merge($list, self::determineNodeLinks($page, 0)); 
-         } 
-   
-         $top_anc->removeFilter(); 
-   
-         return $list; 
-     } 
-   
-   
-     /** 
-      * Traverse page node to extract page links 
-      * 
-      * @param TreeNode $node The node to traverse 
-      * @param int $depth 
-      * @return array [href, text] pairs 
-      */ 
-     private static function determineNodeLinks($node, $depth) 
-     { 
-         $list = []; 
-         $new_depth = $depth + 1; 
-   
-         $list[] = ['href' => $node->getFriendlyUrl(), 'text' => $node->getNavigationName()]; 
-   
-         if ($new_depth <= 10-  and  count($node->children)) {
 
-             foreach ($node->children as $node) { 
-                 $list = array_merge($list, self::determineNodeLinks($node, $new_depth)); 
-             } 
-         } 
-   
-         return $list; 
-     } 
- } 
-