Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- define('F_AUTO', 1);
- class webform
- {
- /*
- File: webform.class.php
- Developer: Shane Harter
- Created: Apr 25, 2008
- Version: 1.0
- Description:
- ---------------------------------------------------------------------------------------
- This class represents an HTML input form. Ties together a data_model object and an event model LCP file
- Instructions:
- ---------------------------------------------------------------------------------------
- Create a webform object, extending this class, for each data_model object in your app.
- Sometimes multiple forms per data object.
- For each field on your form, create a function modeled after input_generic() below. ALL
- of these methods must begin with "input_" -- that naming convention is used when assembling the form.
- Important to use the same field naming as is used on your data_model (which should be the same as the DB)
- Dependencies:
- ----------------------------------------------------------------------------------------
- input_field v1.0
- error v1.0
- fatal_error v1.0
- warning v1.0
- libValidate v1.0
- libArray v1.0
- data_model v1.0
- */
- var $form_name; // String - The form Name (Also used as the id)
- var $form_title; // String - The form Title
- var $form_attrib; // String - Optional Advanced Form Attributes - Should be valid HTML, will be dropped into FORM tag. (OnClick="" for example)
- var $form_method = 'post'; // Enum[get|post] - The method attribute of the form tag
- var $form_action = F_AUTO; // String - The action of the form tag (What URL to get/post to). Auto Detect will attempt to determine the URL based on where this obj is instantiated
- // Configuration Options
- var $show_errors = true; // Boolean - Enable built-in error reporting (for validation errors only). Turn off if you want to display these errors elsewhere on the page
- var $persist = false; // Boolean - Never remove the webform_key from the $_SESSION collection. This will mean the user could use back button, but it also means they could accidently double-submit
- /**
- * @var data_model */
- var $m_model; // Object - data_model - should extend data_model abstract class
- var $m_posted = false; // Boolean - has this form been posted yet? set in load_from_post()
- var $m_fields = array(); // Array of input_field Objects - Associative array using field_name as key, input_field object as value
- var $m_webform_key; // String - The webform key for this instance. Only exists if render() has been called.
- function webform($form_name, $data_object)
- {
- // The form_name is used as the DOM id among other things..
- $this->form_name = htmlentities($form_name);
- // The $data_object object is essential. If the form you're creating is not backed by a DB table
- // then you must still create a basic data_model with properties added for each field, with a save() method.
- if (false == is_subclass_of(&$data_object, 'data_object'))
- {
- $e = new fatal_error('An error has occured while processing this form');
- $e->comment = 'A valid data_object is required.';
- $e->class = 'webform';
- $e->commit();
- return false;
- }
- $this->m_model =& $data_object;
- // Now call fields() to add the actual fields..
- $this->fields();
- }
- // **********************************************************************************************************************************************
- // * Public Methods
- // ----------------------------------------------------------------------------------------------------------------------------------------------
- function fields()
- {
- // Abstract.
- // Use to create field definitions and corresponding add_field() calls
- /*
- * Template:
- $o = new input_field('Name', $this);
- $o->set_label('Label');
- $o->set_field_as_input();
- $this->add_field($o);
- */
- }
- function layout()
- {
- // This method will auto-layout the input_field objects in an HTML Table. Think of it as everything INSIDE the <form>..</form> tags, which are auto-added.
- // Returns HTML
- /*
- Webform Tags Available in Layout:
- -------------------------------------
- %form_name% - The name of the form
- %form_title% - The title of the form
- %error_block% - An error_message block (only applicable if $show_errors=true)
- You will also need to add placeholders for EACH field you've added to the form.
- The actual tag name should match the fieldname that was passed into the 'new input_field' statement. If desired, you also need to add tags for their corresponding
- labels. The tags will look like this:
- %fieldname%
- %label_fieldname%
- If you have an input_field named, for example, userName, the tags will be:
- %username%
- %label_username%
- CSS Styling Options
- -------------------------------------
- Nearly every webform element, the table, tr, td, form tags, etc, have a CSS class specified. This will allow us to have a default style in the global css file,
- and it allows you to overload those styles at the application-level.
- A select few elements also are assigned a unique id tag that lets you define CSS stylization for those specific DOM id's.
- Tag Class ID
- ----------------------------------------------------------------
- <form> webform_form webform_%formname%
- <div> (form_title) webform_title webform_title_%formname%
- <table> webform_table webform_table_%formname%
- <tr> webform_tr
- <td> webform_td
- <label> webform_label
- <input> webform_input
- <select> webform_select
- It's important, though, to remember that input_field's and the webform itself are very customizable. If you define your own input_field by passing the
- actual HTML (opposed to using one of the set_field_as_xxx() methods), the class='' and/or id='' does not get parsed-in. You must include it if you want it there.
- */
- $this->m_sort_fields();
- $fields = '';
- foreach ($this->m_fields as $field)
- {
- $fields .= "
- <tr class='webform_tr'>
- <td width='25%' class='webform_td'>%label_{$field->m_field_name}%</td>
- <td width='75%' class='webform_td'>%{$field->m_field_name}%</td>
- </tr>";
- }
- // Now as long as we have some $layout, wrap w/ table tags
- if (empty($fields))
- {
- return '';
- }
- return "<br/>
- <div class='webform_title' id='webform_title_%form_name%'>%form_title%</div>
- <br/>
- %error_block%
- <table class='webform_table' id='webform_table_%form_name%'>
- {$fields}
- </table>";
- }
- function add_field(&$field)
- {
- // This method will add the supplied $field (of type input_field or subclass_of input_field) to the $fields collection
- // This object must be bound to a data_model object before fields can be added.
- // Returns Boolean, Raises fatal error if field could not be added.
- if (false == is_a($field, 'input_field'))
- {
- $e = new fatal_error('An error has occured while processing this form');
- $e->comment = "add_field() faild. Supplied field could not be validated as an instance or subclass-of the input_field type";
- $e->class = 'webform';
- $e->commit();
- return false;
- }
- if (false == is_a($this->m_model, 'data_object'))
- {
- $e = new fatal_error('An error has occured while processing this form');
- $e->comment = "add_field() failed. The webform must be bound to a valid data_model object before fields can be added.";
- $e->class = 'webform';
- $e->commit();
- return false;
- }
- // This should always be an array, but just in case, do the check here
- if (false == is_array($this->m_fields))
- {
- $this->m_fields = array();
- }
- // If the user hasn't ordered the field, order it based on the order in which it was added.
- if (false == isset($field->order))
- {
- $field->order = count($this->m_fields)+1;
- }
- $this->m_fields[$field->m_field_name] = $field;
- }
- function render()
- {
- // This method will request the layout(), add <form> tags, m_parse_all() the placeholders, and return the result
- // Returns HTML or throws a fatal error
- if ($this->m_validate())
- {
- // Build the Form Action
- $this->m_autodetect_action();
- // Build the layout w/ placeholders for the input fields.
- $form = $this->layout();
- // Create the <form> and <input type='hidden'> tags needed to give the form something to do with itself
- $form = $this->m_build_tags($form);
- // Parse all the webform tags.
- return $this->m_parse_webform($form);
- }
- }
- function save_model()
- {
- // This method will check the $posted flag and, if set, will attempt to save the model (to the DB, not from disaster. Disaster is already assured)
- // Only Applicable AFTER post
- // returns boolean
- if ($this->m_posted)
- {
- if (false == is_a($this->m_model, 'data_object'))
- {
- $e = new fatal_error('An Error has Occurred While Processing this Form');
- $e->comment = "save_model() failed. The bound data model in m_model does not exist or is invalid. ";
- $e->class = 'webform';
- $e->commit();
- return false;
- }
- if ($this->m_model->save())
- {
- if (false == $this->persist)
- {
- $this->destroy();
- }
- return true;
- }
- return false;
- }
- $w = new warning('save_model() called on a webform object that does not appear to have been posted.');
- $w->comment = '[m_posted: ' . libFormat::literal($this->m_posted) . ']';
- $w->commit();
- return false;
- }
- function destroy()
- {
- // This method removes the webform AND bound model from the $_SESSION collection.
- // Session key is created during render() so there's no need to call this if render() hasn't been called.
- // This is called implicitly during the save_model() process UNLESS $pesist=true. If $persist is set, you must destroy() explicitely.
- // Returns Bool
- if ($this->m_webform_key)
- {
- if (isset($_SESSION['webform_' . $this->m_webform_key]))
- {
- unset($_SESSION['webform_' . $this->m_webform_key]);
- }
- if (is_a($this->m_model, 'data_object'))
- {
- $this->m_model->destroy();
- }
- }
- return isset($_SESSION['webform_' . $this->m_webform_key]);
- }
- // **********************************************************************************************************************************************
- // * STATIC (AKA CLASS) METHODS
- // ----------------------------------------------------------------------------------------------------------------------------------------------
- function load_from_post()
- {
- // This method should be called STATICALLY on the webform:: class.
- // It will look for a webform_key in the $_POST collection and, if exists, load that object from the $_SESSION collection.
- // It will also re-load the data_model in the same fashion (calling its own load_from_post constructor)
- if (isset($_POST['webform_key']))
- {
- $o = webform::m_unserialize($_POST['webform_key']); /* @var o webform */
- if (is_a($o, 'webform'))
- {
- // Before we do anything, make sure that the model was successfully reinstantiated
- if (false == is_a($o->m_model, 'data_object'))
- {
- $e = new fatal_error('An Error has Occurred While Processing this Form');
- $e->comment = "The webform object has been loaded, but the data_model is invalid. [Model: {$o->m_model}]";
- $e->class = 'webform';
- $e->commit();
- return false;
- }
- // First load the m_model...
- // This will load values from the $_POST into the bound variable in the m_model object
- $o->m_model->import_posted_data(array_keys($o->m_fields));
- // Second load the input_field array...
- // We need to re-build the bindings between the input_field and the data_model. They were broken during the serialize/unserialize process
- // If you look under a debugger, the $m_bound_field method of each input_field object is now NULL.
- // Also, we're using the $key to implicitely avoid a copy of m_fields being made in the background, which is the usual MO of foreach() in PHP4
- foreach(array_keys($o->m_fields) as $key)
- {
- $o->m_fields[$key]->m_bind_field();
- }
- // Finally, set the m_posted flag
- $o->m_posted = true;
- return $o;
- }
- $e = new fatal_error('An Error has Occurred While Processing this Form');
- $e->comment = "Invalid webform_key provided. Webform could not be loaded from session collection.";
- $e->class = 'webform';
- $e->commit();
- }
- return false;
- }
- // **********************************************************************************************************************************************
- // * Private Methods
- // ----------------------------------------------------------------------------------------------------------------------------------------------
- function m_validate()
- {
- // This method will validate that everything is in-place before render()ing the form. Fatal errors are issued liberally in the webform package: this is an important
- // part of the MPG framework and while a half-baked form is a problem, a much bigger problem is a form that LOOKS like it worked, but didn't.
- // Returns Boolean.
- $errs = array();
- if (false == is_a($this->m_model, 'data_object')) $errs[] = 'A valid data_object object is required';
- if (false == is_array($this->m_fields) || 0 == count($this->m_fields)) $errs[] = 'At least one field must be added before a form can be rendered';
- if (empty($this->form_name)) $errs[] = 'A valid, html_safe form_name is required';
- if (empty($this->form_action)) $errs[] = 'A valid, html_safe form_action is required';
- if (false == libValidate::is_in(strtolower($this->form_method), 'get', 'post')) $errs[] = "The form_method is invalid. Get or Post expected. [method:{$this->form_method}]";
- foreach ($errs as $err)
- {
- $e = new fatal_error('An Error has Occurred While Processing this Form');
- $e->comment = $err;
- $e->class = 'webform';
- $e->commit();
- }
- // If an error object was raised return false.
- return (false == is_object($e));
- }
- function m_build_tags($layout)
- {
- // This methods accepts the indiscriminate HTML and wraps the needed <form> and <input tyep='hidden'> tags around it.
- // Returns HTML.
- return "
- <form id='webform_%form_name%' name='webform_%form_name%' class='webform_form' method='%form_method%' action='%form_action%' %form_attrib%>
- <input type='hidden' name='webform_key' value='%webform_key%'>
- {$layout}
- </form>
- ";
- }
- function m_build_error_block()
- {
- // If $show_errors is set, this will instantiate an error_message object to display validation errors related to this form.
- // Right now, it's simply putting the errors in block-form at the top of the form (or wherever the %error_block placeholder is)
- // Returns HTML error block or empty string if $show_errors is false (or if there are no errors to show)
- if ($this->show_errors)
- {
- $err_msg = new error_message();
- $err_msg->class = validation_error::get_classname();
- return $err_msg->return_message();
- }
- return '';
- }
- function m_parse_webform($html)
- {
- // This method will parse any field-placeholders in the layout with their corresponding fields from their respective input_field objects.
- // Returns HTML
- // Parse the simple form details
- $html = str_replace('%form_name%', $this->form_name, $html);
- $html = str_replace('%form_title%', $this->form_title, $html);
- $html = str_replace('%form_method%', $this->form_method, $html);
- $html = str_replace('%form_action%', $this->form_action, $html);
- $html = str_replace('%form_attrib%', $this->form_attrib, $html);
- $html = str_replace('%error_block%', $this->m_build_error_block(), $html);
- // Parse the input fields themselves, then parse them into the form
- // Uses array_keys to prevent php from making a copy of the m_fields array which is the MO of the foreach loop.
- foreach (array_keys($this->m_fields) as $field_name)
- {
- // Now integrate into the $html
- $html = str_replace("%{$field_name}%", $this->m_fields[$field_name]->render_field(), $html);
- $html = str_replace("%label_{$field_name}%", $this->m_fields[$field_name]->render_label(), $html);
- }
- // Finally, serialize the object and add the webform key in a hidden field.
- // Needs to be the last step b/c any changes made to the object after m_serialize() will not be reflected when the webform is unserialized after its posted.
- if ($this->m_serialize())
- {
- $html = str_replace('%webform_key%', $this->m_webform_key, $html);
- return $html;
- }
- // If we're still here, the serialize failed
- $e = new fatal_error('An Error has Occurred While Processing this Form');
- $e->comment = "m_serialize() failed. Unkown reason. [webform_key: {$this->m_webform_key}]";
- $e->class = 'syserr';
- $e->commit();
- return false;
- }
- function m_sort_fields()
- {
- // This function will sort the $this->fields array based on the $order of each field object.
- // Not applicable if you use the layout() method is overwritten in the subform (since it uses place-holders for each field by name)
- // returns Boolean, sorts the $this->m_fields array
- if (count($this->m_fields) > 0)
- {
- // This will extract the 'order' field from each object in m_fields into the $order array.
- // The keys will match up between $order and $m_fields
- $order = libArray::extract_nested_key($this->m_fields, 'order');
- if (is_array($order) && count($order) == count($this->m_fields))
- {
- // Placeholder for the sorted $m_fields array
- $new_m_fields = array();
- // Sort the $order array
- // Then loop thru the keys of that array (which match the keys in $m_fields still) and populate $new_m_fields;
- @asort($order);
- foreach (array_keys($order) as $key)
- {
- $new_m_fields[$key] = $this->m_fields[$key];
- }
- // Double Check...
- if (count($this->m_fields) == count($new_m_fields))
- {
- $this->m_fields = $new_m_fields;
- return true;
- }
- $e = new warning('An error occured while processing this form');
- $e->comment = "m_sort_fields() failed. Count of m_fields does not match count of new_m_fields after sort() operation. Fields have not been sorted.";
- $e->commit();
- return false;
- }
- $e = new warning('An error occured while processing this form');
- $e->comment = "m_sort_fields() failed. Count of order array does not match count of m_fields array after extract_nested_key() operation. Fields have not been sorted.";
- $e->commit();
- return false;
- }
- $e = new warning('An error occured while processing this form');
- $e->comment = "sort_fields() failed. No fields have been added.";
- $e->commit();
- return false;
- }
- function m_autodetect_action()
- {
- // This method attempts to autodetect the form action and, if successful, populates the $this->form_action property.
- // It will determine what page the object is instantiated on (should be an LCP file) and it will look for a method on that page: {$form_name}PostRun()
- // If the method does not exist, a fatal error will be thrown.
- // If the action is not set to F_AUTO, then just quit and go home
- if ($this->form_action != F_AUTO)
- {
- return;
- }
- // Create glob reference to the LC Service Object
- global $service;
- // Get the backtrace info for the current calling page
- $backtrace = debug_backtrace();
- $backtrace = $backtrace[1]; // This level will have the URL of the lcp page
- if (false == method_exists($service, $this->form_name . 'PostRun'))
- {
- $e = new fatal_error('An error has occured while processing this form');
- $e->comment = "The m_autodetect_action() method failed. The corresponding Post method does not exist. You must create the method, {$this->form_name}PostRun, or manually set the form_action property.";
- $e->class = 'webform';
- $e->commit();
- return false;
- }
- // If we're here, the method exists, so build the action url.
- $path = $backtrace['file'];
- // We now have this: /opt/lampp/logicreate/services/service_name/main.lcp
- $path = explode('/services/', $path);
- // We now have an array, in the second position we should have this: service_name/main.lcp
- $path = str_replace('.lcp', '', $path[1]);
- // We now have this: service_name/main
- $this->form_action = APP_URL . $path . '/event=' . $this->form_name . 'Post';
- // And we now have this: /index.php/service_name/main/event=formIdPostRun
- return true;
- }
- function m_serialize()
- {
- // This function will serialize $this and store it in the $_SESSION collection. A hash is made of the serialized object and that hash is used as the session key.
- // Returns hashed session key.
- // Must be called on an instantiated object, not statically.
- if (is_subclass_of($this, 'webform') == false) return false;
- // Serialize the data_model first, then the whole object
- $this->m_model = $this->m_model->serialize();
- $serial = serialize($this);
- // Hash
- $hash = md5($serial);
- // Save in session memory
- $_SESSION['webform_' . $hash] = $serial;
- // Add this key to the $m_webform_key property.
- // Since this object has already been serialized, this won't exist when its unserialized.
- // Rather than re-serialize it, we just pull this key in during the load_from_post process.
- $this->m_webform_key = $hash;
- // Double check that the session key saved properly
- return (isset($_SESSION['webform_' . $hash]));
- }
- function m_unserialize($hash)
- {
- // This function will return an unserialized version of $text
- // Should be called statically.
- // Used to unserialize a measurable object
- // Get has from session memory and compare. Key is the md5'd, serialized text
- if (isset($_SESSION['webform_' . $hash]))
- {
- // Unserialize the webform
- $s = unserialize($_SESSION['webform_' . $hash]); /* @var s webform */
- // Unserialize the model -- the hash is stored in place of the object in m_model
- $s->m_model = data_model::unserialize($s->m_model);
- // Re-add the webform_key. See notes in m_serialize() if you want to know more about this..
- $s->m_webform_key = $hash;
- return $s;
- }
- return false;
- }
- }
- ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement