PluginProbe
Contact Forms by Cimatti / 1.9.2
Contact Forms by Cimatti v1.9.2
2.3.6 2.3.5 2.3.0 2.2.32 2.2.4 2.2.0 2.1.2 2.1.1 trunk 1.0 1.1 1.2 1.2.1 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 All 62 releases
contact-forms / PFBC / Form.php

Form.php in Contact Forms by Cimatti 1.9.2, at PFBC/Form.php

441 lines 14.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*This project's namespace structure is leveraged to autoload requested classes at runtime.*/
3 function PFBC_Load($class) {
4 $file = dirname(__FILE__) . "/" . str_replace("_", DIRECTORY_SEPARATOR, $class) . ".php";
5 if(is_file($file))
6 include_once $file;
7 }
8 spl_autoload_register("PFBC_Load");
9 if(in_array("__autoload",spl_autoload_functions()))
10 spl_autoload_register("__autoload");
11
12 class Form extends Base {
13 protected $elements = array();
14 protected $prefix = "http";
15 protected $values = array();
16 protected $widthSuffix = "px";
17
18 protected $ajax;
19 protected $ajaxCallback;
20 protected $attributes;
21 protected $error;
22 /*jQueryUI themes can be previewed at http://jqueryui.com/themeroller/.*/
23 protected $jQueryUITheme = "smoothness";
24 protected $resourcesPath;
25 /*Prevents various automated from being automatically applied. Current options for this array
26 included jQuery, jQueryUI, jQueryUIButtons, focus, and style.*/
27 protected $prevent = array();
28 protected $view;
29 protected $width;
30
31 public function __construct($id = "pfbc", $width = "") {
32 $this->configure(array(
33 "width" => $width,
34 "action" => basename($_SERVER["SCRIPT_NAME"]),
35 "id" => preg_replace("/\W/", "-", $id),
36 "method" => "post"
37 ));
38
39 if(isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on")
40 $this->prefix = "https";
41
42 /*The Standard view class is applied by default and will be used unless a different view is
43 specified in the form's configure method*/
44 if(empty($this->view))
45 $this->view = new View_Standard;
46
47 if(empty($this->error))
48 $this->error = new Error_Standard;
49
50 /*The resourcePath property is used to identify where third-party resources needed by the
51 project are located. This property will automatically be set properly if the PFBC directory
52 is uploaded within the server's document root. If symbolic links are used to reference the PFBC
53 directory, you may need to set this property in the form's configure method or directly in this
54 constructor.*/
55 $path = dirname(__FILE__) . "/Resources";
56 if(strpos($path, $_SERVER["DOCUMENT_ROOT"]) !== false)
57 $this->resourcesPath = substr($path, strlen($_SERVER["DOCUMENT_ROOT"]));
58 else
59 $this->resourcesPath = "/PFBC/Resources";
60 }
61
62 /*When a form is serialized and stored in the session, this function prevents any non-essential
63 information from being included.*/
64 public function __sleep() {
65 return array("attributes", "elements", "error");
66 }
67
68 public function addElement(Element $element) {
69 $element->setForm($this);
70 //If the element doesn't have a specified id, a generic identifier is applied.
71 $id = $element->getID();
72 if(empty($id))
73 $element->setID($this->attributes["id"] . "-element-" . sizeof($this->elements));
74 $this->elements[] = $element;
75
76 /*For ease-of-use, the form tag's encytype attribute is automatically set if the File element
77 class is added.*/
78 if($element instanceof Element_File)
79 $this->attributes["enctype"] = "multipart/form-data";
80 }
81
82 /*Values that have been set through the setValues method, either manually by the developer
83 or after validation errors, are applied to elements within this method.*/
84 protected function applyValues() {
85 foreach($this->elements as $element) {
86 $name = $element->getName();
87 if(isset($this->values[$name]))
88 $element->setValue($this->values[$name]);
89 elseif(substr($name, -2) == "[]" && isset($this->values[substr($name, 0, -2)]))
90 $element->setValue($this->values[substr($name, 0, -2)]);
91 }
92 }
93
94 public static function clearErrors($id = "pfbc") {
95 if(!empty($_SESSION["pfbc"][$id]["errors"]))
96 unset($_SESSION["pfbc"][$id]["errors"]);
97 }
98
99 public static function clearValues($id = "pfbc") {
100 if(!empty($_SESSION["pfbc"][$id]["values"]))
101 unset($_SESSION["pfbc"][$id]["values"]);
102 }
103
104 /*This method parses the form's width property into a numeric width value and a width suffix - either px or %.
105 These values are used by the form's concrete view class.*/
106 public function formatWidthProperties() {
107 if(!empty($this->width)) {
108 if(substr($this->width, -1) == "%") {
109 $this->width = substr($this->width, 0, -1);
110 $this->widthSuffix = "%";
111 }
112 elseif(substr($this->width, -2) == "px")
113 $this->width = substr($this->width, 0, -2);
114 }
115 else {
116 /*If the form's width property is empty, 100% will be assumed.*/
117 $this->width = 100;
118 $this->widthSuffix = "%";
119 }
120 }
121
122 public function getAjax() {
123 return $this->ajax;
124 }
125
126 public function getElements() {
127 return $this->elements;
128 }
129
130 public function getError() {
131 return $this->error;
132 }
133
134 public function getId() {
135 return $this->attributes["id"];
136 }
137
138 public function getJQueryUIButtons() {
139 return $this->jQueryUIButtons;
140 }
141
142 public function getPrevent() {
143 return $this->prevent;
144 }
145
146 public function getResourcesPath() {
147 return $this->resourcesPath;
148 }
149
150 public function getErrors() {
151 $errors = array();
152 if(session_id() == "")
153 $errors[""] = array("Error: The pfbc project requires an active session to function properly. Simply add session_start() to your script before any output has been sent to the browser.");
154 else {
155 $errors = array();
156 $id = $this->attributes["id"];
157 if(!empty($_SESSION["pfbc"][$id]["errors"]))
158 $errors = $_SESSION["pfbc"][$id]["errors"];
159 }
160
161 return $errors;
162 }
163
164 public static function getSessionValues($id = "pfbc") {
165 $values = array();
166 if(!empty($_SESSION["pfbc"][$id]["values"]))
167 $values = $_SESSION["pfbc"][$id]["values"];
168 return $values;
169 }
170
171 public function getWidth() {
172 return $this->width;
173 }
174
175 public function getWidthSuffix() {
176 return $this->widthSuffix;
177 }
178
179 public static function isValid($id = "pfbc", $clearValues = true) {
180 $valid = true;
181 /*The form's instance is recovered (unserialized) from the session.*/
182 $form = self::recover($id);
183 if(!empty($form)) {
184 if($_SERVER["REQUEST_METHOD"] == "POST")
185 $data = $_POST;
186 else
187 $data = $_GET;
188
189 /*Any values/errors stored in the session for this form are cleared.*/
190 self::clearValues($id);
191 self::clearErrors($id);
192
193 /*Each element's value is saved in the session and checked against any validation rules applied
194 to the element.*/
195 if(!empty($form->elements)) {
196 foreach($form->elements as $element) {
197 $name = $element->getName();
198 if(substr($name, -2) == "[]")
199 $name = substr($name, 0, -2);
200
201 /*The File element must be handled differently b/c it uses the $_FILES superglobal and
202 not $_GET or $_POST.*/
203 if($element instanceof Element_File)
204 $data[$name] = $_FILES[$name]["name"];
205
206 if(isset($data[$name])) {
207 $value = $data[$name];
208 if(is_array($value)) {
209 $valueSize = sizeof($value);
210 for($v = 0; $v < $valueSize; ++$v)
211 $value[$v] = stripslashes($value[$v]);
212 }
213 else
214 $value = stripslashes($value);
215 self::setSessionValue($id, $name, $value);
216 }
217 else
218 $value = null;
219
220 /*If a validation error is found, the error message is saved in the session along with
221 the element's name.*/
222 if(!$element->isValid($value)) {
223 self::setError($id, $element->getErrors(), $name);
224 $valid = false;
225 }
226 }
227 }
228
229 /*If no validation errors were found, the form's session values are cleared.*/
230 if($valid) {
231 if($clearValues)
232 self::clearValues($id);
233 self::clearErrors($id);
234 }
235 }
236 else
237 $valid = false;
238
239 return $valid;
240 }
241
242 /*This method restores the serialized form instance.*/
243 protected static function recover($id) {
244 if(!empty($_SESSION["pfbc"][$id]["form"]))
245 return unserialize($_SESSION["pfbc"][$id]["form"]);
246 else
247 return "";
248 }
249
250 public function render($returnHTML = false) {
251 $this->view->setForm($this);
252 $this->error->setForm($this);
253
254 /*When validation errors occur, the form's submitted values are saved in a session
255 array, which allows them to be pre-populated when the user is redirected to the form.*/
256 $values = self::getSessionValues($this->attributes["id"]);
257 if(!empty($values))
258 $this->setValues($values);
259 $this->applyValues();
260
261 $this->formatWidthProperties();
262
263 if($returnHTML)
264 ob_start();
265
266 $this->renderCSS();
267 $this->view->render();
268 $this->renderJS();
269
270 /*The form's instance is serialized and saved in a session variable for use during validation.*/
271 $this->save();
272
273 if($returnHTML) {
274 $html = ob_get_contents();
275 ob_end_clean();
276 return $html;
277 }
278 }
279
280 /*When ajax is used to submit the form's data, validation errors need to be manually sent back to the
281 form using json.*/
282 public static function renderAjaxErrorResponse($id = "pfbc") {
283 $form = self::recover($id);
284 if(!empty($form))
285 $form->error->renderAjaxErrorResponse();
286 }
287
288 protected function renderCSS() {
289 $this->renderCSSFiles();
290
291 echo '<style type="text/css">';
292 $this->view->renderCSS();
293 $this->error->renderCSS();
294 foreach($this->elements as $element)
295 $element->renderCSS();
296 echo '</style>';
297 }
298
299 protected function renderCSSFiles() {
300 $urls = array();
301 if(!in_array("jQueryUI", $this->prevent))
302 $urls[] = $this->prefix . "://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/" . $this->jQueryUITheme . "/jquery-ui.css";
303 foreach($this->elements as $element) {
304 $elementUrls = $element->getCSSFiles();
305 if(is_array($elementUrls))
306 $urls = array_merge($urls, $elementUrls);
307 }
308
309 /*This section prevents duplicate css files from being loaded.*/
310 if(!empty($urls)) {
311 $urls = array_values(array_unique($urls));
312 foreach($urls as $url)
313 echo '<link type="text/css" rel="stylesheet" href="', $url, '"/>';
314 }
315 }
316
317 protected function renderJS() {
318 $this->renderJSFiles();
319
320 echo '<script type="text/javascript">';
321 $this->view->renderJS();
322 foreach($this->elements as $element)
323 $element->renderJS();
324
325 $id = $this->attributes["id"];
326
327 echo 'jQuery(document).ready(function() {';
328
329 /*When the form is submitted, disable all submit buttons to prevent duplicate submissions.*/
330 echo 'jQuery("#', $id, '").bind("submit", function() {';
331 if(!in_array("jQueryUIButtons", $this->prevent)) {
332 echo 'jQuery(this).find("button[type=submit]").button("disable");';
333 echo 'jQuery(this).find("button[type=submit] span.ui-button-text").css("padding-right", "2.1em").append("<img class=\"pfbc-loading\" src=\"', $this->resourcesPath, '/loading.gif\"/>");';
334 }
335 else
336 echo 'jQuery(this).find("button[type=submit]").attr("disabled", "disabled");';
337 echo '});';
338
339 /*jQuery is used to set the focus of the form's initial element.*/
340 if(!in_array("focus", $this->prevent))
341 echo 'jQuery("#', $id, ' :input:visible:enabled:first").focus();';
342
343 $this->view->jQueryDocumentReady();
344 foreach($this->elements as $element)
345 $element->jQueryDocumentReady();
346
347 /*For ajax, an anonymous onsubmit javascript function is bound to the form using jQuery. jQuery's
348 serialize function is used to grab each element's name/value pair.*/
349 if(!empty($this->ajax)) {
350 echo 'jQuery("#', $id, '").bind("submit", function() {';
351 $this->error->clear();
352 echo <<<JS
353 jQuery.ajax({
354 url: "{$this->attributes["action"]}",
355 type: "{$this->attributes["method"]}",
356 data: jQuery("#$id").serialize(),
357 success: function(response) {
358 if(response != undefined && typeof response == "object" && response.errors) {
359 JS;
360 $this->error->applyAjaxErrorResponse();
361 echo <<<JS
362 jQuery("html, body").animate({ scrollTop: jQuery("#$id").offset().top }, 500 );
363 }
364 else {
365 JS;
366 /*A callback function can be specified to handle any post submission events.*/
367 if(!empty($this->ajaxCallback))
368 echo $this->ajaxCallback, "(response);";
369
370 echo '}';
371
372 if(!in_array("jQueryUIButtons", $this->prevent)) {
373 echo 'jQuery("#', $id, ' button[type=submit] span.ui-button-text").css("padding-right", "1em").find("img").remove();';
374 echo 'jQuery("#', $id, ' button[type=submit]").button("enable");';
375 }
376 else
377 echo 'jQuery("#', $id, '").find("button[type=submit]").removeAttr("disabled");';
378
379 echo <<<JS
380 }
381 });
382 return false;
383 });
384
385 JS;
386 }
387
388 echo <<<JS
389 });
390 </script>
391 JS;
392 }
393
394 protected function renderJSFiles() {
395 $urls = array();
396 if(!in_array("jQuery", $this->prevent))
397 $urls[] = $this->prefix . "://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js";
398 if(!in_array("jQueryUI", $this->prevent))
399 $urls[] = $this->prefix . "://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js";
400 foreach($this->elements as $element) {
401 $elementUrls = $element->getJSFiles();
402 if(is_array($elementUrls))
403 $urls = array_merge($urls, $elementUrls);
404 }
405
406 /*This section prevents duplicate css files from being loaded.*/
407 if(!empty($urls)) {
408 $urls = array_values(array_unique($urls));
409 foreach($urls as $url)
410 echo '<script type="text/javascript" src="', $url, '"></script>';
411 }
412 }
413
414 /*The save method serialized the form's instance and saves it in the session.*/
415 protected function save() {
416 $_SESSION["pfbc"][$this->attributes["id"]]["form"] = serialize($this);
417 }
418
419 /*Valldation errors are saved in the session after the form submission, and will be displayed to the user
420 when redirected back to the form.*/
421 public static function setError($id, $errors, $element = "") {
422 if(!is_array($errors))
423 $errors = array($errors);
424 if(empty($_SESSION["pfbc"][$id]["errors"][$element]))
425 $_SESSION["pfbc"][$id]["errors"][$element] = array();
426
427 foreach($errors as $error)
428 $_SESSION["pfbc"][$id]["errors"][$element][] = $error;
429 }
430
431 public static function setSessionValue($id, $element, $value) {
432 $_SESSION["pfbc"][$id]["values"][$element] = $value;
433 }
434
435 /*An associative array is used to pre-populate form elements. The keys of this array correspond with
436 the element names.*/
437 public function setValues(array $values) {
438 $this->values = array_merge($this->values, $values);
439 }
440 }
441