Packages

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Config
      • Adapters
      • Extensions
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Diagnostics
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
      • PhpGenerator
  • NetteModule
  • None
  • PHP

Classes

  • CacheMacro
  • CoreMacros
  • FormMacros
  • MacroSet
  • UIMacros
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (http://nette.org)
  5:  *
  6:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  7:  *
  8:  * For the full copyright and license information, please view
  9:  * the file license.txt that was distributed with this source code.
 10:  * @package Nette\Latte\Macros
 11:  */
 12: 
 13: 
 14: 
 15: /**
 16:  * Macros for UI.
 17:  *
 18:  * - {link destination ...} control link
 19:  * - {plink destination ...} presenter link
 20:  * - {snippet ?} ... {/snippet ?} control snippet
 21:  * - {contentType ...} HTTP Content-Type header
 22:  * - {status ...} HTTP status
 23:  *
 24:  * @author     David Grudl
 25:  * @package Nette\Latte\Macros
 26:  */
 27: class UIMacros extends MacroSet
 28: {
 29:     /** @internal PHP identifier */
 30:     const RE_IDENTIFIER = '[_a-zA-Z\x7F-\xFF][_a-zA-Z0-9\x7F-\xFF]*';
 31: 
 32:     /** @var array */
 33:     private $namedBlocks = array();
 34: 
 35:     /** @var bool */
 36:     private $extends;
 37: 
 38: 
 39: 
 40:     public static function install(LatteCompiler $compiler)
 41:     {
 42:         $me = new self($compiler);
 43:         $me->addMacro('include', array($me, 'macroInclude'));
 44:         $me->addMacro('includeblock', array($me, 'macroIncludeBlock'));
 45:         $me->addMacro('extends', array($me, 'macroExtends'));
 46:         $me->addMacro('layout', array($me, 'macroExtends'));
 47:         $me->addMacro('block', array($me, 'macroBlock'), array($me, 'macroBlockEnd'));
 48:         $me->addMacro('define', array($me, 'macroBlock'), array($me, 'macroBlockEnd'));
 49:         $me->addMacro('snippet', array($me, 'macroBlock'), array($me, 'macroBlockEnd'));
 50:         $me->addMacro('ifset', array($me, 'macroIfset'), 'endif');
 51: 
 52:         $me->addMacro('widget', array($me, 'macroControl')); // deprecated - use control
 53:         $me->addMacro('control', array($me, 'macroControl'));
 54: 
 55:         $me->addMacro('href', NULL, NULL, create_function('MacroNode $node, $writer', 'extract(NCFix::$vars['.NCFix::uses(array('me'=>$me)).'], EXTR_REFS);
 56:             return \' ?> href="<?php \' . $me->macroLink($node, $writer) . \' ?>"<?php \';
 57:         '));
 58:         $me->addMacro('plink', array($me, 'macroLink'));
 59:         $me->addMacro('link', array($me, 'macroLink'));
 60:         $me->addMacro('ifCurrent', array($me, 'macroIfCurrent'), 'endif'); // deprecated; use n:class="$presenter->linkCurrent ? ..."
 61: 
 62:         $me->addMacro('contentType', array($me, 'macroContentType'));
 63:         $me->addMacro('status', array($me, 'macroStatus'));
 64:     }
 65: 
 66: 
 67: 
 68:     /**
 69:      * Initializes before template parsing.
 70:      * @return void
 71:      */
 72:     public function initialize()
 73:     {
 74:         $this->namedBlocks = array();
 75:         $this->extends = NULL;
 76:     }
 77: 
 78: 
 79: 
 80:     /**
 81:      * Finishes template parsing.
 82:      * @return array(prolog, epilog)
 83:      */
 84:     public function finalize()
 85:     {
 86:         // try close last block
 87:         try {
 88:             $this->getCompiler()->writeMacro('/block');
 89:         } catch (CompileException $e) {
 90:         }
 91: 
 92:         $epilog = $prolog = array();
 93: 
 94:         if ($this->namedBlocks) {
 95:             foreach ($this->namedBlocks as $name => $code) {
 96:                 $func = '_lb' . substr(md5($this->getCompiler()->getTemplateId() . $name), 0, 10) . '_' . preg_replace('#[^a-z0-9_]#i', '_', $name);
 97:                 $snippet = $name[0] === '_';
 98:                 $prolog[] = "//\n// block $name\n//\n"
 99:                     . "if (!function_exists(\$_l->blocks[" . var_export($name, TRUE) . "][] = '$func')) { "
100:                     . "function $func(\$_l, \$_args) { "
101:                     . (PHP_VERSION_ID > 50208 ? 'extract($_args)' : 'foreach ($_args as $__k => $__v) $$__k = $__v') // PHP bug #46873
102:                     . ($snippet ? '; $_control->validateControl(' . var_export(substr($name, 1), TRUE) . ')' : '')
103:                     . "\n?>$code<?php\n}}";
104:             }
105:             $prolog[] = "//\n// end of blocks\n//";
106:         }
107: 
108:         if ($this->namedBlocks || $this->extends) {
109:             $prolog[] = "// template extending and snippets support";
110: 
111:             $prolog[] = '$_l->extends = '
112:                 . ($this->extends ? $this->extends : 'empty($template->_extended) && isset($_control) && $_control instanceof Presenter ? $_control->findLayoutTemplateFile() : NULL')
113:                 . '; $template->_extended = $_extended = TRUE;';
114: 
115:             $prolog[] = '
116: if ($_l->extends) {
117:     ' . ($this->namedBlocks ? 'ob_start();' : 'return CoreMacros::includeTemplate($_l->extends, get_defined_vars(), $template)->render();') . '
118: 
119: } elseif (!empty($_control->snippetMode)) {
120:     return UIMacros::renderSnippets($_control, $_l, get_defined_vars());
121: }';
122:         } else {
123:             $prolog[] = '
124: // snippets support
125: if (!empty($_control->snippetMode)) {
126:     return UIMacros::renderSnippets($_control, $_l, get_defined_vars());
127: }';
128:         }
129: 
130:         return array(implode("\n\n", $prolog), implode("\n", $epilog));
131:     }
132: 
133: 
134: 
135:     /********************* macros ****************d*g**/
136: 
137: 
138: 
139:     /**
140:      * {include #block}
141:      */
142:     public function macroInclude(MacroNode $node, $writer)
143:     {
144:         $destination = $node->tokenizer->fetchWord(); // destination [,] [params]
145:         if (substr($destination, 0, 1) !== '#') {
146:             return FALSE;
147:         }
148: 
149:         $destination = ltrim($destination, '#');
150:         if (!Strings::match($destination, '#^\$?' . self::RE_IDENTIFIER . '$#')) {
151:             throw new CompileException("Included block name must be alphanumeric string, '$destination' given.");
152:         }
153: 
154:         $parent = $destination === 'parent';
155:         if ($destination === 'parent' || $destination === 'this') {
156:             $item = $node->parentNode;
157:             for ($item = $node->parentNode; $item && $item->name !== 'block' && !isset($item->data->name); $item = $item->parentNode);
158:             if (!$item) {
159:                 throw new CompileException("Cannot include $destination block outside of any block.");
160:             }
161:             $destination = $item->data->name;
162:         }
163: 
164:         $name = $destination[0] === '$' ? $destination : var_export($destination, TRUE);
165:         if (isset($this->namedBlocks[$destination]) && !$parent) {
166:             $cmd = "call_user_func(reset(\$_l->blocks[$name]), \$_l, %node.array? + get_defined_vars())";
167:         } else {
168:             $cmd = 'UIMacros::callBlock' . ($parent ? 'Parent' : '') . "(\$_l, $name, %node.array? + " . ($parent ? 'get_defined_vars' : '$template->getParameters') . '())';
169:         }
170: 
171:         if ($node->modifiers) {
172:             return $writer->write("ob_start(); $cmd; echo %modify(ob_get_clean())");
173:         } else {
174:             return $writer->write($cmd);
175:         }
176:     }
177: 
178: 
179: 
180:     /**
181:      * {includeblock "file"}
182:      */
183:     public function macroIncludeBlock(MacroNode $node, $writer)
184:     {
185:         return $writer->write('CoreMacros::includeTemplate(%node.word, %node.array? + get_defined_vars(), $_l->templates[%var])->render()',
186:             $this->getCompiler()->getTemplateId());
187:     }
188: 
189: 
190: 
191:     /**
192:      * {extends auto | none | $var | "file"}
193:      */
194:     public function macroExtends(MacroNode $node, $writer)
195:     {
196:         if (!$node->args) {
197:             throw new CompileException("Missing destination in {extends}");
198:         }
199:         if (!empty($node->parentNode)) {
200:             throw new CompileException("{extends} must be placed outside any macro.");
201:         }
202:         if ($this->extends !== NULL) {
203:             throw new CompileException("Multiple {extends} declarations are not allowed.");
204:         }
205:         if ($node->args === 'none') {
206:             $this->extends = 'FALSE';
207:         } elseif ($node->args === 'auto') {
208:             $this->extends = '$_presenter->findLayoutTemplateFile()';
209:         } else {
210:             $this->extends = $writer->write('%node.word%node.args');
211:         }
212:         return;
213:     }
214: 
215: 
216: 
217:     /**
218:      * {block [[#]name]}
219:      * {snippet [name [,]] [tag]}
220:      * {define [#]name}
221:      */
222:     public function macroBlock(MacroNode $node, $writer)
223:     {
224:         $name = $node->tokenizer->fetchWord();
225: 
226:         if ($node->name === 'block' && $name === FALSE) { // anonymous block
227:             return $node->modifiers === '' ? '' : 'ob_start()';
228:         }
229: 
230:         $node->data->name = $name = ltrim($name, '#');
231:         if ($name == NULL) {
232:             if ($node->name !== 'snippet') {
233:                 throw new CompileException("Missing block name.");
234:             }
235: 
236:         } elseif (!Strings::match($name, '#^' . self::RE_IDENTIFIER . '$#')) { // dynamic blok/snippet
237:             if ($node->name === 'snippet') {
238:                 for ($parent = $node->parentNode; $parent && $parent->name !== 'snippet'; $parent = $parent->parentNode);
239:                 if (!$parent) {
240:                     throw new CompileException("Dynamic snippets are allowed only inside static snippet.");
241:                 }
242:                 $parent->data->dynamic = TRUE;
243:                 $node->data->leave = TRUE;
244:                 $node->closingCode = "<?php \$_dynSnippets[\$_dynSnippetId] = ob_get_flush() ?>";
245: 
246:                 if ($node->htmlNode) {
247:                     $node->attrCode = $writer->write("<?php echo ' id=\"' . (\$_dynSnippetId = \$_control->getSnippetId({$writer->formatWord($name)})) . '\"' ?>");
248:                     return $writer->write('ob_start()');
249:                 }
250:                 $tag = trim($node->tokenizer->fetchWord(), '<>');
251:                 $tag = $tag ? $tag : 'div';
252:                 $node->closingCode .= "\n</$tag>";
253:                 return $writer->write("?>\n<$tag id=\"<?php echo \$_dynSnippetId = \$_control->getSnippetId({$writer->formatWord($name)}) ?>\"><?php ob_start()");
254: 
255:             } else {
256:                 $node->data->leave = TRUE;
257:                 $fname = $writer->formatWord($name);
258:                 $node->closingCode = "<?php }} call_user_func(reset(\$_l->blocks[$fname]), \$_l, get_defined_vars()) ?>";
259:                 $func = '_lb' . substr(md5($this->getCompiler()->getTemplateId() . $name), 0, 10) . '_' . preg_replace('#[^a-z0-9_]#i', '_', $name);
260:                 return "//\n// block $name\n//\n"
261:                     . "if (!function_exists(\$_l->blocks[$fname][] = '$func')) { "
262:                     . "function $func(\$_l, \$_args) { "
263:                     . (PHP_VERSION_ID > 50208 ? 'extract($_args)' : 'foreach ($_args as $__k => $__v) $$__k = $__v'); // PHP bug #46873
264:             }
265:         }
266: 
267:         // static blok/snippet
268:         if ($node->name === 'snippet') {
269:             $node->data->name = $name = '_' . $name;
270:         }
271:         if (isset($this->namedBlocks[$name])) {
272:             throw new CompileException("Cannot redeclare static block '$name'");
273:         }
274:         $prolog = $this->namedBlocks ? '' : "if (\$_l->extends) { ob_end_clean(); return CoreMacros::includeTemplate(\$_l->extends, get_defined_vars(), \$template)->render(); }\n";
275:         $top = empty($node->parentNode);
276:         $this->namedBlocks[$name] = TRUE;
277: 
278:         $include = 'call_user_func(reset($_l->blocks[%var]), $_l, ' . ($node->name === 'snippet' ? '$template->getParameters()' : 'get_defined_vars()') . ')';
279:         if ($node->modifiers) {
280:             $include = "ob_start(); $include; echo %modify(ob_get_clean())";
281:         }
282: 
283:         if ($node->name === 'snippet') {
284:             if ($node->htmlNode) {
285:                 $node->attrCode = $writer->write('<?php echo \' id="\' . $_control->getSnippetId(%var) . \'"\' ?>', (string) substr($name, 1));
286:                 return $writer->write($prolog . $include, $name);
287:             }
288:             $tag = trim($node->tokenizer->fetchWord(), '<>');
289:             $tag = $tag ? $tag : 'div';
290:             return $writer->write("$prolog ?>\n<$tag id=\"<?php echo \$_control->getSnippetId(%var) ?>\"><?php $include ?>\n</$tag><?php ",
291:                 (string) substr($name, 1), $name
292:             );
293: 
294:         } elseif ($node->name === 'define') {
295:             return $prolog;
296: 
297:         } else {
298:             return $writer->write($prolog . $include, $name);
299:         }
300:     }
301: 
302: 
303: 
304:     /**
305:      * {/block}
306:      * {/snippet}
307:      * {/define}
308:      */
309:     public function macroBlockEnd(MacroNode $node, $writer)
310:     {
311:         if (isset($node->data->name)) { // block, snippet, define
312:             if ($node->name === 'snippet' && isset($node->htmlNode->macroAttrs['snippet']) // n:snippet -> n:inner-snippet
313:                 && preg_match("#^(.*? n:\w+>\n?)(.*?)([ \t]*<[^<]+)$#sD", $node->content, $m))
314:             {
315:                 $node->openingCode = $m[1] . $node->openingCode;
316:                 $node->content = $m[2];
317:                 $node->closingCode .= $m[3];
318:             }
319: 
320:             if (empty($node->data->leave)) {
321:                 if (!empty($node->data->dynamic)) {
322:                     $node->content .= '<?php if (isset($_dynSnippets)) return $_dynSnippets; ?>';
323:                 }
324:                 preg_match("#^(\n)?(.*?)([ \t]*)$#sD", $node->content, $m);
325:                 $this->namedBlocks[$node->data->name] = $m[2];
326:                 $node->content = $m[1] . $node->openingCode . "\n" . $m[3];
327:                 $node->openingCode = "<?php ?>";
328:             }
329: 
330:         } elseif ($node->modifiers) { // anonymous block with modifier
331:             return $writer->write('echo %modify(ob_get_clean())');
332:         }
333:     }
334: 
335: 
336: 
337:     /**
338:      * {ifset #block}
339:      */
340:     public function macroIfset(MacroNode $node, $writer)
341:     {
342:         if (!Strings::contains($node->args, '#')) {
343:             return FALSE;
344:         }
345:         $list = array();
346:         while (($name = $node->tokenizer->fetchWord()) !== FALSE) {
347:             $list[] = $name[0] === '#' ? '$_l->blocks["' . substr($name, 1) . '"]' : $name;
348:         }
349:         return 'if (isset(' . implode(', ', $list) . ')):';
350:     }
351: 
352: 
353: 
354:     /**
355:      * {control name[:method] [params]}
356:      */
357:     public function macroControl(MacroNode $node, $writer)
358:     {
359:         $pair = $node->tokenizer->fetchWord();
360:         if ($pair === FALSE) {
361:             throw new CompileException("Missing control name in {control}");
362:         }
363:         $pair = explode(':', $pair, 2);
364:         $name = $writer->formatWord($pair[0]);
365:         $method = isset($pair[1]) ? ucfirst($pair[1]) : '';
366:         $method = Strings::match($method, '#^(' . self::RE_IDENTIFIER . '|)$#') ? "render$method" : "{\"render$method\"}";
367:         $param = $writer->formatArray();
368:         if (!Strings::contains($node->args, '=>')) {
369:             $param = substr($param, 6, -1); // removes array()
370:         }
371:         return ($name[0] === '$' ? "if (is_object($name)) \$_ctrl = $name; else " : '')
372:             . '$_ctrl = $_control->getComponent(' . $name . '); '
373:             . 'if ($_ctrl instanceof IRenderable) $_ctrl->validateControl(); '
374:             . "\$_ctrl->$method($param)";
375:     }
376: 
377: 
378: 
379:     /**
380:      * {link destination [,] [params]}
381:      * {plink destination [,] [params]}
382:      * n:href="destination [,] [params]"
383:      */
384:     public function macroLink(MacroNode $node, $writer)
385:     {
386:         return $writer->write('echo %escape(' . ($node->name === 'plink' ? '$_presenter' : '$_control') . '->link(%node.word, %node.array?))');
387:     }
388: 
389: 
390: 
391:     /**
392:      * {ifCurrent destination [,] [params]}
393:      */
394:     public function macroIfCurrent(MacroNode $node, $writer)
395:     {
396:         return $writer->write(($node->args ? 'try { $_presenter->link(%node.word, %node.array?); } catch (InvalidLinkException $e) {}' : '')
397:             . '; if ($_presenter->getLastCreatedRequestFlag("current")):');
398:     }
399: 
400: 
401: 
402:     /**
403:      * {contentType ...}
404:      */
405:     public function macroContentType(MacroNode $node, $writer)
406:     {
407:         if (Strings::contains($node->args, 'xhtml')) {
408:             $this->getCompiler()->setContentType(LatteCompiler::CONTENT_XHTML);
409: 
410:         } elseif (Strings::contains($node->args, 'html')) {
411:             $this->getCompiler()->setContentType(LatteCompiler::CONTENT_HTML);
412: 
413:         } elseif (Strings::contains($node->args, 'xml')) {
414:             $this->getCompiler()->setContentType(LatteCompiler::CONTENT_XML);
415: 
416:         } elseif (Strings::contains($node->args, 'javascript')) {
417:             $this->getCompiler()->setContentType(LatteCompiler::CONTENT_JS);
418: 
419:         } elseif (Strings::contains($node->args, 'css')) {
420:             $this->getCompiler()->setContentType(LatteCompiler::CONTENT_CSS);
421: 
422:         } elseif (Strings::contains($node->args, 'calendar')) {
423:             $this->getCompiler()->setContentType(LatteCompiler::CONTENT_ICAL);
424: 
425:         } else {
426:             $this->getCompiler()->setContentType(LatteCompiler::CONTENT_TEXT);
427:         }
428: 
429:         // temporary solution
430:         if (Strings::contains($node->args, '/')) {
431:             return $writer->write('$netteHttpResponse->setHeader("Content-Type", %var)', $node->args);
432:         }
433:     }
434: 
435: 
436: 
437:     /**
438:      * {status ...}
439:      */
440:     public function macroStatus(MacroNode $node, $writer)
441:     {
442:         return $writer->write((substr($node->args, -1) === '?' ? 'if (!$netteHttpResponse->isSent()) ' : '') .
443:             '$netteHttpResponse->setCode(%var)', (int) $node->args
444:         );
445:     }
446: 
447: 
448: 
449:     /********************* run-time writers ****************d*g**/
450: 
451: 
452: 
453:     /**
454:      * Calls block.
455:      * @param  stdClass
456:      * @param  string
457:      * @param  array
458:      * @return void
459:      */
460:     public static function callBlock($context, $name, $params)
461:     {
462:         if (empty($context->blocks[$name])) {
463:             throw new InvalidStateException("Cannot include undefined block '$name'.");
464:         }
465:         $block = reset($context->blocks[$name]);
466:         $block($context, $params);
467:     }
468: 
469: 
470: 
471:     /**
472:      * Calls parent block.
473:      * @param  stdClass
474:      * @param  string
475:      * @param  array
476:      * @return void
477:      */
478:     public static function callBlockParent($context, $name, $params)
479:     {
480:         if (empty($context->blocks[$name]) || ($block = next($context->blocks[$name])) === FALSE) {
481:             throw new InvalidStateException("Cannot include undefined parent block '$name'.");
482:         }
483:         $block($context, $params);
484:     }
485: 
486: 
487: 
488:     public static function renderSnippets($control, $local, $params)
489:     {
490:         $control->snippetMode = FALSE;
491:         $payload = $control->getPresenter()->getPayload();
492:         if (isset($local->blocks)) {
493:             foreach ($local->blocks as $name => $function) {
494:                 if ($name[0] !== '_' || !$control->isControlInvalid(substr($name, 1))) {
495:                     continue;
496:                 }
497:                 ob_start();
498:                 $function = reset($function);
499:                 $snippets = $function($local, $params);
500:                 $payload->snippets[$id = $control->getSnippetId(substr($name, 1))] = ob_get_clean();
501:                 if ($snippets) {
502:                     $payload->snippets += $snippets;
503:                     unset($payload->snippets[$id]);
504:             }
505:         }
506:         }
507:         if ($control instanceof Control) {
508:             foreach ($control->getComponents(FALSE, 'Control') as $child) {
509:                 if ($child->isControlInvalid()) {
510:                     $child->snippetMode = TRUE;
511:                     $child->render();
512:                     $child->snippetMode = FALSE;
513:                 }
514:             }
515:         }
516:     }
517: 
518: }
519: 
Nette Framework 2.0.0 (for PHP 5.2, un-prefixed) API API documentation generated by ApiGen 2.7.0