Now all sub nodes for an anchor are supported
[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 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                 'entries-content',
39                 'header',
40                 'footer',
41                 'block',
42                 'title',
43                 'design',
44                 'text',
45                 'advert',
46                 'anchor',
47                 'anchor-id',
48                 'anchor-text',
49                 'anchor-title',
50                 'anchor-href',
51         );
52
53         /**
54          * Menu instance
55          */
56         private $menuInstance = null;
57
58         /**
59          * Current main node
60          */
61         private $curr = array();
62
63         /**
64          * Content from depency
65          */
66         private $depencyContent = array();
67
68         /**
69          * Protected constructor
70          *
71          * @return      void
72          */
73         protected function __construct () {
74                 // Call parent constructor
75                 parent::__construct(__CLASS__);
76         }
77
78         /**
79          * Creates an instance of the class TemplateEngine and prepares it for usage
80          *
81          * @param       $appInstance    A manageable application
82          * @param       $menuInstance   A RenderableMenu instance
83          * @return      $tplInstance    An instance of TemplateEngine
84          * @throws      BasePathIsEmptyException                If the provided $templateBasePath is empty
85          * @throws      InvalidBasePathStringException  If $templateBasePath is no string
86          * @throws      BasePathIsNoDirectoryException  If $templateBasePath is no
87          *                                                                                      directory or not found
88          * @throws      BasePathReadProtectedException  If $templateBasePath is
89          *                                                                                      read-protected
90          */
91         public final static function createMenuTemplateEngine (ManageableApplication $appInstance, RenderableMenu $menuInstance) {
92                 // Get a new instance
93                 $tplInstance = new MenuTemplateEngine();
94
95                 // Get language and file I/O instances from application
96                 $langInstance = $appInstance->getLanguageInstance();
97                 $ioInstance = $appInstance->getFileIoInstance();
98
99                 // Determine base path
100                 $templateBasePath = $tplInstance->getConfigInstance()->getConfigEntry('application_base_path') . $appInstance->getRequestInstance()->getRequestElement('app') . '/';
101
102                 // Is the base path valid?
103                 if (empty($templateBasePath)) {
104                         // Base path is empty
105                         throw new BasePathIsEmptyException($tplInstance, self::EXCEPTION_UNEXPECTED_EMPTY_STRING);
106                 } elseif (!is_string($templateBasePath)) {
107                         // Is not a string
108                         throw new InvalidBasePathStringException(array($tplInstance, $templateBasePath), self::EXCEPTION_INVALID_STRING);
109                 } elseif (!is_dir($templateBasePath)) {
110                         // Is not a path
111                         throw new BasePathIsNoDirectoryException(array($tplInstance, $templateBasePath), self::EXCEPTION_INVALID_PATH_NAME);
112                 } elseif (!is_readable($templateBasePath)) {
113                         // Is not readable
114                         throw new BasePathReadProtectedException(array($tplInstance, $templateBasePath), self::EXCEPTION_READ_PROTECED_PATH);
115                 }
116
117                 // Get configuration instance
118                 $configInstance = FrameworkConfiguration::getInstance();
119
120                 // Set the base path
121                 $tplInstance->setTemplateBasePath($templateBasePath);
122
123                 // Set the language and IO instances
124                 $tplInstance->setLanguageInstance($langInstance);
125                 $tplInstance->setFileIoInstance($ioInstance);
126
127                 // Set template extensions
128                 $tplInstance->setRawTemplateExtension($configInstance->getConfigEntry('raw_template_extension'));
129                 $tplInstance->setCodeTemplateExtension($configInstance->getConfigEntry('menu_template_extension'));
130
131                 // Absolute output path for compiled templates
132                 $tplInstance->setCompileOutputPath($configInstance->getConfigEntry('base_path') . $configInstance->getConfigEntry('compile_output_path'));
133
134                 // Set the menu instance
135                 $tplInstance->setMenuInstance($menuInstance);
136
137                 // Return the prepared instance
138                 return $tplInstance;
139         }
140
141         /**
142          * Load a specified menu template into the engine
143          *
144          * @param       $template       The menu template we shall load which is
145          *                                              located in 'menu' by default
146          * @return      void
147          */
148         public function loadMenuTemplate ($template) {
149                 // Set template type
150                 $this->setTemplateType($this->getConfigInstance()->getConfigEntry('menu_template_type'));
151
152                 // Load the special template
153                 $this->loadTemplate($template);
154         }
155
156         /**
157          * Getter for current main node
158          *
159          * @return      $currMainNode   Current main node
160          */
161         public final function getCurrMainNode () {
162                 return $this->curr['main_node'];
163         }
164
165         /**
166          * Setter for current main node
167          *
168          * @param       $element                Element name to set as current main node
169          * @return      $currMainNode   Current main node
170          */
171         private final function setCurrMainNode ($element) {
172                 $this->curr['main_node'] = (string) $element;
173         }
174
175         /**
176          * Getter for main node array
177          *
178          * @return      $mainNodes      Array with valid main node names
179          */
180         public final function getMainNodes () {
181                 return $this->mainNodes;
182         }
183
184         /**
185          * Getter for sub node array
186          *
187          * @return      $subNodes       Array with valid sub node names
188          */
189         public final function getSubNodes () {
190                 return $this->subNodes;
191         }
192
193         /**
194          * Handles the start element of an XML resource
195          *
196          * @param       $resource               XML parser resource (currently ignored)
197          * @param       $element                The element we shall handle
198          * @param       $attributes             All attributes
199          * @return      void
200          * @throws      InvalidXmlNodeException         If an unknown/invalid XML node name was found
201          */
202         public function startElement ($resource, $element, array $attributes) {
203                 // Initial method name which will never be called...
204                 $methodName = 'initMenu';
205
206                 // Make the element name lower-case
207                 $element = strtolower($element);
208
209                 // Is the element a main node?
210                 //* DEBUG: */ echo "START: &gt;".$element."&lt;<br />\n";
211                 if (in_array($element, $this->getMainNodes())) {
212                         // Okay, main node found!
213                         $methodName = 'start' . $this->convertToClassName($element);
214
215                         // Set it
216                         $this->setCurrMainNode($element);
217                 } elseif (in_array($element, $this->getSubNodes())) {
218                         // Sub node found
219                         $methodName = 'start' . $this->convertToClassName($element);
220                 } elseif ($element != 'menu') {
221                         // Invalid node name found
222                         throw new InvalidXmlNodeException(array($this, $element, $attributes), XmlParser::EXCEPTION_XML_NODE_UNKNOWN);
223                 }
224
225                 // Call method
226                 //* DEBUG: */ echo "call: ".$methodName."<br />\n";
227                 call_user_func_array(array($this, $methodName), $attributes);
228         }
229
230         /**
231          * Ends the main or sub node by sending out the gathered data
232          *
233          * @param       $resource       An XML resource pointer (currently ignored)
234          * @param       $nodeName       Name of the node we want to finish
235          * @return      void
236          * @throws      XmlNodeMismatchException        If current main node mismatches the closing one
237          */
238         public function endElement ($resource, $nodeName) {
239                 // Make all lower-case
240                 $nodeName = strtolower($nodeName);
241
242                 // Does this match with current main node?
243                 //* DEBUG: */ echo "END: &gt;".$nodeName."&lt;<br />\n";
244                 if (($nodeName != $this->getCurrMainNode()) && (in_array($nodeName, $this->getMainNodes()))) {
245                         // Did not match!
246                         throw new XmlNodeMismatchException (array($this, $nodeName, $this->getCurrMainNode()), XmlParser::EXCEPTION_XML_NODE_MISMATCH);
247                 } // END - if
248
249                 // Construct method name
250                 $methodName = 'finish' . $this->convertToClassName($nodeName);
251
252                 // Call the corresponding method
253                 //* DEBUG: */ echo "call: ".$methodName."<br />\n";
254                 call_user_func_array(array($this, $methodName), array());
255         }
256
257         /**
258          * Currently not used
259          *
260          * @param       $resource               XML parser resource (currently ignored)
261          * @param       $characters             Characters to handle
262          * @return      void
263          * @todo        Find something useful with this!
264          */
265         public function characterHandler ($resource, $characters) {
266                 // Trim all spaces away
267                 $characters = trim($characters);
268
269                 // Is this string empty?
270                 if (empty($characters)) {
271                         // Then skip it silently
272                         return false;
273                 } // END - if
274
275                 // Unfinished work!
276                 $this->partialStub('Handling extra characters is not yet supported! length='.strlen($characters));
277         }
278
279         /**
280          * Handles the template depency for given node
281          *
282          * @param       $node   The node we should load a depency template
283          * @param       $templateDepency        A template to load to satisfy depencies
284          * @return      void
285          */
286         private function handleTemplateDepency ($node, $templateDepency) {
287                 // Is the template depency set?
288                 if ((!empty($templateDepency)) && (!isset($this->depencyContent[$node]))) {
289                         // Get a temporay menu template instance
290                         $templateInstance = ObjectFactory::createObjectByConfiguredName('menu_template_class', array($this->getApplicationInstance(), $this->getMenuInstance()));
291
292                         // Then load it
293                         $templateInstance->loadMenuTemplate($templateDepency);
294
295                         // Get an XmlParser instance
296                         $templateInstance->renderXmlContent();
297
298                         // Parse the template's content contents
299                         $this->depencyContent[$node] = $templateInstance->getRawTemplateData();
300                 } // END - if
301         }
302
303         /**
304          * Intializes the menu
305          *
306          * @param       $templateDepency        A template to load to satisfy depencies
307          * @return      void
308          * @todo        Add cache creation here
309          */
310         private function initMenu ($templateDepency = '') {
311                 // Get web template engine
312                 $this->setTemplateInstance(ObjectFactory::createObjectByConfiguredName('web_template_class', array($this->getApplicationInstance())));
313
314                 // Handle the depency template
315                 $this->handleTemplateDepency('menu', $templateDepency);
316
317                 // Load the header template for this page
318                 $this->getTemplateInstance()->loadCodeTemplate('menu_global_start');
319
320                 // Set the variable group to page
321                 $this->setVariableGroup('menu');
322
323                 // Set its content in this template instance
324                 $this->assignVariable('menu_start', $this->getTemplateInstance()->getRawTemplateData());
325         }
326
327         /**
328          * Finishes the menu
329          *
330          * @return      void
331          */
332         private function finishMenu () {
333                 // Load the header template for this page
334                 $this->getTemplateInstance()->loadCodeTemplate('menu_global_end');
335
336                 // Set the variable group to page
337                 $this->setVariableGroup('menu');
338
339                 // Set its content in this template instance
340                 $this->assignVariable('menu_end', $this->getTemplateInstance()->getRawTemplateData());
341         }
342
343         /**
344          * Starts the menu entries by loading a (maybe) provided template depency
345          *
346          * @param       $templateDepency        A template to load to satisfy depencies
347          * @return      void
348          */
349         private function startEntryList ($templateDepency = '') {
350                 // Handle the depency template
351                 $this->handleTemplateDepency('entries', $templateDepency);
352
353                 // Load the header template for this page
354                 $this->getTemplateInstance()->loadCodeTemplate('menu_entries_start');
355
356                 // Set the variable group to page
357                 $this->setVariableGroup('menu');
358
359                 // Set its content in this template instance
360                 $this->assignVariable('entries_start', $this->getTemplateInstance()->getRawTemplateData());
361         }
362
363         /**
364          * Finishes the menu entries
365          *
366          * @return      void
367          */
368         private function finishEntryList () {
369                 // Load the header template for this page
370                 $this->getTemplateInstance()->loadCodeTemplate('menu_entries_end');
371
372                 // Set the variable group to page
373                 $this->setVariableGroup('menu');
374
375                 // Set its content in this template instance
376                 $this->assignVariable('entries_end', $this->getTemplateInstance()->getRawTemplateData());
377         }
378
379         /**
380          * Starts the menu header
381          *
382          * @return      void
383          */
384         private function startHeader () {
385                 // Do we have a template instance?
386                 if (is_null($this->getTemplateInstance())) {
387                         // Init template instance for underlaying web templates
388                         $templateInstance = ObjectFactory::createObjectByConfiguredName('web_template_class');
389
390                         // Set it in this template engine
391                         $this->setTemplateInstance($templateInstance);
392                 } // END - if
393
394                 // Load the header template for this page
395                 $this->getTemplateInstance()->loadCodeTemplate('menu_header_start');
396
397                 // Set the variable group to page
398                 $this->setVariableGroup('menu');
399
400                 // Set its content in this template instance
401                 $this->assignVariable('header', $this->getTemplateInstance()->getRawTemplateData());
402         }
403
404         /**
405          * Finishes the menu header
406          *
407          * @return      void
408          */
409         private function finishHeader () {
410                 // Load the header template for this page
411                 $this->getTemplateInstance()->loadCodeTemplate('menu_header_end');
412
413                 // Set the variable group to page
414                 $this->setVariableGroup('menu');
415
416                 // Set its content in this template instance
417                 $this->assignVariable('header_end', $this->getTemplateInstance()->getRawTemplateData());
418         }
419
420         /**
421          * Starts the menu footer
422          *
423          * @return      void
424          */
425         private function startFooter () {
426                 // Do we have a template instance?
427                 if (is_null($this->getTemplateInstance())) {
428                         // Init template instance for underlaying web templates
429                         $templateInstance = ObjectFactory::createObjectByConfiguredName('web_template_class');
430
431                         // Set it in this template engine
432                         $this->setTemplateInstance($templateInstance);
433                 } // END - if
434
435                 // Load the footer template for this page
436                 $this->getTemplateInstance()->loadCodeTemplate('menu_footer_start');
437
438                 // Set the variable group to page
439                 $this->setVariableGroup('menu');
440
441                 // Set its content in this template instance
442                 $this->assignVariable('footer', $this->getTemplateInstance()->getRawTemplateData());
443         }
444
445         /**
446          * Finishes the menu footer
447          *
448          * @return      void
449          */
450         private function finishFooter () {
451                 // Load the footer template for this page
452                 $this->getTemplateInstance()->loadCodeTemplate('menu_footer_end');
453
454                 // Set the variable group to page
455                 $this->setVariableGroup('menu');
456
457                 // Set its content in this template instance
458                 $this->assignVariable('footer_end', $this->getTemplateInstance()->getRawTemplateData());
459         }
460
461         /**
462          * Starts the menu property 'title'
463          *
464          * @param       $id             Id of the menu
465          * @param       $class  The title to add to the menu
466          * @return      void
467          */
468         private function startTitle ($id, $class) {
469                 // Set id as current
470                 $this->curr['id'] = $id;
471
472                 // Load the title template for this page
473                 $this->getTemplateInstance()->loadCodeTemplate('menu_title_' . $id . '_start');
474
475                 // Set the variable group to page
476                 $this->setVariableGroup('menu');
477
478                 // Set its content in this template instance
479                 $this->assignVariable('title_start_' . $this->curr['id'], $this->getTemplateInstance()->getRawTemplateData());
480         }
481
482         /**
483          * Finishes the title node by added another template to the menu
484          *
485          * @return      void
486          */
487         private function finishTitle () {
488                 // Load the title template for this page
489                 $this->getTemplateInstance()->loadCodeTemplate('menu_title_' . $this->curr['id'] . '_end');
490
491                 // Set the variable group to page
492                 $this->setVariableGroup('menu');
493
494                 // Set its content in this template instance
495                 $this->assignVariable('title_end_' . $this->curr['id'], $this->getTemplateInstance()->getRawTemplateData());
496         }
497
498         /**
499          * Starts the menu text
500          *
501          * @return      void
502          */
503         private function startText () {
504                 // Do we have a template instance?
505                 if (is_null($this->getTemplateInstance())) {
506                         // Init template instance for underlaying web templates
507                         $templateInstance = ObjectFactory::createObjectByConfiguredName('web_template_class');
508
509                         // Set it in this template engine
510                         $this->setTemplateInstance($templateInstance);
511                 } // END - if
512
513                 // Load the text template for this page
514                 $this->getTemplateInstance()->loadCodeTemplate('menu_text_start');
515
516                 // Set the variable group to page
517                 $this->setVariableGroup('menu');
518
519                 // Set its content in this template instance
520                 $this->assignVariable('text', $this->getTemplateInstance()->getRawTemplateData());
521         }
522
523         /**
524          * Finishes the menu text
525          *
526          * @return      void
527          */
528         private function finishText () {
529                 // Load the text template for this page
530                 $this->getTemplateInstance()->loadCodeTemplate('menu_text_end');
531
532                 // Set the variable group to page
533                 $this->setVariableGroup('menu');
534
535                 // Set its content in this template instance
536                 $this->assignVariable('text_end', $this->getTemplateInstance()->getRawTemplateData());
537         }
538
539         /**
540          * Starts the menu property 'entry'
541          *
542          * @param       $id             Id of the menu
543          * @return      void
544          */
545         private function startEntry ($id) {
546                 // Set id as current
547                 $this->curr['entry_id'] = $id;
548
549                 // Load the entry template for this page
550                 $this->getTemplateInstance()->loadCodeTemplate('menu_entry_' . $id . '_start');
551
552                 // Set the variable group to page
553                 $this->setVariableGroup('menu');
554
555                 // Set its content in this template instance
556                 $this->assignVariable('entry_start_' . $this->curr['id'], $this->getTemplateInstance()->getRawTemplateData());
557         }
558
559         /**
560          * Finishes the entry node by added another template to the menu
561          *
562          * @return      void
563          */
564         private function finishEntry () {
565                 // Load the entry template for this page
566                 $this->getTemplateInstance()->loadCodeTemplate('menu_entry_' . $this->curr['entry_id'] . '_end');
567
568                 // Set the variable group to page
569                 $this->setVariableGroup('menu');
570
571                 // Set its content in this template instance
572                 $this->assignVariable('entry_end_' . $this->curr['id'], $this->getTemplateInstance()->getRawTemplateData());
573         }
574         /**
575          * Starts the menu property 'anchor'
576          *
577          * @param       $id             Id of the anchor
578          * @param       $link   Link text of the anchor
579          * @param       $title  Link title of the anchor
580          * @return      void
581          */
582         private function startAnchor () {
583                 $this->partialStub('Please implement this method.');
584         }
585
586         /**
587          * Finishes the anchor node by added another template to the menu
588          *
589          * @return      void
590          */
591         private function finishAnchor () {
592                 $this->partialStub('Please implement this method.');
593         }
594
595         /**
596          * Getter for menu cache file (FQFN)
597          *
598          * @return      $fqfn   Full-qualified file name of the menu cache
599          */
600         public function getMenuCacheFqfn () {
601                 // Get the FQFN ready
602                 $fqfn = sprintf("%s%s%s/%s.%s",
603                         $this->getConfigInstance()->getConfigEntry('base_path'),
604                         $this->getGenericBasePath(),
605                         'menus/_cache',
606                         md5(
607                                 $this->getMenuInstance()->getMenuName() . ':' .
608                                 $this->__toString() . ':' .
609                                 $this->getMenuInstance()->__toString()
610                         ),
611                         $this->getMenuInstance()->getMenuType()
612                 );
613
614                 // Return it
615                 return $fqfn;
616         }
617 }
618
619 // [EOF]
620 ?>