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