Autodoc
  • Namespace
  • Class
  • Tree

Namespaces

  • BlueTihi
    • Context
  • Brickrouge
    • Element
      • Nodes
    • Renderer
    • Widget
  • ICanBoogie
    • ActiveRecord
    • AutoConfig
    • CLDR
    • Composer
    • Core
    • Event
    • Exception
    • HTTP
      • Dispatcher
      • Request
    • I18n
      • Translator
    • Mailer
    • Modules
      • Taxonomy
        • Support
      • Thumbnailer
        • Versions
    • Object
    • Operation
      • Dispatcher
    • Prototype
    • Routes
    • Routing
      • Dispatcher
    • Session
  • Icybee
    • ActiveRecord
      • Model
    • ConfigOperation
    • Document
    • EditBlock
    • Element
      • ActionbarContextual
      • ActionbarSearch
      • ActionbarToolbar
    • FormBlock
    • Installer
    • ManageBlock
    • Modules
      • Articles
      • Cache
        • Collection
        • ManageBlock
      • Comments
        • ManageBlock
      • Contents
        • ManageBlock
      • Dashboard
      • Editor
        • Collection
      • Files
        • File
        • ManageBlock
      • Forms
        • Form
        • ManageBlock
      • I18n
      • Images
        • ManageBlock
      • Members
      • Modules
        • ManageBlock
      • Nodes
        • ManageBlock
        • Module
      • Pages
        • BreadcrumbElement
        • LanguagesElement
        • ManageBlock
        • NavigationBranchElement
        • NavigationElement
        • Page
        • PageController
      • Registry
      • Search
      • Seo
      • Sites
        • ManageBlock
      • Taxonomy
        • Terms
          • ManageBlock
        • Vocabulary
          • ManageBlock
      • Users
        • ManageBlock
        • NonceLogin
        • Roles
      • Views
        • ActiveRecordProvider
        • Collection
        • View
    • Operation
      • ActiveRecord
      • Constructor
      • Module
      • Widget
    • Rendering
  • None
  • Patron
  • PHP

Classes

  • Blueprint
  • BlueprintNode
  • BreadcrumbElement
  • Content
  • ContentModel
  • CopyOperation
  • DeleteOperation
  • EditBlock
  • ExportBlock
  • ExportOperation
  • Hooks
  • ImportOperation
  • LanguagesElement
  • ListView
  • ManageBlock
  • Model
  • Module
  • NavigationBranchElement
  • NavigationElement
  • NavigationExcludeOperation
  • NavigationIncludeOperation
  • Page
  • PageController
  • PopPage
  • PopTemplate
  • QueryOperationOperation
  • SaveOperation
  • TemplateEditorsOperation
  • UpdateTreeOperation
  1 <?php
  2 
  3 /*
  4  * This file is part of the Icybee package.
  5  *
  6  * (c) Olivier Laviale <olivier.laviale@gmail.com>
  7  *
  8  * For the full copyright and license information, please view the LICENSE
  9  * file that was distributed with this source code.
 10  */
 11 
 12 namespace Icybee\Modules\Pages;
 13 
 14 use ICanBoogie\ActiveRecord\Query;
 15 use ICanBoogie\Exception;
 16 use ICanBoogie\Routing\Pattern;
 17 
 18 class Model extends \Icybee\Modules\Nodes\Model
 19 {
 20     /**
 21      * Before saving the record, we make sure that it is not its own parent.
 22      */
 23     public function save(array $properties, $key=null, array $options=array())
 24     {
 25         if ($key && isset($properties[Page::PARENTID]) && $key == $properties[Page::PARENTID])
 26         {
 27             throw new Exception('A page connot be its own parent.');
 28         }
 29 
 30         if (empty($properties[Page::SITEID]))
 31         {
 32             throw new Exception('site_id is empty.');
 33         }
 34 
 35         unset(self::$blueprint_cache[$properties[Page::SITEID]]);
 36 
 37         return parent::save($properties, $key, $options);
 38     }
 39 
 40     /**
 41      * Before deleting the record, we make sure that it is not used as a parent page or as a
 42      * location target.
 43      */
 44     public function delete($key)
 45     {
 46         $site_id = $this->select('siteid')->filter_by_nid($key)->rc;
 47 
 48         if ($site_id)
 49         {
 50             unset(self::$blueprint_cache[$site_id]);
 51         }
 52 
 53         return parent::delete($key);
 54     }
 55 
 56     /**
 57      * Changes the order of the query with "weight, create".
 58      *
 59      * @param Query $query
 60      *
 61      * @return Query
 62      */
 63     protected function scope_ordered(Query $query, $direction=1)
 64     {
 65         $direction = $direction < 0 ? 'DESC' : 'ASC';
 66 
 67         return $query->order("weight {$direction}, created_at {$direction}");
 68     }
 69 
 70     /**
 71      * Returns the blueprint of the pages tree.
 72      *
 73      * @param int $site_id Identifier of the website.
 74      *
 75      * @return array[int]object
 76      */
 77     public function blueprint($site_id)
 78     {
 79         if (isset(self::$blueprint_cache[$site_id]))
 80         {
 81             return self::$blueprint_cache[$site_id];
 82         }
 83 
 84         $query = $this
 85         ->select('nid, parentid, is_online, is_navigation_excluded, pattern')
 86         ->filter_by_siteid($site_id)->ordered;
 87 
 88         return self::$blueprint_cache[$site_id] = Blueprint::from($query);
 89     }
 90 
 91     /**
 92      * Holds the cached blueprint for each website.
 93      *
 94      * @var array
 95      */
 96     private static $blueprint_cache = array();
 97 
 98     /**
 99      * Returns the home page of the specified site.
100      *
101      * The record cache is used to retrieve or store the home page. Additionnaly the home page
102      * found is stored for each site.
103      *
104      * @param int $siteid Identifier of the site.
105      *
106      * @return Page
107      */
108     public function find_home($siteid)
109     {
110         if (isset(self::$home_by_siteid[$siteid]))
111         {
112             return self::$home_by_siteid[$siteid];
113         }
114 
115         $home = $this->where('siteid = ? AND parentid = 0 AND is_online = 1', $siteid)->ordered->one;
116 
117         if ($home)
118         {
119             $stored = $this->retrieve($home->nid);
120 
121             if ($stored)
122             {
123                 $home = $stored;
124             }
125             else
126             {
127                 $this->store($home);
128             }
129         }
130 
131         self::$home_by_siteid[$siteid] = $home;
132 
133         return $home;
134     }
135 
136     private static $home_by_siteid = array();
137 
138     /**
139      * Finds a page using its path.
140      *
141      * @param string $path
142      *
143      * @return Page
144      */
145     public function find_by_path($path) // TODO-20120922: use a BluePrint object
146     {
147         global $core;
148 
149         $pos = strrpos($path, '.');
150         $extension = null;
151 
152         if ($pos && $pos > strrpos($path, '/'))
153         {
154             $extension = substr($path, $pos);
155             $path = substr($path, 0, $pos);
156         }
157 
158         $l = strlen($path);
159 
160         if ($l && $path{$l - 1} == '/')
161         {
162             $path = substr($path, 0, -1);
163         }
164 
165         #
166         # matching site
167         #
168 
169         $site = $core->site;
170         $siteid = $site->siteid;
171         $site_path = $site->path;
172 
173         if ($site_path)
174         {
175             if (strpos($path, $site_path) !== 0)
176             {
177                 return;
178             }
179 
180             $path = substr($path, strlen($site_path));
181         }
182 
183         if (!$path)
184         {
185             #
186             # The home page is requested, we load the first parentless online page of the site.
187             #
188 
189             $page = $this->find_home($siteid);
190 
191             if (!$page)
192             {
193                 return;
194             }
195 
196             if (!$this->retrieve($page->nid))
197             {
198                 $this->store($page);
199             }
200 
201             return $page;
202         }
203 
204         $parts = explode('/', $path);
205 
206         array_shift($parts);
207 
208         $parts_n = count($parts);
209 
210         $vars = array();
211 
212         #
213         # We load from all the pages just what we need to find a matching path, and create a tree
214         # with it.
215         #
216 
217         $tries = $this->select('nid, parentid, slug, pattern')->filter_by_siteid($siteid)->ordered->all(\PDO::FETCH_OBJ);
218         $tries = self::nestNodes($tries);
219 
220         $try = null;
221         $pages_by_ids = array();
222 
223         for ($i = 0 ; $i < $parts_n ; $i++)
224         {
225             if ($try)
226             {
227                 $tries = $try->children;
228             }
229 
230             $part = $path_part = $parts[$i];
231 
232             #
233             # first we search for a matching slug
234             #
235 
236             foreach ($tries as $try)
237             {
238                 $pattern = $try->pattern;
239 
240                 if ($pattern)
241                 {
242                     $stripped = preg_replace('#<[^>]+>#', '', $pattern);
243                     $nparts = substr_count($stripped, '/') + 1;
244                     $path_part = implode('/', array_slice($parts, $i, $nparts));
245 
246                     $pattern = Pattern::from($pattern);
247 
248                     if (!$pattern->match($path_part, $path_captured))
249                     {
250                         $try = null;
251 
252                         continue;
253                     }
254 
255                     #
256                     # found matching pattern !
257                     # we skip parts ate by the pattern
258                     #
259 
260                     $i += $nparts - 1;
261 
262                     #
263                     # even if the pattern matched, $match is not guaranteed to be an array,
264                     # 'feed.xml' is a valid pattern. // FIXME-20110327: is it still ?
265                     #
266 
267                     if (is_array($path_captured))
268                     {
269                         $vars = $path_captured + $vars;
270                     }
271 
272                     break;
273                 }
274                 else if ($part == $try->slug)
275                 {
276                     break;
277                 }
278 
279                 $try = null;
280             }
281 
282             #
283             # If `try` is null at this point it's that the path could not be matched.
284             #
285 
286             if (!$try)
287             {
288                 return;
289             }
290 
291             #
292             # otherwise, we continue
293             #
294 
295             $pages_by_ids[$try->nid] = array
296             (
297                 'url_part' => $path_part,
298                 'url_variables' => $vars
299             );
300         }
301 
302         #
303         # append the extension (if any) to the last page of the branch
304         #
305 
306         $pages_by_ids[$try->nid]['url_part'] .= $extension;
307 
308         #
309         # All page objects have been loaded, we need to set up some additionnal properties, link
310         # each page to its parent and propagate the online status.
311         #
312 
313         $parent = null;
314         $pages = $this->find(array_keys($pages_by_ids));
315 
316         foreach ($pages as $page)
317         {
318             $page->url_part = $pages_by_ids[$page->nid]['url_part'];
319             $page->url_variables = $pages_by_ids[$page->nid]['url_variables'];
320 
321             if ($parent)
322             {
323 //              $page->parent = $parent;
324 
325                 if (!$parent->is_online)
326                 {
327                     $page->is_online = false;
328                 }
329             }
330 
331             $parent = $page;
332         }
333 
334         return $page;
335     }
336 
337     /**
338      * Nest an array of nodes, using their `parentid` property.
339      *
340      * Children are stored in the `children` property of their parents.
341      *
342      * Parent is stored in the `parent` property of its children.
343      *
344      * @param array $entries The array of nodes.
345      * @param array $parents The array of nodes, where the key is the entry's `nid`.
346      */
347     static public function nestNodes($entries, &$entries_by_ids=null) // TODO-20120922: deprecate
348     {
349         #
350         # In order to easily access entries, they are store by their Id in an array.
351         #
352 
353         $entries_by_ids = array();
354 
355         foreach ($entries as $entry)
356         {
357             $entry->children = array();
358 
359             $entries_by_ids[$entry->nid] = $entry;
360         }
361 
362         #
363         #
364         #
365 
366         $tree = array();
367 
368         foreach ($entries_by_ids as $entry)
369         {
370             if (!$entry->parentid || empty($entries_by_ids[$entry->parentid]))
371             {
372                 $tree[] = $entry;
373 
374                 continue;
375             }
376 
377             $entry->parent = $entries_by_ids[$entry->parentid];
378             $entry->parent->children[] = $entry;
379         }
380 
381         return $tree;
382     }
383 
384     /**
385      * Walk the nodes and sets their depth level.
386      *
387      * @param $nodes The nodes to walk through.
388      * @param $max_depth The maximum depth level of the nodes. Nodes beyond the max_depth are removed.
389      * Default to false (no maximum depth level).
390      * @param $depth The depth level to start from. Default to 0.
391      */
392     static public function setNodesDepth($nodes, $max_depth=false, $depth=0) // TODO-20120922: deprecate
393     {
394         foreach ($nodes as $node)
395         {
396             $node->depth = $depth;
397 
398             if ($node->children)
399             {
400                 if ($max_depth !== false && $max_depth == $depth)
401                 {
402                     if ($max_depth === 1)
403                     {
404                         echo "<h1>max_depth ($max_depth) reached for</h1>";
405                         var_dump($node);
406                     }
407 
408                     #
409                     # The `children` property is unset rather then emptied, making the loading
410                     # of children possible by accessing the `children` property.
411                     #
412 
413                     unset($node->children);
414                 }
415                 else
416                 {
417                     self::setNodesDepth($node->children, $max_depth, $depth + 1);
418                 }
419             }
420         }
421     }
422 
423     /**
424      * Creates an array from all the nested nodes, where keys are node's Id.
425      *
426      * @param $nodes
427      */
428     static public function levelNodesById($nodes) // TODO-20120922: deprecate
429     {
430         $by_id = array();
431 
432         foreach ($nodes as $node)
433         {
434             $by_id[$node->nid] = $node;
435 
436             if (isset($node->children))
437             {
438                 $by_id += self::levelNodesById($node->children);
439             }
440         }
441 
442         return $by_id;
443     }
444 }
Autodoc API documentation generated by ApiGen 2.8.0