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