/[drupal]/contributions/sandbox/frando/fapi4/fapi.classes.inc
ViewVC logotype

Contents of /contributions/sandbox/frando/fapi4/fapi.classes.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.7 - (show annotations) (download) (as text)
Wed Sep 5 13:37:08 2007 UTC (2 years, 2 months ago) by frando
Branch: MAIN
CVS Tags: HEAD
Changes since 1.6: +6 -19 lines
File MIME type: text/x-php
FAPI4 at Froscon: Move all the Iterator and traversing stuff out of the DrupalElement class into a DrupalIterator class. Makes the code much more readable and understandable.
1 <?php
2 /**
3 * BUGGY CONCEPT CODE!
4 *
5 * This is code for a object oriented Forms and Rendering API in Drupal 7.
6 */
7
8 /**
9 * DrupalWidget extends DrupalElement and adds rendering functionality.
10 *
11 * DrupalWidget is to be used for non-input widgets like markup or table.
12 *
13 * Biggest TODO: Integrate with Drupal's theme system. Probably not easy,
14 * but certainly doable. In case of doubt we could just only use regular
15 * theme functions instead of a theme() method.
16 */
17 abstract class DrupalWidget extends DrupalElement {
18 /**
19 * Constructor
20 */
21 public function __construct($properties = array()) {
22 parent::__construct($properties);
23 // We're obviously not yet rendered.
24 $this['rendered'] = FALSE;
25 $this['render'] = TRUE;
26 }
27
28 /**
29 * Render the current element and all children.
30 * This works similar to drupal_render()...
31 */
32 public function render($force = FALSE) {
33 if (!$force && !$this['render']) {
34 return;
35 }
36 if (!$force && $this['rendered']) {
37 return $this['renderedContent'];
38 }
39
40 $this->invoke('preRender');
41
42 if (isset($this['theme']) && is_callable($this['theme'])) {
43 $content = call_user_func_array($this['theme'], array(&$this));
44 }
45 else {
46 $content = $this->theme();
47 }
48
49 $this['rendered'] = TRUE;
50 $this['renderedContent'] = $content;
51
52 $this->invoke('postRender');
53
54 return $this['renderedContent'];
55 }
56
57 /**
58 * Render all children of the current element.
59 * Optionally wrap each children between $prefix and $suffix.
60 */
61 public function renderChildren($prefix = '', $suffix = '') {
62 $output = "";
63 foreach (q($this)->children()->filterByType('DrupalWidget') as $child) {
64 $output .= $prefix . $child->render(FALSE) . $suffix;
65 }
66 return $output;
67 }
68
69 protected function theme() {
70 $output = "<div><span>". $this->toString() ."</span><br />";
71 $output .= $this->renderChildren();
72 $output .= '</div>';
73 return $output;
74 }
75 }
76
77
78 /**
79 * DrupalFormWidget extends DrupalWidget and adds functionality needed for form elements.
80 *
81 */
82 abstract class DrupalFormWidget extends DrupalWidget {
83 public function __construct($properties = array()) {
84 $defaults = array(
85 'attributes' => array(),
86 'has_error' => FALSE,
87 'value' => '',
88 'default_value' => '',
89 );
90 parent::__construct(array_merge($defaults, $properties));
91 }
92
93 public function assignValue() {
94 if ($this['parent_form']['posted'] && $this['post_value']) {
95 $this['value'] = $this['post_value'];
96 }
97 else {
98 $this['value'] = $this['default_value'];
99 }
100 }
101
102 public function setError($message) {
103 $this['error_message'] = $message;
104 $this['has_error'] = TRUE;
105 $this['parent_form']['has_error'] = TRUE;
106 }
107 }
108
109 /**
110 * A form. This is where FAPI will live.
111 *
112 * Each individual form will be a class of its own that extends DrupalForm.
113 */
114 abstract class DrupalForm extends DrupalWidget {
115 public function __construct($properties = array()) {
116 $defaults = array(
117 'form_id' => get_class($this),
118 'method' => 'POST',
119 'has_error' => FALSE,
120 'cache' => TRUE,
121 'posted' => FALSE,
122 'step' => 0,
123 );
124 // Call the parent constructor. This will also invoke 'construct'.
125 parent::__construct(array_merge($defaults, $properties));
126
127 // Add a hidden field with the form_id
128 $this->addChild('form_id', new DrupalHiddenElement(array('default_value' => $this['form_id'])));
129 // Add a submit button. TODO: only do this if there is no submit button so far.
130 $this->addChild('submit', new DrupalSubmitButton());
131 }
132
133 /**
134 * This is called whenever a new descendant element is added to the form (during construction)
135 */
136 protected function descendantAdded($element) {
137 if ($element instanceof DrupalFormWidget) {
138 // We add $this as parent_form to the element
139 $element['parent_form'] = $this;
140 // And set the post_name to the path from $this to the element
141 $element['post_name'] = $element->createPath($this, $element);
142 }
143 }
144
145 public function build() {
146 $this['action'] = $_GET['q'];
147
148 // Check whether the form got posted
149 if (isset($_POST[$this['name'] .'/form_id']) && $_POST[$this['name'] .'/form_id'] == $this['form_id']) {
150 // Assign the POST values to each element. That's the last time that we deal with POST!
151 foreach ($_POST as $path => $value) {
152 $e = $this->findByPath($path);
153 $e['post_value'] = $value;
154 }
155 $this['posted'] = TRUE;
156 }
157
158 q($this)->descendants()->filterByType('DrupalFormWidget')->assignValue();
159
160
161 if ($this['posted']) {
162 $this->processPostedForm();
163 }
164
165 $this->cache();
166 }
167
168 /**
169 * This is called after the form is built, but only if the form got posted.
170 */
171 private function processPostedForm() {
172 drupal_set_message("The form got posted!<br>POST:<pre>" . print_r($_POST, true) ."</pre>");
173
174 $this['has_error'] = FALSE;
175
176 // Validate.
177
178 q($this)->descendants()->filterByType('DrupalFormWidget')->set('has_error', FALSE)->invoke('validate');
179
180 // The form itself can also have a validate function.
181 $this->invoke('validate');
182
183 // If no validation errors, submit.
184 if (!$this['has_error']) {
185 drupal_set_message("No validation error ('error' in a textfield triggers an error). Let's invoke 'submit'.\n");
186 $this['step'] = $this['step'] + 1;
187 $this->invoke('submit');
188 }
189 // Otherwise, don't submit.
190 else {
191 drupal_set_message("Error. See below.\n");
192 }
193 }
194
195 public function cache() {
196 if (!$this['cache']) {
197 return;
198 }
199 // Add a build_id ...
200 if (!isset($this['build_id'])) {
201 $this['build_id'] = md5(mt_rand());
202 $this->addChild('form_build_id', new DrupalHiddenElement(array('default_value' => $this['build_id'])));
203 }
204 cache_set('form_build:'. $this['build_id'], $this);
205 }
206
207 public function redirect($target = NULL) {
208 if (isset($target)) {
209 drupal_goto($target);
210 }
211 elseif (isset($this['redirect'])) {
212 drupal_goto($this['redirect']);
213 }
214 else {
215 drupal_goto($_GET['q']);
216 }
217 }
218
219 public function theme() {
220 $output = '<form action="'. $this['action'] .'" method="'. $this['method'] . '">';
221 $output .= $this->renderChildren();
222 $output .= '</form>';
223 return $output;
224 }
225 }
226
227
228
229 /* Some basic widgets */
230
231
232
233 class DrupalHiddenElement extends DrupalFormWidget {
234 public function theme() {
235 return '<input type="hidden" name="'. $this['post_name'] .'" " value="'. $this['value'] ."\" ". drupal_attributes($this['attributes']) ." />\n";
236 }
237 }
238
239 class DrupalSubmitButton extends DrupalFormWidget {
240 public function __construct($properties = array()) {
241 $defaults = array('default_value' => 'submit');
242 parent::__construct(array_merge($defaults, $properties));
243 }
244 public function theme() {
245 return '<input type="submit" name="'. $this['post_name'] .'" " value="'. $this['value'] ."\" ". drupal_attributes($this['attributes']) ." />\n";
246 }
247 }
248
249 class DrupalTextfield extends DrupalFormWidget {
250 public function validate() {
251 if ($this['post_value'] == 'error') {
252 $this->setError('A validation error!');
253 }
254 }
255 public function theme() {
256 $output = '<div class="form=item">';
257 if ($this['has_error']) {
258 $output .= "<span class='error-message'>{$this['error_message']}</span>";
259 }
260 if (!empty($this['title'])) {
261 $output .= ' <label>'. t('!title :', array('!title' => $this['title'])) ."</label>\n";
262 }
263 $output .= '<input type="textfield" name="'. $this['post_name'] .'" " value="'. $this['value'] ."\" ";
264 $output .= drupal_attributes($this['attributes']) ." />\n";
265
266 if (!empty($this['description'])) {
267 $output .= ' <div class="description">'. $this['description'] ."</div>\n";
268 }
269 return $output;
270 }
271 }
272
273
274

  ViewVC Help
Powered by ViewVC 1.1.2