71522e7ada6c49c5c2e86722c6d2b361e4f2d481
[core.git] / inc / classes / main / template / menu / class_MenuTemplateEngine.php
1 <?php
2 /**
3  * A Menu template engine class
4  *
5  * @author              Roland Haeder <webmaster@ship-simu.org>
6  * @version             0.0.0
7  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2011 Core Developer Team
8  * @license             GNU GPL 3.0 or any newer version
9  * @link                http://www.ship-simu.org
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program. If not, see <http://www.gnu.org/licenses/>.
23  */
24 class MenuTemplateEngine extends BaseTemplateEngine implements CompileableTemplate {
25         /**
26          * Main nodes in the XML tree ('menu' is ignored)
27          */
28         private $mainNodes = array(
29                 'block-list',
30         );
31
32         /**
33          * Sub nodes in the XML tree
34          */
35         private $subNodes = array(
36                 'entry-list',
37                 'entry',
38                 'entry-id',
39                 'entries-content',
40                 'block-header',
41                 'block-footer',
42                 'footer-id',
43                 'footer-class',
44                 'footer-text',
45                 'block',
46                 'title',
47                 'title-id',
48                 'title-class',
49                 'title-text',
50                 'design',
51                 'text',
52                 'advert',
53                 'anchor',
54                 'anchor-id',
55                 'anchor-text',
56                 'anchor-title',
57                 'anchor-href',
58         );
59
60         /**
61          * Menu instance
62          */
63         private $menuInstance = null;
64
65         /**
66          * Current main node
67          */
68         private $curr = array();
69
70         /**
71          * Content from dependency
72          */
73         private $dependencyContent = array();
74
75         /**
76          * Protected constructor
77          *
78          * @return      void
79          */
80         protected function __construct () {
81                 // Call parent constructor
82                 parent::__construct(__CLASS__);
83         }
84
85         /**
86          * Creates an instance of the class TemplateEngine and prepares it for usage
87          *
88          * @param       $applicationInstance    A manageable application
89          * @param       $menuInstance                   A RenderableMenu instance
90          * @return      $templateInstance               An instance of TemplateEngine
91          * @throws      BasePathIsEmptyException                If the provided $templateBasePath is empty
92          * @throws      InvalidBasePathStringException  If $templateBasePath is no string
93          * @throws      BasePathIsNoDirectoryException  If $templateBasePath is no
94          *                                                                                      directory or not found
95          * @throws      BasePathReadProtectedException  If $templateBasePath is
96          *                                                                                      read-protected
97          */
98         public static final function createMenuTemplateEngine (ManageableApplication $applicationInstance, RenderableMenu $menuInstance) {
99                 // Get a new instance
100                 $templateInstance = new MenuTemplateEngine();
101
102                 // Determine base path
103                 $templateBasePath = $templateInstance->getConfigInstance()->getConfigEntry('application_base_path') . $applicationInstance->getRequestInstance()->getRequestElement('app') . '/';
104
105                 // Is the base path valid?
106                 if (empty($templateBasePath)) {
107                         // Base path is empty
108                         throw new BasePathIsEmptyException($templateInstance, self::EXCEPTION_UNEXPECTED_EMPTY_STRING);
109                 } elseif (!is_string($templateBasePath)) {
110                         // Is not a string
111                         throw new InvalidBasePathStringException(array($templateInstance, $templateBasePath), self::EXCEPTION_INVALID_STRING);
112                 } elseif (!is_dir($templateBasePath)) {
113                         // Is not a path
114                         throw new BasePathIsNoDirectoryException(array($templateInstance, $templateBasePath), self::EXCEPTION_INVALID_PATH_NAME);
115                 } elseif (!is_readable($templateBasePath)) {
116                         // Is not readable
117                         throw new BasePathReadProtectedException(array($templateInstance, $templateBasePath), self::EXCEPTION_READ_PROTECED_PATH);
118                 }
119
120                 // Set the base path
121                 $templateInstance->setTemplateBasePath($templateBasePath);
122
123                 // Set template extensions
124                 $templateInstance->setRawTemplateExtension($templateInstance->getConfigInstance()->getConfigEntry('raw_template_extension'));
125                 $templateInstance->setCodeTemplateExtension($templateInstance->getConfigInstance()->getConfigEntry('menu_template_extension'));
126
127                 // Absolute output path for compiled templates
128                 $templateInstance->setCompileOutputPath($templateInstance->getConfigInstance()->getConfigEntry('base_path') . $templateInstance->getConfigInstance()->getConfigEntry('compile_output_path'));
129
130                 // Set the menu instance
131                 $templateInstance->setMenuInstance($menuInstance);
132
133                 // Init a variable stacker
134                 $stackerInstance = ObjectFactory::createObjectByConfiguredName('menu_stacker_class');
135
136                 // Set it
137                 $templateInstance->setStackerInstance($stackerInstance);
138
139                 // Return the prepared instance
140                 return $templateInstance;
141         }
142
143         /**
144          * Load a specified menu template into the engine
145          *
146          * @param       $template       The menu template we shall load which is
147          *                                              located in 'menu' by default
148          * @return      void
149          */
150         public function loadMenuTemplate ($template) {
151                 // Set template type
152                 $this->setTemplateType($this->getConfigInstance()->getConfigEntry('menu_template_type'));
153
154                 // Load the special template
155                 $this->loadTemplate($template);
156         }
157
158         /**
159          * Getter for current main node
160          *
161          * @return      $currMainNode   Current main node
162          */
163         public final function getCurrMainNode () {
164                 return $this->curr['main_node'];
165         }
166
167         /**
168          * Setter for current main node
169          *
170          * @param       $element                Element name to set as current main node
171          * @return      $currMainNode   Current main node
172          */
173         private final function setCurrMainNode ($element) {
174                 $this->curr['main_node'] = (string) $element;
175         }
176
177         /**
178          * Getter for main node array
179          *
180          * @return      $mainNodes      Array with valid main node names
181          */
182         public final function getMainNodes () {
183                 return $this->mainNodes;
184         }
185
186         /**
187          * Getter for sub node array
188          *
189          * @return      $subNodes       Array with valid sub node names
190          */
191         public final function getSubNodes () {
192                 return $this->subNodes;
193         }
194
195         /**
196          * Handles the start element of an XML resource
197          *
198          * @param       $resource               XML parser resource (currently ignored)
199          * @param       $element                The element we shall handle
200          * @param       $attributes             All attributes
201          * @return      void
202          * @throws      InvalidXmlNodeException         If an unknown/invalid XML node name was found
203          */
204         public function startElement ($resource, $element, array $attributes) {
205                 // Initial method name which will never be called...
206                 $methodName = 'initMenu';
207
208                 // Make the element name lower-case
209                 $element = strtolower($element);
210
211                 // Is the element a main node?
212                 //* DEBUG: */ echo "START: &gt;".$element."&lt;<br />\n";
213                 if (in_array($element, $this->getMainNodes())) {
214                         // Okay, main node found!
215                         $methodName = 'start' . $this->convertToClassName($element);
216
217                         // Set it
218                         $this->setCurrMainNode($element);
219                 } elseif (in_array($element, $this->getSubNodes())) {
220                         // Sub node found
221                         $methodName = 'start' . $this->convertToClassName($element);
222                 } elseif ($element != 'menu') {
223                         // Invalid node name found
224                         throw new InvalidXmlNodeException(array($this, $element, $attributes), XmlParser::EXCEPTION_XML_NODE_UNKNOWN);
225                 }
226
227                 // Call method
228                 //* DEBUG: */ echo "call: ".$methodName."<br />\n";
229                 call_user_func_array(array($this, $methodName), $attributes);
230         }
231
232         /**
233          * Ends the main or sub node by sending out the gathered data
234          *
235          * @param       $resource       An XML resource pointer (currently ignored)
236          * @param       $nodeName       Name of the node we want to finish
237          * @return      void
238          * @throws      XmlNodeMismatchException        If current main node mismatches the closing one
239          */
240         public function endElement ($resource, $nodeName) {
241                 // Make all lower-case
242                 $nodeName = strtolower($nodeName);
243
244                 // Does this match with current main node?
245                 //* DEBUG: */ echo "END: &gt;".$nodeName."&lt;<br />\n";
246                 if (($nodeName != $this->getCurrMainNode()) && (in_array($nodeName, $this->getMainNodes()))) {
247                         // Did not match!
248                         throw new XmlNodeMismatchException (array($this, $nodeName, $this->getCurrMainNode()), XmlParser::EXCEPTION_XML_NODE_MISMATCH);
249                 } // END - if
250
251                 // Construct method name
252                 $methodName = 'finish' . $this->convertToClassName($nodeName);
253
254                 // Call the corresponding method
255                 //* DEBUG: */ echo "call: ".$methodName."<br />\n";
256                 call_user_func_array(array($this, $methodName), array());
257         }
258
259         /**
260          * Currently not used
261          *
262          * @param       $resource               XML parser resource (currently ignored)
263          * @param       $characters             Characters to handle
264          * @return      void
265          * @todo        Find something useful with this!
266          */
267         public function characterHandler ($resource, $characters) {
268                 // Trim all spaces away
269                 $characters = trim($characters);
270
271                 // Is this string empty?
272                 if (empty($characters)) {
273                         // Then skip it silently
274                         return false;
275                 } // END - if
276
277                 // Assign the found characters to variable and use the last entry from
278                 // stack as the name
279                 parent::assignVariable($this->getStackerInstance()->getNamed('current_node'), $characters);
280         }
281
282         /**
283          * Handles the template dependency for given node
284          *
285          * @param       $node                                   The node we should load a dependency template
286          * @param       $templateDependency             A template to load to satisfy dependencies
287          * @return      void
288          */
289         private function handleTemplateDependency ($node, $templateDependency) {
290                 // Is the template dependency set?
291                 if ((!empty($templateDependency)) && (!isset($this->dependencyContent[$node]))) {
292                         // Get a temporay menu template instance
293                         $templateInstance = ObjectFactory::createObjectByConfiguredName('menu_template_class', array($this->getApplicationInstance(), $this->getMenuInstance()));
294
295                         // Then load it
296                         $templateInstance->loadMenuTemplate($templateDependency);
297
298                         // Get an XmlParser instance
299                         $templateInstance->renderXmlContent();
300
301                         // Parse the template's content contents
302                         $this->dependencyContent[$node] = $templateInstance->getRawTemplateData();
303                 } // END - if
304         }
305
306         /**
307          * Intializes the menu
308          *
309          * @param       $templateDependency             A template to load to satisfy dependencies
310          * @return      void
311          * @todo        Add cache creation here
312          */
313         private function initMenu ($templateDependency = '') {
314                 // Get web template engine
315                 $this->setTemplateInstance(ObjectFactory::createObjectByConfiguredName('web_template_class', array($this->getApplicationInstance())));
316
317                 // Handle the dependency template
318                 $this->handleTemplateDependency('menu', $templateDependency);
319
320                 // Push the node name on the stacker
321                 $this->getStackerInstance()->pushNamed('current_node', 'menu');
322         }
323
324         /**
325          * Starts the menu entries
326          *
327          * @param       $templateDependency     A template to load to satisfy dependencies
328          * @return      void
329          */
330         private function startEntryList () {
331                 // Push the node name on the stacker
332                 $this->getStackerInstance()->pushNamed('current_node', 'entry-list');
333         }
334
335         /**
336          * Starts the menu block header
337          *
338          * @return      void
339          */
340         private function startBlockHeader () {
341                 // Push the node name on the stacker
342                 $this->getStackerInstance()->pushNamed('current_node', 'block-header');
343         }
344
345         /**
346          * Starts the menu block footer
347          *
348          * @return      void
349          */
350         private function startBlockFooter () {
351                 // Push the node name on the stacker
352                 $this->getStackerInstance()->pushNamed('current_node', 'block-footer');
353         }
354
355         /**
356          * Starts the menu property 'block-list'
357          *
358          * @return      void
359          */
360         private function startBlockList () {
361                 // Push the node name on the stacker
362                 $this->getStackerInstance()->pushNamed('current_node', 'block-list');
363         }
364
365         /**
366          * Starts the menu property 'block'
367          *
368          * @return      void
369          */
370         private function startBlock () {
371                 // Push the node name on the stacker
372                 $this->getStackerInstance()->pushNamed('current_node', 'block');
373         }
374
375         /**
376          * Starts the menu property 'title'
377          *
378          * @return      void
379          */
380         private function startTitle () {
381                 // Push the node name on the stacker
382                 $this->getStackerInstance()->pushNamed('current_node', 'title');
383         }
384
385         /**
386          * Starts the menu property 'title-id'
387          *
388          * @return      void
389          */
390         private function startTitleId () {
391                 // Push the node name on the stacker
392                 $this->getStackerInstance()->pushNamed('current_node', 'title-id');
393         }
394
395         /**
396          * Starts the menu property 'title-class'
397          *
398          * @return      void
399          */
400         private function startTitleClass () {
401                 // Push the node name on the stacker
402                 $this->getStackerInstance()->pushNamed('current_node', 'title-class');
403         }
404
405         /**
406          * Starts the menu property 'title-text'
407          *
408          * @return      void
409          */
410         private function startTitleText () {
411                 // Push the node name on the stacker
412                 $this->getStackerInstance()->pushNamed('current_node', 'title-text');
413         }
414
415         /**
416          * Starts the menu property 'entry'
417          *
418          * @return      void
419          */
420         private function startEntry () {
421                 // Push the node name on the stacker
422                 $this->getStackerInstance()->pushNamed('current_node', 'entry');
423         }
424
425         /**
426          * Starts the menu property 'entry-id'
427          *
428          * @return      void
429          */
430         private function startEntryId () {
431                 // Push the node name on the stacker
432                 $this->getStackerInstance()->pushNamed('current_node', 'entry-id');
433         }
434
435         /**
436          * Starts the menu property 'anchor'
437          *
438          * @return      void
439          */
440         private function startAnchor () {
441                 // Push the node name on the stacker
442                 $this->getStackerInstance()->pushNamed('current_node', 'anchor');
443         }
444
445         /**
446          * Starts the menu property 'anchor-id'
447          *
448          * @return      void
449          */
450         private function startAnchorId () {
451                 // Push the node name on the stacker
452                 $this->getStackerInstance()->pushNamed('current_node', 'anchor-id');
453         }
454
455         /**
456          * Starts the menu property 'anchor-text'
457          *
458          * @return      void
459          */
460         private function startAnchorText () {
461                 // Push the node name on the stacker
462                 $this->getStackerInstance()->pushNamed('current_node', 'anchor-text');
463         }
464
465         /**
466          * Starts the menu property 'anchor-title'
467          *
468          * @return      void
469          */
470         private function startAnchorTitle () {
471                 // Push the node name on the stacker
472                 $this->getStackerInstance()->pushNamed('current_node', 'anchor-title');
473         }
474
475         /**
476          * Starts the menu property 'anchor-href'
477          *
478          * @return      void
479          */
480         private function startAnchorHref () {
481                 // Push the node name on the stacker
482                 $this->getStackerInstance()->pushNamed('current_node', 'anchor-href');
483         }
484
485         /**
486          * Starts the menu property 'footer-id'
487          *
488          * @return      void
489          */
490         private function startFooterId () {
491                 // Push the node name on the stacker
492                 $this->getStackerInstance()->pushNamed('current_node', 'footer-id');
493         }
494
495         /**
496          * Starts the menu property 'footer-class'
497          *
498          * @return      void
499          */
500         private function startFooterClass () {
501                 // Push the node name on the stacker
502                 $this->getStackerInstance()->pushNamed('current_node', 'footer-class');
503         }
504
505         /**
506          * Starts the menu property 'footer-text'
507          *
508          * @return      void
509          */
510         private function startFooterText () {
511                 // Push the node name on the stacker
512                 $this->getStackerInstance()->pushNamed('current_node', 'footer-text');
513         }
514
515         /**
516          * Finishes the title node by added another template to the menu
517          *
518          * @return      void
519          */
520         private function finishTitle () {
521                 // Pop the last entry
522                 $this->getStackerInstance()->popNamed('current_node');
523         }
524
525         /**
526          * Finishes the title-id node by
527          *
528          * @return      void
529          */
530         private function finishTitleId () {
531                 // Pop the last entry
532                 $this->getStackerInstance()->popNamed('current_node');
533         }
534
535         /**
536          * Finishes the title-class node
537          *
538          * @return      void
539          */
540         private function finishTitleClass () {
541                 // Pop the last entry
542                 $this->getStackerInstance()->popNamed('current_node');
543         }
544
545         /**
546          * Finishes the title-class node
547          *
548          * @return      void
549          */
550         private function finishTitleText () {
551                 // Pop the last entry
552                 $this->getStackerInstance()->popNamed('current_node');
553         }
554
555         /**
556          * Finishes the footer-text node
557          *
558          * @return      void
559          */
560         private function finishFooterText () {
561                 // Pop the last entry
562                 $this->getStackerInstance()->popNamed('current_node');
563         }
564
565         /**
566          * Finishes the footer-class node
567          *
568          * @return      void
569          */
570         private function finishFooterClass () {
571                 // Pop the last entry
572                 $this->getStackerInstance()->popNamed('current_node');
573         }
574
575         /**
576          * Finishes the footer-id node
577          *
578          * @return      void
579          */
580         private function finishFooterId () {
581                 // Pop the last entry
582                 $this->getStackerInstance()->popNamed('current_node');
583         }
584
585         /**
586          * Finishes the anchor-href node
587          *
588          * @return      void
589          */
590         private function finishAnchorHref () {
591                 // Pop the last entry
592                 $this->getStackerInstance()->popNamed('current_node');
593         }
594
595         /**
596          * Finishes the anchor-title node
597          *
598          * @return      void
599          */
600         private function finishAnchorTitle () {
601                 // Pop the last entry
602                 $this->getStackerInstance()->popNamed('current_node');
603         }
604
605         /**
606          * Finishes the anchor-text node
607          *
608          * @return      void
609          */
610         private function finishAnchorText () {
611                 // Pop the last entry
612                 $this->getStackerInstance()->popNamed('current_node');
613         }
614
615         /**
616          * Finishes the anchor-id node
617          *
618          * @return      void
619          */
620         private function finishAnchorId () {
621                 // Pop the last entry
622                 $this->getStackerInstance()->popNamed('current_node');
623         }
624
625         /**
626          * Finishes the anchor node
627          *
628          * @return      void
629          */
630         private function finishAnchor () {
631                 // Pop the last entry
632                 $this->getStackerInstance()->popNamed('current_node');
633         }
634
635         /**
636          * Finishes the entry-id node
637          *
638          * @return      void
639          */
640         private function finishEntryId () {
641                 // Pop the last entry
642                 $this->getStackerInstance()->popNamed('current_node');
643         }
644
645         /**
646          * Finishes the entry node
647          *
648          * @return      void
649          */
650         private function finishEntry () {
651                 // Pop the last entry
652                 $this->getStackerInstance()->popNamed('current_node');
653         }
654
655         /**
656          * Finishes the block node
657          *
658          * @return      void
659          */
660         private function finishBlock () {
661                 // Pop the last entry
662                 $this->getStackerInstance()->popNamed('current_node');
663         }
664
665         /**
666          * Finishes the block-list node
667          *
668          * @return      void
669          */
670         private function finishBlockList () {
671                 // Pop the last entry
672                 $this->getStackerInstance()->popNamed('current_node');
673         }
674
675         /**
676          * Finishes the menu entries
677          *
678          * @return      void
679          */
680         private function finishEntryList () {
681                 // Pop the last entry
682                 $this->getStackerInstance()->popNamed('current_node');
683         }
684
685         /**
686          * Finishes the menu block header
687          *
688          * @return      void
689          */
690         private function finishBlockHeader () {
691                 // Pop the last entry
692                 $this->getStackerInstance()->popNamed('current_node');
693         }
694
695         /**
696          * Finishes the menu block footer
697          *
698          * @return      void
699          */
700         private function finishBlockFooter () {
701                 // Pop the last entry
702                 $this->getStackerInstance()->popNamed('current_node');
703         }
704
705         /**
706          * Finishes the menu
707          *
708          * @return      void
709          */
710         private function finishMenu () {
711                 // Pop the last entry
712                 $this->getStackerInstance()->popNamed('current_node');
713         }
714
715         /**
716          * Getter for menu cache file (FQFN)
717          *
718          * @return      $fqfn   Full-qualified file name of the menu cache
719          */
720         public function getMenuCacheFqfn () {
721                 // Get the FQFN ready
722                 $fqfn = sprintf("%s%s%s/%s.%s",
723                         $this->getConfigInstance()->getConfigEntry('base_path'),
724                         $this->getGenericBasePath(),
725                         'menus/_cache',
726                         md5(
727                                 $this->getMenuInstance()->getMenuName() . ':' .
728                                 $this->__toString() . ':' .
729                                 $this->getMenuInstance()->__toString()
730                         ),
731                         $this->getMenuInstance()->getMenuType()
732                 );
733
734                 // Return it
735                 return $fqfn;
736         }
737 }
738
739 // [EOF]
740 ?>