1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Latte\Macros;
9:
10: use Nette;
11: use Nette\Latte;
12: use Nette\Latte\MacroNode;
13: use Nette\Latte\PhpWriter;
14: use Nette\Latte\CompileException;
15: use Nette\Forms\Form;
16:
17:
18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28:
29: class FormMacros extends MacroSet
30: {
31:
32: public static function install(Latte\Compiler $compiler)
33: {
34: $me = new static($compiler);
35: $me->addMacro('form', array($me, 'macroForm'), 'Nette\Latte\Macros\FormMacros::renderFormEnd($_form)');
36: $me->addMacro('formContainer', array($me, 'macroFormContainer'), '$formContainer = $_form = array_pop($_formStack)');
37: $me->addMacro('label', array($me, 'macroLabel'), array($me, 'macroLabelEnd'));
38: $me->addMacro('input', array($me, 'macroInput'), NULL, array($me, 'macroInputAttr'));
39: $me->addMacro('name', array($me, 'macroName'), array($me, 'macroNameEnd'), array($me, 'macroNameAttr'));
40: $me->addMacro('inputError', array($me, 'macroInputError'));
41: }
42:
43:
44:
45:
46:
47: 48: 49:
50: public function macroForm(MacroNode $node, PhpWriter $writer)
51: {
52: if ($node->htmlNode && strtolower($node->htmlNode->name) === 'form') {
53: throw new CompileException('Did you mean <form n:name=...> ?');
54: }
55: $name = $node->tokenizer->fetchWord();
56: if ($name === FALSE) {
57: throw new CompileException("Missing form name in {{$node->name}}.");
58: }
59: $node->tokenizer->reset();
60: return $writer->write(
61: 'Nette\Latte\Macros\FormMacros::renderFormBegin($form = $_form = '
62: . ($name[0] === '$' ? 'is_object(%node.word) ? %node.word : ' : '')
63: . '$_control[%node.word], %node.array)'
64: );
65: }
66:
67:
68: 69: 70:
71: public function macroFormContainer(MacroNode $node, PhpWriter $writer)
72: {
73: $name = $node->tokenizer->fetchWord();
74: if ($name === FALSE) {
75: throw new CompileException("Missing form name in {{$node->name}}.");
76: }
77: $node->tokenizer->reset();
78: return $writer->write(
79: '$_formStack[] = $_form; $formContainer = $_form = ' . ($name[0] === '$' ? 'is_object(%node.word) ? %node.word : ' : '') . '$_form[%node.word]'
80: );
81: }
82:
83:
84: 85: 86:
87: public function macroLabel(MacroNode $node, PhpWriter $writer)
88: {
89: $words = $node->tokenizer->fetchWords();
90: if (!$words) {
91: throw new CompileException("Missing name in {{$node->name}}.");
92: }
93: $name = array_shift($words);
94: return $writer->write(
95: ($name[0] === '$' ? '$_input = is_object(%0.word) ? %0.word : $_form[%0.word]; if ($_label = $_input' : 'if ($_label = $_form[%0.word]')
96: . '->%1.raw) echo $_label'
97: . ($node->tokenizer->isNext() ? '->addAttributes(%node.array)' : ''),
98: $name,
99: $words ? ('getLabelPart(' . implode(', ', array_map(array($writer, 'formatWord'), $words)) . ')') : 'getLabel()'
100: );
101: }
102:
103:
104: 105: 106:
107: public function macroLabelEnd(MacroNode $node, PhpWriter $writer)
108: {
109: if ($node->content != NULL) {
110: $node->openingCode = rtrim($node->openingCode, '?> ') . '->startTag() ?>';
111: return $writer->write('if ($_label) echo $_label->endTag()');
112: }
113: }
114:
115:
116: 117: 118:
119: public function macroInput(MacroNode $node, PhpWriter $writer)
120: {
121: $words = $node->tokenizer->fetchWords();
122: if (!$words) {
123: throw new CompileException("Missing name in {{$node->name}}.");
124: }
125: $name = array_shift($words);
126: return $writer->write(
127: ($name[0] === '$' ? '$_input = is_object(%0.word) ? %0.word : $_form[%0.word]; echo $_input' : 'echo $_form[%0.word]')
128: . '->%1.raw'
129: . ($node->tokenizer->isNext() ? '->addAttributes(%node.array)' : ''),
130: $name,
131: $words ? 'getControlPart(' . implode(', ', array_map(array($writer, 'formatWord'), $words)) . ')' : 'getControl()'
132: );
133: }
134:
135:
136: 137: 138:
139: public function macroInputAttr(MacroNode $node, PhpWriter $writer)
140: {
141: if (strtolower($node->htmlNode->name) === 'input') {
142: return $this->macroNameAttr($node, $writer);
143: } else {
144: throw new CompileException("Use n:name instead of n:input.");
145: }
146: }
147:
148:
149: 150: 151:
152: public function macroNameAttr(MacroNode $node, PhpWriter $writer)
153: {
154: $words = $node->tokenizer->fetchWords();
155: if (!$words) {
156: throw new CompileException("Missing name in n:{$node->name}.");
157: }
158: $name = array_shift($words);
159: $tagName = strtolower($node->htmlNode->name);
160: $node->isEmpty = !in_array($tagName, array('form', 'select', 'textarea'), TRUE);
161:
162: if ($tagName === 'form') {
163: return $writer->write(
164: 'Nette\Latte\Macros\FormMacros::renderFormBegin($form = $_form = '
165: . ($name[0] === '$' ? 'is_object(%0.word) ? %0.word : ' : '')
166: . '$_control[%0.word], %1.var, FALSE)',
167: $name,
168: array_fill_keys(array_keys($node->htmlNode->attrs), NULL)
169: );
170: } else {
171: $method = $tagName === 'label' ? 'getLabel' : 'getControl';
172: return $writer->write(
173: '$_input = ' . ($name[0] === '$' ? 'is_object(%0.word) ? %0.word : ' : '')
174: . '$_form[%0.word]; echo $_input->%1.raw'
175: . ($node->htmlNode->attrs ? '->addAttributes(%2.var)' : '') . '->attributes()',
176: $name,
177: $words
178: ? $method . 'Part(' . implode(', ', array_map(array($writer, 'formatWord'), $words)) . ')'
179: : "{method_exists(\$_input, '{$method}Part')?'{$method}Part':'{$method}'}()",
180: array_fill_keys(array_keys($node->htmlNode->attrs), NULL)
181: );
182: }
183: }
184:
185:
186: public function macroName(MacroNode $node, PhpWriter $writer)
187: {
188: if (!$node->htmlNode) {
189: throw new CompileException("Unknown macro {{$node->name}}, use n:{$node->name} attribute.");
190: } elseif ($node->prefix !== MacroNode::PREFIX_NONE) {
191: throw new CompileException("Unknown attribute n:{$node->prefix}-{$node->name}, use n:{$node->name} attribute.");
192: }
193: }
194:
195:
196: public function macroNameEnd(MacroNode $node, PhpWriter $writer)
197: {
198: preg_match('#^(.*? n:\w+>)(.*)(<[^?].*)\z#s', $node->content, $parts);
199: if (strtolower($node->htmlNode->name) === 'form') {
200: $node->content = $parts[1] . $parts[2] . '<?php Nette\Latte\Macros\FormMacros::renderFormEnd($_form, FALSE) ?>' . $parts[3];
201: } else {
202: $node->content = $parts[1] . '<?php echo $_input->getControl()->getHtml() ?>' . $parts[3];
203: }
204: }
205:
206:
207: 208: 209:
210: public function macroInputError(MacroNode $node, PhpWriter $writer)
211: {
212: $name = $node->tokenizer->fetchWord();
213: if (!$name) {
214: return $writer->write('echo %escape($_input->getError())');
215: } elseif ($name[0] === '$') {
216: return $writer->write('$_input = is_object(%0.word) ? %0.word : $_form[%0.word]; echo %escape($_input->getError())', $name);
217: } else {
218: return $writer->write('echo %escape($_form[%0.word]->getError())', $name);
219: }
220: }
221:
222:
223:
224:
225:
226: 227: 228: 229:
230: public static function renderFormBegin(Form $form, array $attrs, $withTags = TRUE)
231: {
232: foreach ($form->getControls() as $control) {
233: $control->setOption('rendered', FALSE);
234: }
235: $el = $form->getElementPrototype();
236: $el->action = (string) $el->action;
237: $el = clone $el;
238: if (strcasecmp($form->getMethod(), 'get') === 0) {
239: $el->action = preg_replace('~\?[^#]*~', '', $el->action, 1);
240: }
241: $el->addAttributes($attrs);
242: echo $withTags ? $el->startTag() : $el->attributes();
243: }
244:
245:
246: 247: 248: 249:
250: public static function renderFormEnd(Form $form, $withTags = TRUE)
251: {
252: $s = '';
253: if (strcasecmp($form->getMethod(), 'get') === 0) {
254: foreach (preg_split('#[;&]#', parse_url($form->getElementPrototype()->action, PHP_URL_QUERY), NULL, PREG_SPLIT_NO_EMPTY) as $param) {
255: $parts = explode('=', $param, 2);
256: $name = urldecode($parts[0]);
257: if (!isset($form[$name])) {
258: $s .= Nette\Utils\Html::el('input', array('type' => 'hidden', 'name' => $name, 'value' => urldecode($parts[1])));
259: }
260: }
261: }
262:
263: foreach ($form->getComponents(TRUE, 'Nette\Forms\Controls\HiddenField') as $control) {
264: if (!$control->getOption('rendered')) {
265: $s .= $control->getControl();
266: }
267: }
268:
269: if (iterator_count($form->getComponents(TRUE, 'Nette\Forms\Controls\TextInput')) < 2) {
270: $s .= '<!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->';
271: }
272:
273: echo ($s ? "<div>$s</div>\n" : '') . ($withTags ? $form->getElementPrototype()->endTag() . "\n" : '');
274: }
275:
276: }
277: