diff --git a/tests/webfiori/test/ui/LoadTemplateTest.php b/tests/webfiori/test/ui/LoadTemplateTest.php index 630602b..8f3fa15 100644 --- a/tests/webfiori/test/ui/LoadTemplateTest.php +++ b/tests/webfiori/test/ui/LoadTemplateTest.php @@ -17,7 +17,8 @@ class LoadTemplateTest extends TestCase { * @test */ public function test00() { - $this->expectException('Exception'); + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Empty template path'); $compiler = new TemplateCompiler(''); } /** @@ -112,6 +113,28 @@ public function test07() { $node = $compiler->getCompiled(); $this->assertEquals("
\n No posts.\n
", $node->toHTML()); } + /** + * @test + */ + public function test08() { + $compiler = new TemplateCompiler('template.php', [ + 'message' => 'Good Job!', + 'posts' => [ + 'One', + 'Two', + 'Three' + ]]); + $this->assertEquals("
" + . "" + . "
\n" + . " Good Job!" + . "
" + . "
", $compiler->getCompiled()->toHTML()); + } /** * @test */ @@ -179,7 +202,7 @@ public function testHeadTemplate04() { */ public function testAddChildFromTemplate00() { $node = new HTMLNode(); - $node->component(self::TEST_TEMPLATES_PATH.'component-00.html', [ + $node->include(self::TEST_TEMPLATES_PATH.'component-00.html', [ 'base' => 'https://example.com', 'home-label' => 'Home Page', 'about-label' => 'About Us', diff --git a/tests/webfiori/test/ui/sub-component.php b/tests/webfiori/test/ui/sub-component.php new file mode 100644 index 0000000..1b24761 --- /dev/null +++ b/tests/webfiori/test/ui/sub-component.php @@ -0,0 +1,3 @@ +
+ +
diff --git a/tests/webfiori/test/ui/template.php b/tests/webfiori/test/ui/template.php new file mode 100644 index 0000000..18ccbec --- /dev/null +++ b/tests/webfiori/test/ui/template.php @@ -0,0 +1,18 @@ +
+ + + + +
diff --git a/webfiori/ui/CodeSnippet.php b/webfiori/ui/CodeSnippet.php index 581fa96..f81db5e 100644 --- a/webfiori/ui/CodeSnippet.php +++ b/webfiori/ui/CodeSnippet.php @@ -37,7 +37,6 @@ * @version 1.0.3 */ class CodeSnippet extends HTMLNode { - private $code; private $codeDisplay; @@ -250,12 +249,12 @@ private function addLineHelper() { $span = new HTMLNode('span'); $span->setClassName('line-number'); $span->setAttribute('style', - 'font-weight: bold;' - .'display: block;' - .'font-family: monospace;' - .'border-right: 1px dotted white;' - .'padding-right: 4px;' - .'color: #378e80;'); + 'font-weight: bold;' + .'display: block;' + .'font-family: monospace;' + .'border-right: 1px dotted white;' + .'padding-right: 4px;' + .'color: #378e80;'); $span->addTextNode($this->currentLineNum); $this->currentLineNum++; $this->lineNumsNode->addChild($span); diff --git a/webfiori/ui/HTMLNode.php b/webfiori/ui/HTMLNode.php index e92955a..ef064e8 100644 --- a/webfiori/ui/HTMLNode.php +++ b/webfiori/ui/HTMLNode.php @@ -698,9 +698,9 @@ public function comment(string $txt) { * @throws TemplateNotFoundException If the file that the component is * loaded from does not exist. * - * + * @deprecated Use HTMLNode::include() */ - public function component(string $path, array $slotsValues) { + public function component(string $path, array $slotsValues = []) { $loaded = self::fromFile($path, $slotsValues); if (gettype($loaded) == 'array') { @@ -719,7 +719,6 @@ public function component(string $path, array $slotsValues) { * * @return int The number of child nodes attached to the node. * - * */ public function count() : int { return $this->childrenCount(); @@ -825,8 +824,9 @@ public function form(array $attributes = []) : HTMLNode { * * @throws TemplateNotFoundException */ - public static function fromFile(string $absPath, array $slotsOrVars) { + public static function fromFile(string $absPath, array $slotsOrVars = []) { $compiler = new TemplateCompiler($absPath, $slotsOrVars); + return $compiler->getCompiled(); } @@ -1219,6 +1219,33 @@ public function img(array $attributes = []) : HTMLNode { return $this->addChild($img); } + /** + * Loads HTML-like or PHP component and make it a child of current node. + * + * This method can be used to load any component that uses HTML syntax + * into an object and make it a child of the instance at which the method is + * called in. If the component file contains more than one node as a root note, + * all nodes will be added as children. + * + * @param string $path The location of the file that + * will have the HTML component. + * + * @param array $values An array that contains slots values or variables + * to be passed to PHP template. A slot in + * the component is a string which is enclosed between two curly braces (such as {{name}}). + * This array must be associative. The indices of the array are slots names + * and values of the indices are slots values. The values of the slots can be + * also sub-array that contains more values. For example, if we + * have a slot with the name {{ user-name }}, then the array can have the + * index 'user-name' with the value of the slot. + * + * @throws TemplateNotFoundException If the file that the component is + * loaded from does not exist. + * + */ + public function include(string $path, array $values = []) { + $this->component($path, $values); + } /** * Adds new input (<input>, <select> or <textarea>) * element as a child to the body of the node. @@ -1950,7 +1977,6 @@ public function setNodeName(string $name) : bool { * */ public function setStyle(array $cssStyles, bool $override = false) : HTMLNode { - if (!$override) { $styleArr = $this->getStyle(); } else { @@ -2664,8 +2690,7 @@ private function validateAttrNameHelper(string $name) : bool { * * */ - private function validateFormattingOptions(array $FO): array - { + private function validateFormattingOptions(array $FO): array { $defaultFormat = self::DEFAULT_CODE_FORMAT; foreach ($defaultFormat as $key => $value) { diff --git a/webfiori/ui/HeadNode.php b/webfiori/ui/HeadNode.php index f42fc0b..d52be1c 100644 --- a/webfiori/ui/HeadNode.php +++ b/webfiori/ui/HeadNode.php @@ -215,27 +215,6 @@ public function addChild($node, $attrsOrChain = [], bool $chainOnParent = true) } - return $this; - } - /** - * Add multiple CSS resources files. - * - * @param array $files An array that holds paths to CSS files. This also - * can be an associative array. In this case, the indices are paths to files - * and the value of each index is a sub associative array of attributes. - * - * @return HeadNode The method will return the instance at which the method - * is called on. - */ - public function addCSSFiles(array $files) : HeadNode { - foreach ($files as $index => $options) { - if (gettype($index) == 'integer') { - $this->addCSS($options); - } else { - $this->addCSS($index, $options); - } - } - return $this; } /** @@ -306,24 +285,24 @@ public function addCSS(string $href, array $otherAttrs = []) : HeadNode { return $this; } /** - * Add multiple JS resources files. + * Add multiple CSS resources files. * - * @param array $files An array that holds paths to JS files. This also + * @param array $files An array that holds paths to CSS files. This also * can be an associative array. In this case, the indices are paths to files * and the value of each index is a sub associative array of attributes. * * @return HeadNode The method will return the instance at which the method * is called on. */ - public function addJSFiles(array $files) : HeadNode { + public function addCSSFiles(array $files) : HeadNode { foreach ($files as $index => $options) { if (gettype($index) == 'integer') { - $this->addJs($options); + $this->addCSS($options); } else { - $this->addJs($index, $options); + $this->addCSS($index, $options); } } - + return $this; } /** @@ -396,6 +375,27 @@ public function addJs(string $loc, array $otherAttrs = []) : HeadNode { return $this; } + /** + * Add multiple JS resources files. + * + * @param array $files An array that holds paths to JS files. This also + * can be an associative array. In this case, the indices are paths to files + * and the value of each index is a sub associative array of attributes. + * + * @return HeadNode The method will return the instance at which the method + * is called on. + */ + public function addJSFiles(array $files) : HeadNode { + foreach ($files as $index => $options) { + if (gettype($index) == 'integer') { + $this->addJs($options); + } else { + $this->addJs($index, $options); + } + } + + return $this; + } /** * Adds new 'link' node. * Note that if the 'rel' attribute value is 'canonical' or 'alternate', no node will be @@ -451,18 +451,6 @@ public function addLink(string $rel, string $href, array $otherAttrs = []) : Hea return $this; } - /** - * Adds a set of meta tags. - * - * @param array $tags An associative array. The indices of the array - * are the values of the attribute 'name' and the value of the index is - * the value of the attribute 'content'. - */ - public function addMetaTags(array $tags) { - foreach ($tags as $name => $content) { - $this->addMeta($name, $content); - } - } /** * Adds new meta tag. * @@ -514,6 +502,18 @@ public function addMeta(string $name, string $content, bool $override = false) : return $this; } + /** + * Adds a set of meta tags. + * + * @param array $tags An associative array. The indices of the array + * are the values of the attribute 'name' and the value of the index is + * the value of the attribute 'content'. + */ + public function addMetaTags(array $tags) { + foreach ($tags as $name => $content) { + $this->addMeta($name, $content); + } + } /** * Returns a linked list of all alternate nodes that was added to the header. * diff --git a/webfiori/ui/Input.php b/webfiori/ui/Input.php index 2af9d40..a7427c8 100644 --- a/webfiori/ui/Input.php +++ b/webfiori/ui/Input.php @@ -163,8 +163,7 @@ public function addChild($node, $attrsOrChain = [], bool $chainOnParent = true) * * @since 1.0.1 */ - public function addOption(array $options = []): Input - { + public function addOption(array $options = []): Input { if ($this->getNodeName() == 'select' && gettype($options) == 'array' && isset($options['value']) && isset($options['label'])) { $option = new HTMLNode('option'); $option->setAttribute('value', $options['value']); @@ -202,8 +201,7 @@ public function addOption(array $options = []): Input * * @since 1.0.1 */ - public function addOptions(array $arrayOfOpt): Input - { + public function addOptions(array $arrayOfOpt): Input { if (gettype($arrayOfOpt) == 'array') { foreach ($arrayOfOpt as $value => $lblOrOptions) { if (gettype($lblOrOptions) == 'array') { @@ -249,8 +247,7 @@ public function addOptions(array $arrayOfOpt): Input * is called on. * @since 1.0.1 */ - public function addOptionsGroup(array $optionsGroupArr): Input - { + public function addOptionsGroup(array $optionsGroupArr): Input { if ($this->getNodeName() == 'select' && gettype($optionsGroupArr) == 'array' && isset($optionsGroupArr['label']) && isset($optionsGroupArr['options'])) { $optGroup = new HTMLNode('optgroup'); $optGroup->setAttribute('label', $optionsGroupArr['label']); @@ -382,8 +379,7 @@ public function setMin(int $min) : Input { * * @since 1.0 */ - public function setMinLength(int $length): Input - { + public function setMinLength(int $length): Input { if ($length >= 0) { $iType = $this->getType(); @@ -426,8 +422,7 @@ public function setNodeName(string $name) : bool { * @return Input The method will return the instance at which the method * is called on. */ - public function setPlaceholder(string $text = null): Input - { + public function setPlaceholder(string $text = null): Input { if ($text !== null) { $iType = $this->getType(); @@ -491,6 +486,7 @@ private function addOptionsToGroupHelper($optionsGroupArr, $optGroup) { foreach ($optionsGroupArr['options'] as $value => $labelOrOptions) { $o = new HTMLNode('option'); $o->setAttribute('value', $value); + if (gettype($labelOrOptions) == 'array' && isset($labelOrOptions['label'])) { $o->addTextNode($labelOrOptions['label'],false); diff --git a/webfiori/ui/TemplateCompiler.php b/webfiori/ui/TemplateCompiler.php index feeac64..6250a93 100644 --- a/webfiori/ui/TemplateCompiler.php +++ b/webfiori/ui/TemplateCompiler.php @@ -10,6 +10,7 @@ */ namespace webfiori\ui; +use InvalidArgumentException; use webfiori\collections\Queue; use webfiori\ui\exceptions\InvalidNodeNameException; use webfiori\ui\exceptions\TemplateNotFoundException; @@ -50,12 +51,29 @@ class TemplateCompiler { * @throws InvalidNodeNameException */ public function __construct(string $templatePath, array $vars = []) { - if (!file_exists($templatePath)) { - throw new TemplateNotFoundException('No file was found at "'.$templatePath.'".'); + $trimmedPath = trim($templatePath); + + if (strlen($trimmedPath) == 0) { + throw new InvalidArgumentException('Empty template path'); + } + + if (!file_exists($trimmedPath)) { + $possibleLocations = self::getCallingFilesPaths(); + + foreach ($possibleLocations as $dir) { + if (file_exists($dir.$trimmedPath)) { + $trimmedPath = $dir.$trimmedPath; + break; + } + } + + if (!file_exists($trimmedPath)) { + throw new TemplateNotFoundException('No file was found at "'.$trimmedPath.'".'); + } } - $extArr = explode('.', $templatePath); + $extArr = explode('.', $trimmedPath); $this->tType = strtolower($extArr[count($extArr) - 1]); - $this->path = $templatePath; + $this->path = $trimmedPath; $this->rawOutput = ''; $this->compile($vars); } @@ -154,7 +172,6 @@ public static function fromHTMLText(string $text, bool $asHTMLDocObj = true) { } } else { if (count($nodesArr) != 1) { - foreach ($nodesArr as $node) { $asHtmlNode = self::fromHTMLTextHelper00($node); $retVal[] = $asHtmlNode; @@ -169,6 +186,37 @@ public static function fromHTMLText(string $text, bool $asHTMLDocObj = true) { return null; } + /** + * Returns an array that contains directories names of the calling files. + * + * This method is used to extract the pathes of files at which they called + * this method. + * + * @return array An array that contains directories names of the calling files. + */ + public static function getCallingFilesPaths() : array { + $debugTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 15); + $retVal = []; + + foreach ($debugTrace as $traceEntry) { + if (isset($traceEntry['file'])) { + $file = $traceEntry['file']; + $split = explode(DIRECTORY_SEPARATOR, $file); + $withoutFile = array_diff($split, [$split[count($split) - 1]]); + $dir = implode(DIRECTORY_SEPARATOR, $withoutFile); + + if (strlen($dir) != 0) { + $dir .= DIRECTORY_SEPARATOR; + + if (!in_array($dir, $retVal)) { + $retVal[] = $dir; + } + } + } + } + + return $retVal; + } /** * Returns the compiled template. *