Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 26
CRAP
0.00% covered (danger)
0.00%
0 / 204
Template
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 26
5852
0.00% covered (danger)
0.00%
0 / 204
 __construct( $assoc = null, \aae\ui\ResourceManagerInterface $resourceManager = null, \aae\ui\Escapeable $escaper = null, $load = true )
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 7
 getTemplate($templateName)
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 3
 objectSet($object)
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 2
 load($templateName = null)
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 13
 loadFromFile($path)
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 7
 setLocalizer($localizer)
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 __toString()
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 11
 getBaseDir()
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 offsetSet($offset, $value)
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 3
 offsetExists($offset)
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 offsetUnset($offset)
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 offsetGet($offset)
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 1
 arraySet($array)
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 5
 _collapseArrays()
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 13
 _collapseArray($templateVarName)
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 29
 _insertResourceLinks()
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 13
 _resolveVars($string, $replacements, $escape = false)
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 _resolveLocalizerVars($string)
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 6
 _insertResources($templateName)
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 10
 _addResource($dependencyName)
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 6
 _getResolvedTemplateDir($templateName)
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 3
 _getTemplateConfig($templateName)
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 10
 _setConfigConstants($assoc)
0.00% covered (danger)
0.00%
0 / 1
90
0.00% covered (danger)
0.00%
0 / 25
 _buildTemplate($assoc)
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 9
 _buildPath($path)
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 5
 _insertChildren($templateConfig)
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 16
<?php
/**
 *
 */
namespace aae\ui {
    /**
     * @author Axel Ancona Esselmann
     * @package aae\ui
     */
    class Template implements \ArrayAccess, TemplateInterface {
        private $_assoc, $_baseDir = "", $_modRewriteDocumentRoot = "", $_resourceManager, $_outputEscaper, $_htmlString = "", $_outputAssoc = [], $_outputAssocUnescaped = [], $_templateName = NULL, $_localizer = NULL, $_varResolver =NULL, $_noTemplate = false;
        /**
         *
         * Template is a Document that can build itself from multiple template
         * source files. An associative array passed in the constructor dictates
         * the structure of the final HTML DOM. The document structure is not flat,
         * elements can have child elements listed in an array with a "children"
         * key. Those elements are inserted into their respective parent tags
         * by CSS id. The parent's CSS id's are located in each child's "id" key.
         * The template files for each element to be loaded should be provided
         * with a "template" key.
         *
         * Unless a "base_dir" is present ( see load() ), paths cant be relative.
         * Paths start one directory below the document root.
         *
         *
         * The document.html file has a div with a "top" id
         * and a div with a "main" id.
         * <body>
         *   <div id="top"></div>
         *   <div id="top"></div>
         * </body>
         *
         * Both, top.html and main.html have valid HTML inside <body> tags.
         *
         * {
         *   "template": "/protected/templates/document.html",
         *   "children":
         *     [
         *       {
         *         "div": "top",
         *         "template": "/protected/templates/top.html"
         *       },
         *       {
         *         "div": "main",
         *         "template": "/protected/templates/main.html"
         *       }
         *     ]
         *   }
         * }
         *
         * (For readability the associative array is displayed in JSON)
         *
         * The example above builds the following document structure:
         *
         * <body>
         *   <div id="top">
         *     <div>Content of child nodes of <body> tag in top.html</div>
         *   </div>
         *   <div id="top">
         *     <div>Content of child nodes of <body> tag in main.html</div>
         *   </div>
         * </body>
         *
         *
         * @param array  $assoc template configuration
         * @param boolean $load true build the template during instantiation
         */
        public function __construct(
            $assoc = null,
            \aae\ui\ResourceManagerInterface $resourceManager = null,
            \aae\ui\Escapeable $escaper = null,
            $load = true
        ) {
            $this->_assoc = $assoc;
            $this->_resourceManager = $resourceManager;
            if (!is_null($resourceManager) && array_key_exists("css_vars", $this->_assoc)) $this->_resourceManager->setReplacementVars($this->_assoc["css_vars"]);
            $this->_outputEscaper = $escaper;
            $this->_varResolver = new \aae\str\VarSubstituter($this->_outputEscaper);
            if ($load) $this->load();
        }
        public function getTemplate($templateName) {
            $template = new Template($this->_assoc, $this->_resourceManager, $this->_outputEscaper, true);
            $template->load($templateName);
            return $template;
        }
        public function objectSet($object) {
            foreach ($object->toArray() as $varName => $value) $this->_outputAssoc[$varName] = $value;
        }
        /**
         *
         * The loading of Template from a configuration file can be separated from
         * instantiation. To take advantage of this feature, pass false as a
         * second argument to the Template constructor.
         *
         * If load() is used, a specific template in the assoc array can be used
         * to load Template. The structure of the assoc passed to the constructor
         * should then be as follows:
         *
         * {
         *    "theNameOfTheTemplate": {
         *      ...
         *    }
         * }
         *
         * (For readability the associative array is displayed in JSON)
         *
         * @param  string $templateName the key name of the template to be loaded
         */
        public function load($templateName = null) {
            if (is_array($this->_assoc)) {
                if (is_null($this->_templateName)) {
                    $this->_templateName = $templateName;
                }
                $templateConfig = $this->_getTemplateConfig($templateName);
                $this->_setConfigConstants($this->_assoc);
                try {
                    $this->_buildTemplate($templateConfig);
                    $this->_insertResources($templateName);
                    $this->_insertChildren($templateConfig);
                } catch (\aae\fs\FileDoesNotExistException $e) {
                    $this->_noTemplate = true;
                }
            }
        }
        public function loadFromFile($path) {
            $extension = ".".basename(dirname($path));
            if (substr($path, -strlen($extension)) !== $extension) {
                $path .= $extension;
            }
            $path = new \aae\fs\File($path);
            $this->_htmlString = file_get_contents($path);
        }
        public function setLocalizer($localizer) {
            $this->_localizer = $localizer;
        }
        public function __toString() {
            if ($this->_noTemplate) {
                $this->_collapseArrays();
                $joined = array_merge($this->_outputAssoc, $this->_outputAssocUnescaped);
                // var_dump(json_decode(json_encode($joined)));
                return json_encode($joined);
            }
            if (is_null($this->_templateName)) $this->load("DefaultView");
            $this->_insertResourceLinks();
            $this->_collapseArrays();
            $out = $this->_resolveVars($this->_htmlString, $this->_outputAssocUnescaped);
            $out = $this->_resolveVars($out, $this->_outputAssoc, true);
            $out = $this->_resolveLocalizerVars($out);
            return $out;
        }
        public function getBaseDir() {
            return $this->_baseDir;
        }
        public function offsetSet($offset, $value) {
            if (is_null($offset)) throw new \Exception("Provide an offset add to the template.", 1208141630);
            $this->_outputAssoc[$offset] = $value;
        }
        public function offsetExists($offset) {
            return isset($this->_outputAssoc[$offset]);
        }
        public function offsetUnset($offset) {
            unset($this->_outputAssoc[$offset]);
        }
        public function offsetGet($offset) {
            return isset($this->_outputAssoc[$offset]) ? $this->_outputAssoc[$offset] : null;
        }
        public function arraySet($array) {
            if (!is_array($array)) throw new \Exception("arraySet expects an array of arguments", 204151756);
            foreach ($array as $key => $value) {
                $this[$key] = $value;
            }
        }
        private function _collapseArrays() {
            foreach ($this->_outputAssoc as $varName => $varValue) {
                if (is_array($varValue)) {
                    $this->_collapseArray($varName);
                } else {
                    if (is_a($varValue, get_class($this))) {
                        $this->_outputAssoc[$varName] = new \aae\ui\encoding\IsHtml($varValue->__toString());
                    } else if (is_subclass_of($varValue, "\\aae\\db\\Persistable")) {
                        $objAsArray = $varValue->toArray();
                        $this->_outputAssoc[$varName] = [$objAsArray];
                        $this->_collapseArray($varName);
                    }
                }
            }
        }
        private function _collapseArray($templateVarName) {
            $replacementElements = [];
            $subTemplateName = ($this->_templateName)."_".$templateVarName;
            foreach ($this->_outputAssoc[$templateVarName] as $key => $element) {
                if (is_subclass_of($element, "\\aae\\db\\Persistable")) {
                    $element = $element->toArray();
                }
                $elementString = $this->_outputEscaper->getArrayElementOpen();
                $subTemplate = new Template(
                    $this->_assoc,
                    $this->_resourceManager,
                    $this->_outputEscaper,
                    false
                );
                $subTemplate->_baseDir = $this->_baseDir;
                $subTemplate->load($subTemplateName);
                if (is_array($element)) {
                    foreach ($element as $varName => $varValue) {
                        $subTemplate[$varName] = $varValue;
                    }
                }
                $elementString .= $subTemplate->__toString();
                $elementString .= $this->_outputEscaper->getArrayElementClose();
                $replacementElements[] = $elementString;
            }
            $replacement  = $this->_outputEscaper->getArrayOpen();
            $replacement .= implode($replacementElements, $this->_outputEscaper->getArrayElementSeparator());
            $replacement .= $this->_outputEscaper->getArrayClose();
            $this->_outputAssocUnescaped[$templateVarName] = $replacement;
            unset($this->_outputAssoc[$templateVarName]);
        }
        protected function _insertResourceLinks() {
            $resourceLinks = $this->_resourceManager->getLinks();
            $this->_outputAssocUnescaped["cssLinks"] = "";
            $this->_outputAssocUnescaped["jsLinks"]  = "";
            if (count($resourceLinks) > 0) {
                foreach ($resourceLinks as $link) {
                    if (pathinfo($link, PATHINFO_EXTENSION) == "js") {
                        $this->_outputAssocUnescaped["jsLinks"] .= "\n<script type=\"text/javascript\" src=\"$link\"></script>";
                    } else if (pathinfo($link, PATHINFO_EXTENSION) == "css") {
                        $this->_outputAssocUnescaped["cssLinks"] .= "\n<link rel=\"stylesheet\" type=\"text/css\" href=\"$link\" />";
                    }
                }
            }
        }
        private function _resolveVars($string, $replacements, $escape = false) {
            return $this->_varResolver->resolveVars($string, $replacements, $escape);
        }
        private function _resolveLocalizerVars($string) {
            if (is_null($this->_localizer)) return $string;
            return $this->_varResolver->resolveVars(
                $string,
                $this->_localizer->localizeAll(),
                true,
                "@"
            );
        }
        private function _insertResources($templateName) {
            if (!is_null($templateName)) {
                $viewPos = strpos($templateName, "View");
                if ($viewPos) {
                    $jsDependencyName = substr($templateName, 0, $viewPos)."JS";
                    $this->_addResource($jsDependencyName);
                    $cssDependencyName = substr($templateName, 0, $viewPos)."CSS";
                    $this->_addResource($cssDependencyName);
                }
            }
        }
        private function _addResource($dependencyName) {
            if (array_key_exists($dependencyName, $this->_assoc)) {
                foreach ($this->_assoc[$dependencyName] as $dependency) {
                    $this->_resourceManager->addResource($dependency);
                }
            }
        }
        private function _getResolvedTemplateDir($templateName) {
            $extension = ".".basename($this->_baseDir);
            $path = new \aae\fs\File($this->_baseDir.$templateName.$extension);
            return $path;
        }
        private function _getTemplateConfig($templateName) {
            if (!is_null($templateName)) {
                if (!array_key_exists($templateName, $this->_assoc)) {
                    try {
                        $path = $this->_getResolvedTemplateDir($templateName);
                        if (file_exists($path)) {
                            return ["template" => $templateName];
                        }
                    } catch (\aae\fs\FileDoesNotExistException $e) {
                        return ["template" => false];
                        // throw new \aae\fs\FileDoesNotExistException("No entry for template $templateName in the template configuration file.", 225141923);
                    }
                }
                return $this->_assoc[$templateName];
            } else {
                return $this->_assoc;
            }
        }
        private function _setConfigConstants($assoc) {
            if (array_key_exists("base_dir", $assoc)) {
                $this->_baseDir = $assoc["base_dir"];
            }
            if (array_key_exists("mod_rewrite_document_root", $assoc)) {
                $this->_modRewriteDocumentRoot = $assoc["mod_rewrite_document_root"];
            }
            if (!is_null($this->_resourceManager)) {
                if (array_key_exists("js_base_dir", $assoc)) {
                    $this->_resourceManager->addJsBaseDir($assoc["js_base_dir"]);
                }
                if (array_key_exists("js_model_dir", $assoc)) {
                    $this->_resourceManager->addJsBaseDir($assoc["js_model_dir"]);
                }
                if (array_key_exists("css_base_dir", $assoc)) {
                    $this->_resourceManager->addCssBaseDir($assoc["css_base_dir"]);
                }
                if (array_key_exists("css_model_dir", $assoc)) {
                    $this->_resourceManager->addCssBaseDir($assoc["css_model_dir"]);
                }
            }
            if (!is_null($this->_resourceManager)) {
                $this->_resourceManager->setBaseDir($this->_baseDir);
                $this->_resourceManager->setModRewriteDocumentRoot($this->_modRewriteDocumentRoot);
            }
        }
        private function _buildTemplate($assoc) {
            if (array_key_exists("template", $assoc)) {
                $templateDir = $assoc["template"];
                $templateDir = $this->_buildPath($templateDir);
                $this->loadFromFile($templateDir);
            } else if (array_key_exists("view", $assoc)) {
                $templateConfig = $assoc["view"];
                $this->load($templateConfig);
            }
        }
        private function _buildPath($path) {
            if ($path[0] != "/") {
                $path = $this->_baseDir.$path;
            } else {
                $path = substr($path, 1);
            }
            return $path;
        }
        private function _insertChildren($templateConfig) {
            if (array_key_exists("children", $templateConfig)) {
                foreach ($templateConfig["children"] as $child) {
                    $childTemplate     = new Template($this->_assoc, $this->_resourceManager, $this->_outputEscaper, false);
                    if (array_key_exists("view", $child)) {
                        $childTemplate->load($child["view"]);
                        $childDiv          = $child["id"];
                        $this->_htmlString = $this->_resolveVars($this->_htmlString, [$childDiv => $childTemplate->_htmlString]);
                    } else if (array_key_exists("template", $child)) {
                        // throw new \Exception("Not implemented!!!!", 1);
                        $child["base_dir"] = $this->_baseDir;
                        $childTemplate     = new Template($child, null, $this->_outputEscaper);
                        $childDiv          = $child["id"];
                        $this->_htmlString = $this->_resolveVars($this->_htmlString, [$childDiv => $childTemplate->_htmlString]);
                        //$this->insertHtmlAtId($childTemplate, $childDiv, false);
                    }
                }
            }
        }
    }
}