summaryrefslogtreecommitdiff
path: root/modules/forge
diff options
context:
space:
mode:
authorBharat Mediratta <bharat@menalto.com>2008-12-15 09:22:32 +0000
committerBharat Mediratta <bharat@menalto.com>2008-12-15 09:22:32 +0000
commit93977e579cca9a9d0df28099e1fb7596bcc1c825 (patch)
tree6968d4a9f4a3cadb332e23e8e807748eccd25deb /modules/forge
parent4fe895a2c473cd22e4741552186fe2be48503167 (diff)
Refresh of Forge r168 from vendor/forge/modified
Diffstat (limited to 'modules/forge')
-rw-r--r--modules/forge/controllers/forge_demo.php83
-rw-r--r--modules/forge/libraries/Forge.php319
-rw-r--r--modules/forge/libraries/Form_Checkbox.php83
-rw-r--r--modules/forge/libraries/Form_Checklist.php92
-rw-r--r--modules/forge/libraries/Form_Dateselect.php138
-rw-r--r--modules/forge/libraries/Form_Dropdown.php78
-rw-r--r--modules/forge/libraries/Form_Group.php89
-rw-r--r--modules/forge/libraries/Form_Hidden.php25
-rw-r--r--modules/forge/libraries/Form_Input.php555
-rw-r--r--modules/forge/libraries/Form_Password.php23
-rw-r--r--modules/forge/libraries/Form_Phonenumber.php98
-rw-r--r--modules/forge/libraries/Form_Radio.php22
-rw-r--r--modules/forge/libraries/Form_Submit.php41
-rw-r--r--modules/forge/libraries/Form_Textarea.php31
-rw-r--r--modules/forge/libraries/Form_Upload.php187
-rw-r--r--modules/forge/models/user_edit.php132
-rw-r--r--modules/forge/views/forge_template.php69
17 files changed, 2065 insertions, 0 deletions
diff --git a/modules/forge/controllers/forge_demo.php b/modules/forge/controllers/forge_demo.php
new file mode 100644
index 00000000..0521b939
--- /dev/null
+++ b/modules/forge/controllers/forge_demo.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * Forge module demo controller. This controller should NOT be used in production.
+ * It is for demonstration purposes only!
+ *
+ * $Id$
+ *
+ * @package Forge
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class Forge_demo_Controller extends Controller {
+
+ // Do not allow to run in production
+ const ALLOW_PRODUCTION = FALSE;
+
+ public function index()
+ {
+ $profiler = new Profiler;
+
+ $foods = array
+ (
+ 'tacos' => array('tacos', FALSE),
+ 'burgers' => array('burgers', FALSE),
+ 'spaghetti' => array('spaghetti (checked)', TRUE),
+ 'cookies' => array('cookies (checked)', TRUE),
+ );
+
+ $form = new Forge(NULL, 'New User');
+
+ // Create each input, following this format:
+ //
+ // type($name)->attr(..)->attr(..);
+ //
+ $form->hidden('hideme')->value('hiddenz!');
+ $form->input('email')->label(TRUE)->rules('required|valid_email');
+ $form->input('username')->label(TRUE)->rules('required|length[5,32]');
+ $form->password('password')->label(TRUE)->rules('required|length[5,32]');
+ $form->password('confirm')->label(TRUE)->matches($form->password);
+ $form->checkbox('remember')->label('Remember Me');
+ $form->checklist('foods')->label('Favorite Foods')->options($foods)->rules('required');
+ $form->dropdown('state')->label('Home State')->options(locale_US::states())->rules('required');
+ $form->dateselect('birthday')->label(TRUE)->minutes(15)->years(1950, date('Y'));
+ $form->submit('Save');
+
+ if ($form->validate())
+ {
+ echo Kohana::debug($form->as_array());
+ }
+
+ echo $form->render();
+
+ // Using a custom template:
+ // echo $form->render('custom_view', TRUE);
+ // Inside the view access the inputs using $input_id->render(), ->label() etc
+ //
+ // To get the errors use $input_id_errors.
+ // Set the error format with $form->error_format('<div>{message}</div>');
+ // Defaults to <p class="error">{message}</p>
+ //
+ // Examples:
+ // echo $username->render(); echo $password_errors;
+ }
+
+ public function upload()
+ {
+ $profiler = new Profiler;
+
+ $form = new Forge;
+ $form->input('hello')->label(TRUE);
+ $form->upload('file', TRUE)->label(TRUE)->rules('required|size[200KB]|allow[jpg,png,gif]');
+ $form->submit('Upload');
+
+ if ($form->validate())
+ {
+ echo Kohana::debug($form->as_array());
+ }
+
+ echo $form->render();
+ }
+
+} // End Forge Demo Controller
diff --git a/modules/forge/libraries/Forge.php b/modules/forge/libraries/Forge.php
new file mode 100644
index 00000000..f6bca42b
--- /dev/null
+++ b/modules/forge/libraries/Forge.php
@@ -0,0 +1,319 @@
+<?php
+/**
+ * FORGE (FORm GEneration) library.
+ *
+ * $Id$
+ *
+ * @package Forge
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class Forge_Core {
+
+ // Template variables
+ protected $template = array
+ (
+ 'title' => '',
+ 'class' => '',
+ 'open' => '',
+ 'close' => '',
+ );
+
+ // Form attributes
+ protected $attr = array();
+
+ // Form inputs and hidden inputs
+ public $inputs = array();
+ public $hidden = array();
+
+ // Error message format, only used with custom templates
+ public $error_format = '<p class="error">{message}</p>';
+ public $newline_char = "\n";
+
+ /**
+ * Form constructor. Sets the form action, title, method, and attributes.
+ *
+ * @return void
+ */
+ public function __construct($action = NULL, $title = '', $method = NULL, $attr = array())
+ {
+ // Set form attributes
+ $this->attr['action'] = $action;
+ $this->attr['method'] = empty($method) ? 'post' : $method;
+
+ // Set template variables
+ $this->template['title'] = $title;
+
+ // Empty attributes sets the class to "form"
+ empty($attr) and $attr = array('class' => 'form');
+
+ // String attributes is the class name
+ is_string($attr) and $attr = array('class' => $attr);
+
+ // Extend the template with the attributes
+ $this->attr += $attr;
+ }
+
+ /**
+ * Magic __get method. Returns the specified form element.
+ *
+ * @param string unique input name
+ * @return object
+ */
+ public function __get($key)
+ {
+ if (isset($this->inputs[$key]))
+ {
+ return $this->inputs[$key];
+ }
+ elseif (isset($this->hidden[$key]))
+ {
+ return $this->hidden[$key];
+ }
+ }
+
+ /**
+ * Magic __call method. Creates a new form element object.
+ *
+ * @throws Kohana_Exception
+ * @param string input type
+ * @param string input name
+ * @return object
+ */
+ public function __call($method, $args)
+ {
+ // Class name
+ $input = 'Form_'.ucfirst($method);
+
+ // Create the input
+ switch (count($args))
+ {
+ case 1:
+ $input = new $input($args[0]);
+ break;
+ case 2:
+ $input = new $input($args[0], $args[1]);
+ break;
+ default:
+ throw new Kohana_Exception('forge.invalid_input', $input);
+ }
+
+ if ( ! ($input instanceof Form_Input) AND ! ($input instanceof Forge))
+ throw new Kohana_Exception('forge.unknown_input', get_class($input));
+
+ $input->method = $this->attr['method'];
+
+ if ($name = $input->name)
+ {
+ // Assign by name
+ if ($method == 'hidden')
+ {
+ $this->hidden[$name] = $input;
+ }
+ else
+ {
+ $this->inputs[$name] = $input;
+ }
+ }
+ else
+ {
+ // No name, these are unretrievable
+ $this->inputs[] = $input;
+ }
+
+ return $input;
+ }
+
+ /**
+ * Set a form attribute. This method is chainable.
+ *
+ * @param string|array attribute name, or an array of attributes
+ * @param string attribute value
+ * @return object
+ */
+ public function set_attr($key, $val = NULL)
+ {
+ if (is_array($key))
+ {
+ // Merge the new attributes with the old ones
+ $this->attr = array_merge($this->attr, $key);
+ }
+ else
+ {
+ // Set the new attribute
+ $this->attr[$key] = $val;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Validates the form by running each inputs validation rules.
+ *
+ * @return bool
+ */
+ public function validate()
+ {
+ $status = TRUE;
+
+ $inputs = array_merge($this->hidden, $this->inputs);
+
+ foreach ($inputs as $input)
+ {
+ if ($input->validate() == FALSE)
+ {
+ $status = FALSE;
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * Returns the form as an array of input names and values.
+ *
+ * @return array
+ */
+ public function as_array()
+ {
+ $data = array();
+ foreach (array_merge($this->hidden, $this->inputs) as $input)
+ {
+ if (is_object($input->name)) // It's a Forge_Group object (hopefully)
+ {
+ foreach ($input->inputs as $group_input)
+ {
+ if ($name = $group_input->name)
+ {
+ $data[$name] = $group_input->value;
+ }
+ }
+ }
+ else if (is_array($input->inputs))
+ {
+ foreach ($input->inputs as $group_input)
+ {
+ if ($name = $group_input->name)
+ {
+ $data[$name] = $group_input->value;
+ }
+ }
+ }
+ else if ($name = $input->name) // It's a normal input
+ {
+ // Return only named inputs
+ $data[$name] = $input->value;
+ }
+ }
+ return $data;
+ }
+
+ /**
+ * Changes the error message format. Your message formatting must
+ * contain a {message} placeholder.
+ *
+ * @throws Kohana_Exception
+ * @param string new message format
+ * @return void
+ */
+ public function error_format($string = '')
+ {
+ if (strpos((string) $string, '{message}') === FALSE)
+ throw new Kohana_Exception('validation.error_format');
+
+ $this->error_format = $string;
+ }
+
+ /**
+ * Creates the form HTML
+ *
+ * @param string form view template name
+ * @param boolean use a custom view
+ * @return string
+ */
+ public function render($template = 'forge_template', $custom = FALSE)
+ {
+ // Load template
+ $form = new View($template);
+
+ if ($custom)
+ {
+ // Using a custom view
+
+ $data = array();
+ foreach (array_merge($this->hidden, $this->inputs) as $input)
+ {
+ $data[$input->name] = $input;
+
+ // Groups will never have errors, so skip them
+ if ($input instanceof Form_Group)
+ continue;
+
+ // Compile the error messages for this input
+ $messages = '';
+ $errors = $input->error_messages();
+ if (is_array($errors) AND ! empty($errors))
+ {
+ foreach ($errors as $error)
+ {
+ // Replace the message with the error in the html error string
+ $messages .= str_replace('{message}', $error, $this->error_format).$this->newline_char;
+ }
+ }
+
+ $data[$input->name.'_errors'] = $messages;
+ }
+
+ $form->set($data);
+ }
+ else
+ {
+ // Using a template view
+
+ $form->set($this->template);
+ $hidden = array();
+ if ( ! empty($this->hidden))
+ {
+ foreach ($this->hidden as $input)
+ {
+ $hidden[$input->name] = $input->value;
+ }
+ }
+
+ $form_type = 'open';
+ // See if we need a multipart form
+ $check_inputs = array($this->inputs);
+ while ($check_inputs) {
+ foreach (array_shift($check_inputs) as $input) {
+ if ($input instanceof Form_Upload)
+ {
+ $form_type = 'open_multipart';
+ }
+ if ($input instanceof Form_Group)
+ {
+ $check_inputs += array($input->inputs);
+ }
+ }
+ }
+
+ // Set the form open and close
+ $form->open = form::$form_type(arr::remove('action', $this->attr), $this->attr, $hidden);
+ $form->close = form::close();
+
+ // Set the inputs
+ $form->inputs = $this->inputs;
+ }
+
+ return $form;
+ }
+
+ /**
+ * Returns the form HTML
+ */
+ public function __toString()
+ {
+ return (string) $this->render();
+ }
+
+} // End Forge
diff --git a/modules/forge/libraries/Form_Checkbox.php b/modules/forge/libraries/Form_Checkbox.php
new file mode 100644
index 00000000..c015e437
--- /dev/null
+++ b/modules/forge/libraries/Form_Checkbox.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * FORGE checkbox input library.
+ *
+ * $Id$
+ *
+ * @package Forge
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class Form_Checkbox_Core extends Form_Input {
+
+ protected $data = array
+ (
+ 'type' => 'checkbox',
+ 'class' => 'checkbox',
+ 'value' => '1',
+ 'checked' => FALSE,
+ );
+
+ protected $protect = array('type');
+
+ public function __get($key)
+ {
+ if ($key == 'value')
+ {
+ // Return the value if the checkbox is checked
+ return $this->data['checked'] ? $this->data['value'] : NULL;
+ }
+
+ return parent::__get($key);
+ }
+
+ public function label($val = NULL)
+ {
+ if ($val === NULL)
+ {
+ // Do not display labels for checkboxes, labels wrap checkboxes
+ return '';
+ }
+ else
+ {
+ $this->data['label'] = ($val === TRUE) ? utf8::ucwords(inflector::humanize($this->name)) : $val;
+ return $this;
+ }
+ }
+
+ protected function html_element()
+ {
+ // Import the data
+ $data = $this->data;
+
+ if (empty($data['checked']))
+ {
+ // Not checked
+ unset($data['checked']);
+ }
+ else
+ {
+ // Is checked
+ $data['checked'] = 'checked';
+ }
+
+ if ($label = arr::remove('label', $data))
+ {
+ // There must be one space before the text
+ $label = ' '.ltrim($label);
+ }
+
+ return '<label>'.form::input($data).$label.'</label>';
+ }
+
+ protected function load_value()
+ {
+ if (is_bool($this->valid))
+ return;
+
+ // Makes the box checked if the value from POST is the same as the current value
+ $this->data['checked'] = ($this->input_value($this->name) == $this->data['value']);
+ }
+
+} // End Form Checkbox \ No newline at end of file
diff --git a/modules/forge/libraries/Form_Checklist.php b/modules/forge/libraries/Form_Checklist.php
new file mode 100644
index 00000000..6b1df490
--- /dev/null
+++ b/modules/forge/libraries/Form_Checklist.php
@@ -0,0 +1,92 @@
+<?php
+/**
+ * FORGE checklist input library.
+ *
+ * $Id$
+ *
+ * @package Forge
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class Form_Checklist_Core extends Form_Input {
+
+ protected $data = array
+ (
+ 'name' => '',
+ 'type' => 'checkbox',
+ 'class' => 'checklist',
+ 'options' => array(),
+ );
+
+ protected $protect = array('name', 'type');
+
+ public function __construct($name)
+ {
+ $this->data['name'] = $name;
+ }
+
+ public function __get($key)
+ {
+ if ($key == 'value')
+ {
+ // Return the currently checked values
+ $array = array();
+ foreach ($this->data['options'] as $id => $opt)
+ {
+ // Return the options that are checked
+ ($opt[1] === TRUE) and $array[] = $id;
+ }
+ return $array;
+ }
+
+ return parent::__get($key);
+ }
+
+ public function render()
+ {
+ // Import base data
+ $base_data = $this->data;
+
+ // Make it an array
+ $base_data['name'] .= '[]';
+
+ // Newline
+ $nl = "\n";
+
+ $checklist = '<ul class="'.arr::remove('class', $base_data).'">'.$nl;
+ foreach (arr::remove('options', $base_data) as $val => $opt)
+ {
+ // New set of input data
+ $data = $base_data;
+
+ // Get the title and checked status
+ list ($title, $checked) = $opt;
+
+ // Set the name, value, and checked status
+ $data['value'] = $val;
+ $data['checked'] = $checked;
+
+ $checklist .= '<li><label>'.form::checkbox($data).' '.$title.'</label></li>'.$nl;
+ }
+ $checklist .= '</ul>';
+
+ return $checklist;
+ }
+
+ protected function load_value()
+ {
+ foreach ($this->data['options'] as $val => $checked)
+ {
+ if ($input = $this->input_value($this->data['name']))
+ {
+ $this->data['options'][$val][1] = in_array($val, $input);
+ }
+ else
+ {
+ $this->data['options'][$val][1] = FALSE;
+ }
+ }
+ }
+
+} // End Form Checklist \ No newline at end of file
diff --git a/modules/forge/libraries/Form_Dateselect.php b/modules/forge/libraries/Form_Dateselect.php
new file mode 100644
index 00000000..d531b3c8
--- /dev/null
+++ b/modules/forge/libraries/Form_Dateselect.php
@@ -0,0 +1,138 @@
+<?php
+/**
+ * FORGE dateselect input library.
+ *
+ * $Id$
+ *
+ * @package Forge
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class Form_Dateselect_Core extends Form_Input {
+
+ protected $data = array
+ (
+ 'name' => '',
+ 'class' => 'dropdown',
+ );
+
+ protected $protect = array('type');
+
+ // Precision for the parts, you can use @ to insert a literal @ symbol
+ protected $parts = array
+ (
+ 'month' => array(),
+ 'day' => array(1),
+ 'year' => array(),
+ ' @ ',
+ 'hour' => array(),
+ ':',
+ 'minute' => array(5),
+ 'am_pm' => array(),
+ );
+
+ public function __construct($name)
+ {
+ // Set name
+ $this->data['name'] = $name;
+
+ // Default to the current time
+ $this->data['value'] = time();
+ }
+
+ public function __call($method, $args)
+ {
+ if (isset($this->parts[substr($method, 0, -1)]))
+ {
+ // Set options for date generation
+ $this->parts[substr($method, 0, -1)] = $args;
+ return $this;
+ }
+
+ return parent::__call($method, $args);
+ }
+
+ public function html_element()
+ {
+ // Import base data
+ $data = $this->data;
+
+ // Get the options and default selection
+ $time = $this->time_array(arr::remove('value', $data));
+
+ // No labels or values
+ unset($data['label']);
+
+ $input = '';
+ foreach ($this->parts as $type => $val)
+ {
+ if (is_int($type))
+ {
+ // Just add the separators
+ $input .= $val;
+ continue;
+ }
+
+ // Set this input name
+ $data['name'] = $this->data['name'].'['.$type.']';
+
+ // Set the selected option
+ $selected = $time[$type];
+
+ if ($type == 'am_pm')
+ {
+ // Options are static
+ $options = array('AM' => 'AM', 'PM' => 'PM');
+ }
+ else
+ {
+ // minute(s), hour(s), etc
+ $type .= 's';
+
+ // Use the date helper to generate the options
+ $options = empty($val) ? date::$type() : call_user_func_array(array('date', $type), $val);
+ }
+
+ $input .= form::dropdown($data, $options, $selected);
+ }
+
+ return $input;
+ }
+
+ protected function time_array($timestamp)
+ {
+ $time = array_combine
+ (
+ array('month', 'day', 'year', 'hour', 'minute', 'am_pm'),
+ explode('--', date('n--j--Y--g--i--A', $timestamp))
+ );
+
+ // Minutes should always be in 5 minute increments
+ $time['minute'] = num::round($time['minute'], current($this->parts['minute']));
+
+ return $time;
+ }
+
+ protected function load_value()
+ {
+ if (is_bool($this->valid))
+ return;
+
+ $time = $this->input_value($this->name);
+
+ // Make sure all the required inputs keys are set
+ $time += $this->time_array(time());
+
+ $this->data['value'] = mktime
+ (
+ date::adjust($time['hour'], $time['am_pm']),
+ $time['minute'],
+ 0,
+ $time['month'],
+ $time['day'],
+ $time['year']
+ );
+ }
+
+} // End Form Dateselect \ No newline at end of file
diff --git a/modules/forge/libraries/Form_Dropdown.php b/modules/forge/libraries/Form_Dropdown.php
new file mode 100644
index 00000000..ac810299
--- /dev/null
+++ b/modules/forge/libraries/Form_Dropdown.php
@@ -0,0 +1,78 @@
+<?php
+/**
+ * FORGE dropdown input library.
+ *
+ * $Id$
+ *
+ * @package Forge
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class Form_Dropdown_Core extends Form_Input {
+
+ protected $data = array
+ (
+ 'name' => '',
+ 'class' => 'dropdown',
+ );
+
+ protected $protect = array('type');
+
+ public function __get($key)
+ {
+ if ($key == 'value')
+ {
+ return $this->selected;
+ }
+
+ return parent::__get($key);
+ }
+
+ public function html_element()
+ {
+ // Import base data
+ $base_data = $this->data;
+
+ unset($base_data['label']);
+
+ // Get the options and default selection
+ $options = arr::remove('options', $base_data);
+ $selected = arr::remove('selected', $base_data);
+
+ return form::dropdown($base_data, $options, $selected);
+ }
+
+ protected function load_value()
+ {
+ if (is_bool($this->valid))
+ return;
+
+ $this->data['selected'] = $this->input_value($this->name);
+ }
+
+ public function validate()
+ {
+ // Validation has already run
+ if (is_bool($this->is_valid))
+ return $this->is_valid;
+
+ if ($this->input_value() == FALSE)
+ {
+ // No data to validate
+ return $this->is_valid = FALSE;
+ }
+
+ // Load the submitted value
+ $this->load_value();
+
+ if ( ! array_key_exists($this->value, $this->data['options']))
+ {
+ // Value does not exist in the options
+ return $this->is_valid = FALSE;
+ }
+
+ return parent::validate();
+ }
+
+} // End Form Dropdown \ No newline at end of file
diff --git a/modules/forge/libraries/Form_Group.php b/modules/forge/libraries/Form_Group.php
new file mode 100644
index 00000000..ec6b3ff3
--- /dev/null
+++ b/modules/forge/libraries/Form_Group.php
@@ -0,0 +1,89 @@
+<?php
+/**
+ * FORGE group library.
+ *
+ * $Id$
+ *
+ * @package Forge
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class Form_Group_Core extends Forge {
+
+ protected $data = array
+ (
+ 'type' => 'group',
+ 'name' => '',
+ 'class' => 'group',
+ 'label' => '',
+ 'message' => ''
+ );
+
+ // Input method
+ public $method;
+
+ public function __construct($name = NULL, $class = 'group')
+ {
+ $this->data['name'] = $name;
+ $this->data['class'] = $class;
+
+ // Set dummy data so we don't get errors
+ $this->attr['action'] = '';
+ $this->attr['method'] = 'post';
+ }
+
+ public function __get($key)
+ {
+ if ($key == 'type' || $key == 'name' || $key == 'label')
+ {
+ return $this->data[$key];
+ }
+ return parent::__get($key);
+ }
+
+ public function __set($key, $val)
+ {
+ if ($key == 'method')
+ {
+ $this->attr['method'] = $val;
+ }
+ $this->$key = $val;
+ }
+
+ public function label($val = NULL)
+ {
+ if ($val === NULL)
+ {
+ if ($label = $this->data['label'])
+ {
+ return $this->data['label'];
+ }
+ }
+ else
+ {
+ $this->data['label'] = ($val === TRUE) ? ucwords(inflector::humanize($this->data['name'])) : $val;
+ return $this;
+ }
+ }
+
+ public function message($val = NULL)
+ {
+ if ($val === NULL)
+ {
+ return $this->data['message'];
+ }
+ else
+ {
+ $this->data['message'] = $val;
+ return $this;
+ }
+ }
+
+ public function render()
+ {
+ // No Sir, we don't want any html today thank you
+ return;
+ }
+
+} // End Form Group \ No newline at end of file
diff --git a/modules/forge/libraries/Form_Hidden.php b/modules/forge/libraries/Form_Hidden.php
new file mode 100644
index 00000000..4dcbcc27
--- /dev/null
+++ b/modules/forge/libraries/Form_Hidden.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * FORGE hidden input library.
+ *
+ * $Id$
+ *
+ * @package Forge
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class Form_Hidden_Core extends Form_Input {
+
+ protected $data = array
+ (
+ 'name' => '',
+ 'value' => '',
+ );
+
+ public function render()
+ {
+ return form::hidden($this->data['name'], $this->data['value']);
+ }
+
+} // End Form Hidden \ No newline at end of file
diff --git a/modules/forge/libraries/Form_Input.php b/modules/forge/libraries/Form_Input.php
new file mode 100644
index 00000000..7dfc974d
--- /dev/null
+++ b/modules/forge/libraries/Form_Input.php
@@ -0,0 +1,555 @@
+<?php
+/**
+ * FORGE base input library.
+ *
+ * $Id$
+ *
+ * @package Forge
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class Form_Input_Core {
+
+ // Input method
+ public $method;
+
+ // Element data
+ protected $data = array
+ (
+ 'type' => 'text',
+ 'class' => 'textbox',
+ 'value' => ''
+ );
+
+ // Protected data keys
+ protected $protect = array();
+
+ // Validation rules, matches, and callbacks
+ protected $rules = array();
+ protected $matches = array();
+ protected $callbacks = array();
+
+ // Validation check
+ protected $is_valid;
+
+ // Errors
+ protected $errors = array();
+ protected $error_messages = array();
+
+ /**
+ * Sets the input element name.
+ */
+ public function __construct($name)
+ {
+ $this->data['name'] = $name;
+ }
+
+ /**
+ * Sets form attributes, or return rules.
+ */
+ public function __call($method, $args)
+ {
+ if ($method == 'rules')
+ {
+ if (empty($args))
+ return $this->rules;
+
+ // Set rules and action
+ $rules = $args[0];
+ $action = substr($rules, 0, 1);
+
+ if (in_array($action, array('-', '+', '=')))
+ {
+ // Remove the action from the rules
+ $rules = substr($rules, 1);
+ }
+ else
+ {
+ // Default action is append
+ $action = '';
+ }
+
+ $this->add_rules(explode('|', $rules), $action);
+ }
+ elseif ($method == 'name')
+ {
+ // Do nothing. The name should stay static once it is set.
+ }
+ else
+ {
+ $this->data[$method] = $args[0];
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns form attributes.
+ *
+ * @param string attribute name
+ * @return string
+ */
+ public function __get($key)
+ {
+ if (isset($this->data[$key]))
+ {
+ return $this->data[$key];
+ }
+ }
+
+ /**
+ * Sets a form element that this element must match the value of.
+ *
+ * @chainable
+ * @param object another Forge input
+ * @return object
+ */
+ public function matches($input)
+ {
+ if ( ! in_array($input, $this->matches, TRUE))
+ {
+ $this->matches[] = $input;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sets a callback method as a rule for this input.
+ *
+ * @chainable
+ * @param callback
+ * @return object
+ */
+ public function callback($callback)
+ {
+ if ( ! in_array($callback, $this->callbacks, TRUE))
+ {
+ $this->callbacks[] = $callback;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sets or returns the input label.
+ *
+ * @chainable
+ * @param string label to set
+ * @return string|object
+ */
+ public function label($val = NULL)
+ {
+ if ($val === NULL)
+ {
+ if (isset($this->data['name']) AND isset($this->data['label']))
+ {
+ return form::label($this->data['name'], $this->data['label']);
+ }
+ return FALSE;
+ }
+ else
+ {
+ $this->data['label'] = ($val === TRUE) ? utf8::ucwords(inflector::humanize($this->name)) : $val;
+ return $this;
+ }
+ }
+
+ /**
+ * Set or return the error message.
+ *
+ * @chainable
+ * @param string error message
+ * @return strong|object
+ */
+ public function message($val = NULL)
+ {
+ if ($val === NULL)
+ {
+ if (isset($this->data['message']))
+ return $this->data['message'];
+ }
+ else
+ {
+ $this->data['message'] = $val;
+ return $this;
+ }
+ }
+
+ /**
+ * Runs validation and returns the element HTML.
+ *
+ * @return string
+ */
+ public function render()
+ {
+ // Make sure validation runs
+ $this->validate();
+
+ return $this->html_element();
+ }
+
+ /**
+ * Returns the form input HTML.
+ *
+ * @return string
+ */
+ protected function html_element()
+ {
+ $data = $this->data;
+
+ unset($data['label']);
+ unset($data['message']);
+
+ return form::input($data);
+ }
+
+ /**
+ * Replace, remove, or append rules.
+ *
+ * @param array rules to change
+ * @param string action to use: replace, remove, append
+ */
+ protected function add_rules( array $rules, $action)
+ {
+ if ($action === '=')
+ {
+ // Just replace the rules
+ $this->rules = $rules;
+ return;
+ }
+
+ foreach ($rules as $rule)
+ {
+ if ($action === '-')
+ {
+ if (($key = array_search($rule, $this->rules)) !== FALSE)
+ {
+ // Remove the rule
+ unset($this->rules[$key]);
+ }
+ }
+ else
+ {
+ if ( ! in_array($rule, $this->rules))
+ {
+ if ($action == '+')
+ {
+ array_unshift($this->rules, $rule);
+ }
+ else
+ {
+ $this->rules[] = $rule;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Add an error to the input.
+ *
+ * @chainable
+ * @return object
+ */
+ public function add_error($key, $val)
+ {
+ if ( ! isset($this->errors[$key]))
+ {
+ $this->errors[$key] = $val;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set or return the error messages.
+ *
+ * @chainable
+ * @param string|array failed validation function, or an array of messages
+ * @param string error message
+ * @return object|array
+ */
+ public function error_messages($func = NULL, $message = NULL)
+ {
+ // Set custom error messages
+ if ( ! empty($func))
+ {
+ if (is_array($func))
+ {
+ // Replace all
+ $this->error_messages = $func;
+ }
+ else
+ {
+ if (empty($message))
+ {
+ // Single error, replaces all others
+ $this->error_messages = $func;
+ }
+ else
+ {
+ // Add custom error
+ $this->error_messages[$func] = $message;
+ }
+ }
+ return $this;
+ }
+
+ // Make sure validation runs
+ is_null($this->is_valid) and $this->validate();
+
+ // Return single error
+ if ( ! is_array($this->error_messages) AND ! empty($this->errors))
+ return array($this->error_messages);
+
+ $messages = array();
+ foreach ($this->errors as $func => $args)
+ {
+ if (is_string($args))
+ {
+ $error = $args;
+ }
+ else
+ {
+ // Force args to be an array
+ $args = is_array($args) ? $args : array();
+
+ // Add the label or name to the beginning of the args
+ array_unshift($args, $this->label ? utf8::strtolower($this->label) : $this->name);
+
+ if (isset($this->error_messages[$func]))
+ {
+ // Use custom error message
+ $error = vsprintf($this->error_messages[$func], $args);
+ }
+ else
+ {
+ // Get the proper i18n entry, very hacky but it works
+ switch ($func)
+ {
+ case 'valid_url':
+ case 'valid_email':
+ case 'valid_ip':
+ // Fetch an i18n error message
+ $error = Kohana::lang('validation.'.$func, $args);
+ break;
+ case substr($func, 0, 6) === 'valid_':
+ // Strip 'valid_' from func name
+ $func = (substr($func, 0, 6) === 'valid_') ? substr($func, 6) : $func;
+ case 'alpha':
+ case 'alpha_dash':
+ case 'digit':
+ case 'numeric':
+ // i18n strings have to be inserted into valid_type
+ $args[] = Kohana::lang('validation.'.$func);
+ $error = Kohana::lang('validation.valid_type', $args);
+ break;
+ default:
+ $error = Kohana::lang('validation.'.$func, $args);
+ }
+ }
+ }
+
+ // Add error to list
+ $messages[] = $error;
+ }
+
+ return $messages;
+ }
+
+ /**
+ * Get the global input value.
+ *
+ * @return string|bool
+ */
+ protected function input_value($name = array())
+ {
+ // Get the Input instance
+ $input = Input::instance();
+
+ // Fetch the method for this object
+ $method = $this->method;
+
+ return $input->$method($name, NULL);
+ }
+
+ /**
+ * Load the value of the input, if form data is present.
+ *
+ * @return void
+ */
+ protected function load_value()
+ {
+ if (is_bool($this->is_valid))
+ return;
+
+ if ($name = $this->name)
+ {
+ // Load POSTed value, but only for named inputs
+ $this->data['value'] = $this->input_value($name);
+ }
+
+ if (is_string($this->data['value']))
+ {
+ // Trim string values
+ $this->data['value'] = trim($this->data['value']);
+ }
+ }
+
+ /**
+ * Validate this input based on the set rules.
+ *
+ * @return bool
+ */
+ public function validate()
+ {
+ // Validation has already run
+ if (is_bool($this->is_valid))
+ return $this->is_valid;
+
+ // No data to validate
+ if ($this->input_value() == FALSE)
+ return $this->is_valid = FALSE;
+
+ // Load the submitted value
+ $this->load_value();
+
+ // No rules to validate
+ if (count($this->rules) == 0 AND count($this->matches) == 0 AND count($this->callbacks) == 0)
+ return $this->is_valid = TRUE;
+
+ if ( ! empty($this->rules))
+ {
+ foreach ($this->rules as $rule)
+ {
+ if (($offset = strpos($rule, '[')) !== FALSE)
+ {
+ // Get the args
+ $args = preg_split('/, ?/', trim(substr($rule, $offset), '[]'));
+
+ // Remove the args from the rule
+ $rule = substr($rule, 0, $offset);
+ }
+
+ if (substr($rule, 0, 6) === 'valid_' AND method_exists('valid', substr($rule, 6)))
+ {
+ $func = substr($rule, 6);
+
+ if ($this->value AND ! valid::$func($this->value))
+ {
+ $this->errors[$rule] = TRUE;
+ }
+ }
+ elseif (method_exists($this, 'rule_'.$rule))
+ {
+ // The rule function is always prefixed with rule_
+ $rule = 'rule_'.$rule;
+
+ if (isset($args))
+ {
+ // Manually call up to 2 args for speed
+ switch (count($args))
+ {
+ case 1:
+ $this->$rule($args[0]);
+ break;
+ case 2:
+ $this->$rule($args[0], $args[1]);
+ break;
+ default:
+ call_user_func_array(array($this, $rule), $args);
+ break;
+ }
+ }
+ else
+ {
+ // Just call the rule
+ $this->$rule();
+ }
+
+ // Prevent args from being re-used
+ unset($args);
+ }
+ else
+ {
+ throw new Kohana_Exception('validation.invalid_rule', $rule);
+ }
+
+ // Stop when an error occurs
+ if ( ! empty($this->errors))
+ break;
+ }
+ }
+
+ if ( ! empty($this->matches))
+ {
+ foreach ($this->matches as $input)
+ {
+ if ($this->value != $input->value)
+ {
+ // Field does not match
+ $this->errors['matches'] = array($input->label ? utf8::strtolower($input->label) : $input->name);
+ break;
+ }
+ }
+ }
+
+ if ( ! empty($this->callbacks))
+ {
+ foreach ($this->callbacks as $callback)
+ {
+ call_user_func($callback, $this);
+
+ // Stop when an error occurs
+ if ( ! empty($this->errors))
+ break;
+ }
+ }
+
+ // If there are errors, validation failed
+ return $this->is_valid = empty($this->errors);
+ }
+
+ /**
+ * Validate required.
+ */
+ protected function rule_required()
+ {
+ if ($this->value === '' OR $this->value === NULL)
+ {
+ $this->errors['required'] = TRUE;
+ }
+ }
+
+ /**
+ * Validate length.
+ */
+ protected function rule_length($min, $max = NULL)
+ {
+ // Get the length, return if zero
+ if (($length = utf8::strlen($this->value)) === 0)
+ return;
+
+ if ($max == NULL)
+ {
+ if ($length != $min)
+ {
+ $this->errors['exact_length'] = array($min);
+ }
+ }
+ else
+ {
+ if ($length < $min)
+ {
+ $this->errors['min_length'] = array($min);
+ }
+ elseif ($length > $max)
+ {
+ $this->errors['max_length'] = array($max);
+ }
+ }
+ }
+
+} // End Form Input \ No newline at end of file
diff --git a/modules/forge/libraries/Form_Password.php b/modules/forge/libraries/Form_Password.php
new file mode 100644
index 00000000..ac4dd8ae
--- /dev/null
+++ b/modules/forge/libraries/Form_Password.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * FORGE password input library.
+ *
+ * $Id$
+ *
+ * @package Forge
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class Form_Password_Core extends Form_Input {
+
+ protected $data = array
+ (
+ 'type' => 'password',
+ 'class' => 'password',
+ 'value' => '',
+ );
+
+ protected $protect = array('type');
+
+} // End Form Password \ No newline at end of file
diff --git a/modules/forge/libraries/Form_Phonenumber.php b/modules/forge/libraries/Form_Phonenumber.php
new file mode 100644
index 00000000..e30f47b1
--- /dev/null
+++ b/modules/forge/libraries/Form_Phonenumber.php
@@ -0,0 +1,98 @@
+<?php
+/**
+ * FORGE phone number input library.
+ *
+ * $Id$
+ *
+ * @package Forge
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class Form_Phonenumber_Core extends Form_Input {
+
+ protected $data = array
+ (
+ 'name' => '',
+ 'class' => 'phone_number',
+ );
+
+ protected $protect = array('type');
+
+ // Precision for the parts, you can use @ to insert a literal @ symbol
+ protected $parts = array
+ (
+ 'area_code' => '',
+ 'exchange' => '',
+ 'last_four' => '',
+ );
+
+ public function __construct($name)
+ {
+ // Set name
+ $this->data['name'] = $name;
+ }
+
+ public function __call($method, $args)
+ {
+ if (isset($this->parts[substr($method, 0, -1)]))
+ {
+ // Set options for date generation
+ $this->parts[substr($method, 0, -1)] = $args;
+ return $this;
+ }
+
+ return parent::__call($method, $args);
+ }
+
+ public function html_element()
+ {
+ // Import base data
+ $data = $this->data;
+
+ $input = '';
+ foreach ($this->parts as $type => $val)
+ {
+ isset($data['value']) OR $data['value'] = '';
+ $temp = $data;
+ $temp['name'] = $this->data['name'].'['.$type.']';
+ $offset = (strlen($data['value']) == 10) ? 0 : 3;
+ switch ($type)
+ {
+ case 'area_code':
+ if (strlen($data['value']) == 10)
+ {
+ $temp['value'] = substr($data['value'], 0, 3);
+ }
+ else
+ $temp['value'] = '';
+ $temp['class'] = 'area_code';
+ $input .= form::input(array_merge(array('value' => $val), $temp)).'-';
+ break;
+ case 'exchange':
+ $temp['value'] = substr($data['value'], (3-$offset), 3);
+ $temp['class'] = 'exchange';
+ $input .= form::input(array_merge(array('value' => $val), $temp)).'-';
+ break;
+ case 'last_four':
+ $temp['value'] = substr($data['value'], (6-$offset), 4);
+ $temp['class'] = 'last_four';
+ $input .= form::input(array_merge(array('value' => $val), $temp));
+ break;
+ }
+
+ }
+
+ return $input;
+ }
+
+ protected function load_value()
+ {
+ if (is_bool($this->valid))
+ return;
+
+ $data = $this->input_value($this->name, $this->data['name']);
+
+ $this->data['value'] = $data['area_code'].$data['exchange'].$data['last_four'];
+ }
+} // End Form Phonenumber \ No newline at end of file
diff --git a/modules/forge/libraries/Form_Radio.php b/modules/forge/libraries/Form_Radio.php
new file mode 100644
index 00000000..f88632f2
--- /dev/null
+++ b/modules/forge/libraries/Form_Radio.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ * FORGE radio input library.
+ *
+ * $Id$
+ *
+ * @package Forge
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class Form_Radio_Core extends Form_Checkbox {
+
+ protected $data = array
+ (
+ 'type' => 'radio',
+ 'class' => 'radio',
+ 'value' => '1',
+ 'checked' => FALSE,
+ );
+
+} // End Form_Radio \ No newline at end of file
diff --git a/modules/forge/libraries/Form_Submit.php b/modules/forge/libraries/Form_Submit.php
new file mode 100644
index 00000000..527580c9
--- /dev/null
+++ b/modules/forge/libraries/Form_Submit.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * FORGE submit input library.
+ *
+ * $Id$
+ *
+ * @package Forge
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class Form_Submit_Core extends Form_Input {
+
+ protected $data = array
+ (
+ 'type' => 'submit',
+ 'class' => 'submit'
+ );
+
+ protected $protect = array('type');
+
+ public function __construct($value)
+ {
+ $this->data['value'] = $value;
+ }
+
+ public function render()
+ {
+ $data = $this->data;
+ unset($data['label']);
+
+ return form::button($data);
+ }
+
+ public function validate()
+ {
+ // Submit buttons do not need to be validated
+ return $this->is_valid = TRUE;
+ }
+
+} // End Form Submit \ No newline at end of file
diff --git a/modules/forge/libraries/Form_Textarea.php b/modules/forge/libraries/Form_Textarea.php
new file mode 100644
index 00000000..f6d28fd0
--- /dev/null
+++ b/modules/forge/libraries/Form_Textarea.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * FORGE textarea input library.
+ *
+ * $Id$
+ *
+ * @package Forge
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class Form_Textarea_Core extends Form_Input {
+
+ protected $data = array
+ (
+ 'class' => 'textarea',
+ 'value' => '',
+ );
+
+ protected $protect = array('type');
+
+ protected function html_element()
+ {
+ $data = $this->data;
+
+ unset($data['label']);
+
+ return form::textarea($data);
+ }
+
+} // End Form Textarea \ No newline at end of file
diff --git a/modules/forge/libraries/Form_Upload.php b/modules/forge/libraries/Form_Upload.php
new file mode 100644
index 00000000..dce8816e
--- /dev/null
+++ b/modules/forge/libraries/Form_Upload.php
@@ -0,0 +1,187 @@
+<?php
+/**
+ * FORGE upload input library.
+ *
+ * $Id$
+ *
+ * @package Forge
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class Form_Upload_Core extends Form_Input {
+
+ protected $data = array
+ (
+ 'class' => 'upload',
+ 'value' => '',
+ );
+
+ protected $protect = array('type', 'label', 'value');
+
+ // Upload data
+ protected $upload;
+
+ // Upload directory and filename
+ protected $directory;
+ protected $filename;
+
+ public function __construct($name, $filename = FALSE)
+ {
+ parent::__construct($name);
+
+ if ( ! empty($_FILES[$name]))
+ {
+ if (empty($_FILES[$name]['tmp_name']) OR is_uploaded_file($_FILES[$name]['tmp_name']))
+ {
+ // Cache the upload data in this object
+ $this->upload = $_FILES[$name];
+
+ // Hack to allow file-only inputs, where no POST data is present
+ $_POST[$name] = $this->upload['name'];
+
+ // Set the filename
+ $this->filename = empty($filename) ? FALSE : $filename;
+ }
+ else
+ {
+ // Attempt to delete the invalid file
+ is_writable($_FILES[$name]['tmp_name']) and unlink($_FILES[$name]['tmp_name']);
+
+ // Invalid file upload, possible hacking attempt
+ unset($_FILES[$name]);
+ }
+ }
+ }
+
+ /**
+ * Sets the upload directory.
+ *
+ * @param string upload directory
+ * @return void
+ */
+ public function directory($dir = NULL)
+ {
+ // Use the global upload directory by default
+ empty($dir) and $dir = Kohana::config('upload.directory');
+
+ // Make the path asbolute and normalize it
+ $directory = str_replace('\\', '/', realpath($dir)).'/';
+
+ // Make sure the upload director is valid and writable
+ if ($directory === '/' OR ! is_dir($directory) OR ! is_writable($directory))
+ throw new Kohana_Exception('upload.not_writable', $dir);
+
+ $this->directory = $directory;
+ }
+
+ public function validate()
+ {
+ // The upload directory must always be set
+ empty($this->directory) and $this->directory();
+
+ // By default, there is no uploaded file
+ $filename = '';
+
+ if ($status = parent::validate() AND $this->upload['error'] === UPLOAD_ERR_OK)
+ {
+ // Set the filename to the original name
+ $filename = $this->upload['name'];
+
+ if (Kohana::config('upload.remove_spaces'))
+ {
+ // Remove spaces, due to global upload configuration
+ $filename = preg_replace('/\s+/', '_', $this->data['value']);
+ }
+
+ if (file_exists($filepath = $this->directory.$filename))
+ {
+ if ($this->filename !== TRUE OR ! is_writable($filepath))
+ {
+ // Prefix the file so that the filename is unique
+ $filepath = $this->directory.'uploadfile-'.uniqid(time()).'-'.$this->upload['name'];
+ }
+ }
+
+ // Move the uploaded file to the upload directory
+ move_uploaded_file($this->upload['tmp_name'], $filepath);
+ }
+
+ if ( ! empty($_POST[$this->data['name']]))
+ {
+ // Reset the POST value to the new filename
+ $this->data['value'] = $_POST[$this->data['name']] = empty($filepath) ? '' : $filepath;
+ }
+
+ return $status;
+ }
+
+ protected function rule_required()
+ {
+ if (empty($this->upload) OR $this->upload['error'] === UPLOAD_ERR_NO_FILE)
+ {
+ $this->errors['required'] = TRUE;
+ }
+ }
+
+ public function rule_allow()
+ {
+ if (empty($this->upload['tmp_name']) OR count($types = func_get_args()) == 0)
+ return;
+
+ if (($mime = file::mime($this->upload['tmp_name'])) === FALSE)
+ {
+ // Trust the browser
+ $mime = $this->upload['type'];
+ }
+
+ // Allow nothing by default
+ $allow = FALSE;
+
+ foreach ($types as $type)
+ {
+ // Load the mime types
+ $type = Kohana::config('mimes.'.$type);
+
+ if (is_array($type) AND in_array($mime, $type))
+ {
+ // Type is valid
+ $allow = TRUE;
+ break;
+ }
+ }
+
+ if ($allow === FALSE)
+ {
+ $this->errors['invalid_type'] = TRUE;
+ }
+ }
+
+ public function rule_size($size)
+ {
+ // Skip the field if it is empty
+ if (empty($this->upload) OR $this->upload['error'] === UPLOAD_ERR_NO_FILE)
+ return;
+
+ $bytes = (int) $size;
+
+ switch (substr($size, -2))
+ {
+ case 'GB': $bytes *= 1024;
+ case 'MB': $bytes *= 1024;
+ case 'KB': $bytes *= 1024;
+ default: break;
+ }
+
+ if (empty($this->upload['size']) OR $this->upload['size'] > $bytes)
+ {
+ $this->errors['max_size'] = array($size);
+ }
+ }
+
+ protected function html_element()
+ {
+ return form::upload($this->data);
+ }
+
+} // End Form Upload
diff --git a/modules/forge/models/user_edit.php b/modules/forge/models/user_edit.php
new file mode 100644
index 00000000..c8cf9594
--- /dev/null
+++ b/modules/forge/models/user_edit.php
@@ -0,0 +1,132 @@
+<?php
+
+class User_Edit_Model extends User_Model {
+
+ // Overload the class
+ protected $class = 'user';
+
+ // Forge instance
+ protected $form;
+
+ public function __construct($action, $title, $id = FALSE)
+ {
+ // Load the user
+ parent::__construct($id);
+
+ // Create the form
+ $this->form = new Forge($action, $title);
+
+ $this->form->input('username')->label(TRUE)->rules('required|length[5,32]')->value($this->object->username);
+ $this->form->input('email')->label(TRUE)->rules('required|length[5,127]|valid_email')->value($this->object->email);
+ $this->form->password('password')->label(TRUE)->rules('length[5,64]');
+ $this->form->password('confirm')->label(TRUE)->matches($this->form->password);
+
+ // Make sure that the username does not already exist
+ $this->form->username->callback(array($this, 'is_existing_user'));
+
+ if ($this->object->id == 0)
+ {
+ // Password fields are required for new users
+ $this->form->password->rules('+required');
+ }
+
+ // // Find all roles
+ // $roles = new Role_Model;
+ // $roles = $roles->find(ALL);
+ //
+ // $options = array();
+ // foreach ($roles as $role)
+ // {
+ // // Add each role to the options
+ // $options[$role->name] = isset($this->roles[$role->id]);
+ // }
+ //
+ // // Create a checklist of roles
+ // $this->form->checklist('roles')->options($options)->label(TRUE);
+
+ // Add the save button
+ $this->form->submit('Save');
+ }
+
+ public function is_existing_user($input)
+ {
+ if ($this->object->username == $input->value)
+ return TRUE;
+
+ if (self::$db->count_records($this->table, array('username' => $input->value)) > 0)
+ {
+ $input->add_error(__FUNCTION__, 'The username <strong>'.$input->value.'</strong> is already in use.');
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ public function save()
+ {
+ if ($this->form->validate() AND $data = $this->form->as_array())
+ {
+ if (empty($data['password']))
+ {
+ // Remove the empty password so it's not reset
+ unset($data['password'], $data['confirm']);
+ }
+
+ // Need to set this before saving
+ $new_user = ($this->object->id == 0);
+
+ // Remove the roles from data
+ isset($data['roles']) and $roles = arr::remove('roles', $data);
+
+ foreach ($data as $field => $val)
+ {
+ // Set object data from the form
+ $this->$field = $val;
+ }
+
+ if ($status = parent::save())
+ {
+ // if ($new_user)
+ // {
+ // foreach ($roles as $role)
+ // {
+ // // Add the user roles
+ // $this->add_role($role);
+ // }
+ // }
+ // else
+ // {
+ // foreach (array_diff($this->roles, $roles) as $role)
+ // {
+ // // Remove roles that were deactivated
+ // $this->remove_role($role);
+ // }
+ //
+ // foreach (array_diff($roles, $this->roles) as $role)
+ // {
+ // // Add new roles
+ // $this->add_role($role);
+ // }
+ // }
+ }
+
+ // Return the save status
+ return $status;
+ }
+
+ return FALSE;
+ }
+
+ public function render()
+ {
+ // Proxy to form html
+ return $this->form->render();
+ }
+
+ public function __toString()
+ {
+ // Proxy to form html
+ return $this->form->render();
+ }
+
+} // End User Edit Model \ No newline at end of file
diff --git a/modules/forge/views/forge_template.php b/modules/forge/views/forge_template.php
new file mode 100644
index 00000000..d71b16f7
--- /dev/null
+++ b/modules/forge/views/forge_template.php
@@ -0,0 +1,69 @@
+<?php echo $open; ?>
+<table class="<?php echo $class ?>">
+<?php if ($title != ''): ?>
+<caption><?php echo $title ?></caption>
+<?php endif ?>
+<?php
+foreach ($inputs as $input):
+
+$sub_inputs = array();
+if ($input->type == 'group'):
+ $sub_inputs = $input->inputs;
+
+?>
+<tr>
+<th colspan="2"><?php echo $input->label() ?></th>
+</tr>
+<?php
+
+ if ($message = $input->message()):
+
+?>
+<tr>
+<td colspan="2"><p class="group_message"><?php echo $message ?></p></td>
+</tr>
+<?php
+
+ endif;
+
+else:
+ $sub_inputs = array($input);
+endif;
+
+foreach ($sub_inputs as $input):
+
+?>
+<tr>
+<th><?php echo $input->label() ?></th>
+<td>
+<?php
+
+echo $input->render();
+
+if ($message = $input->message()):
+
+?>
+<p class="message"><?php echo $message ?></p>
+<?php
+
+endif;
+
+foreach ($input->error_messages() as $error):
+
+?>
+<p class="error"><?php echo $error ?></p>
+<?php
+
+endforeach;
+
+?>
+</td>
+</tr>
+<?php
+
+endforeach;
+
+endforeach;
+?>
+</table>
+<?php echo $close ?> \ No newline at end of file