Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added datamapper

Signed-off-by: Purwandi <free6300@gmail.com>
  • Loading branch information...
commit 06e5800a9b487102e057b14ef5db6b7225f01dd4 1 parent b224b84
Purwandi purwandi authored
Showing with 10,634 additions and 50 deletions.
  1. +83 −0 _assets/css/body.css
  2. +2 −2 application/config/autoload.php
  3. +3 −3 application/config/database.php
  4. +36 −0 application/config/datamapper.php
  5. +28 −0 application/controllers/login.php
  6. +0 −1  application/core/MY_Controller.php
  7. +53 −0 application/core/MY_Model.php
  8. +221 −0 application/extensions/array.php
  9. +225 −0 application/extensions/csv.php
  10. +798 −0 application/extensions/htmlform.php
  11. +227 −0 application/extensions/json.php
  12. +1,397 −0 application/extensions/nestedsets.php
  13. +211 −0 application/extensions/rowindex.php
  14. +70 −0 application/extensions/simplecache.php
  15. +76 −0 application/extensions/translate.php
  16. +6,677 −0 application/libraries/datamapper.php
  17. +5 −26 application/models/auth_m.php
  18. +11 −0 application/models/user_gas.php
  19. +90 −0 application/third_party/datamapper/bootstrap.php
  20. +162 −0 application/third_party/datamapper/system/DB.php
  21. +71 −0 application/third_party/datamapper/system/DB_driver.php
  22. +41 −0 application/third_party/datamapper/system/Lang.php
  23. +65 −0 application/third_party/datamapper/system/Loader.php
  24. +5 −11 application/views/layouts/default.php
  25. +5 −6 application/views/layouts/footer.php
  26. +27 −0 application/views/layouts/navbar.php
  27. +19 −0 application/views/login/index.php
  28. +19 −0 application/views/welcome/index.php
  29. +7 −1 index.php
83 _assets/css/body.css
View
@@ -0,0 +1,83 @@
+/* Body and structure
+-------------------------------------------------- */
+body {
+ position: relative;
+ padding-top: 60px;
+ background-color: #fff;
+}
+
+/* Faded out hr */
+hr.soften {
+ height: 1px;
+ margin: 54px 0;
+ background-image: -webkit-linear-gradient(left, rgba(0,0,0,0), rgba(0,0,0,.1), rgba(0,0,0,0));
+ background-image: -moz-linear-gradient(left, rgba(0,0,0,0), rgba(0,0,0,.1), rgba(0,0,0,0));
+ background-image: -ms-linear-gradient(left, rgba(0,0,0,0), rgba(0,0,0,.1), rgba(0,0,0,0));
+ background-image: -o-linear-gradient(left, rgba(0,0,0,0), rgba(0,0,0,.1), rgba(0,0,0,0));
+ border: 0;
+}
+
+/* Tweak navbar brand link to be super sleek
+-------------------------------------------------- */
+.navbar-fixed-top .brand {
+ padding-right: 0;
+ padding-left: 0;
+ margin: 0 auto;
+ font-weight: bold;
+ color: #000;
+ text-shadow: 0 1px 0 rgba(255,255,255,.1), 0 0 30px rgba(255,255,255,.125);
+ -webkit-transition: all .2s linear;
+ -moz-transition: all .2s linear;
+ transition: all .2s linear;
+}
+.navbar-fixed-top .brand:hover {
+ text-decoration: none;
+}
+
+
+/* Welcome
+-------------------------------------------------- */
+/* Masthead (docs home) */
+.masthead {
+ padding-top: 36px;
+ margin-bottom: 72px;
+}
+.masthead h1 {
+ margin-bottom: 9px;
+ font-size: 81px;
+ font-weight: bold;
+ letter-spacing: -1px;
+ line-height: 1;
+}
+.masthead h1,
+.masthead p {
+ text-align: center;
+}
+.masthead h1 {
+ margin-bottom: 18px;
+}
+.masthead p {
+ margin-left: 5%;
+ margin-right: 5%;
+ font-size: 30px;
+ line-height: 36px;
+}
+
+/* marketing
+-------------------------------------------------- */
+.marketing p {
+ text-align: center;
+}
+
+/* Footer
+-------------------------------------------------- */
+.footer {
+ margin-top: 45px;
+ padding: 10px 0 36px;
+ border-top: 1px solid #e5e5e5;
+}
+.footer p {
+ font-size: 12px;
+ margin-bottom: 0;
+ color: #555;
+}
4 application/config/autoload.php
View
@@ -78,7 +78,7 @@
| $autoload['libraries'] = array('database', 'session', 'xmlrpc');
*/
-$autoload['libraries'] = array();
+$autoload['libraries'] = array('session','database');
/*
@@ -135,7 +135,7 @@
|
*/
-$autoload['model'] = array();
+$autoload['model'] = array('auth_m');
/* End of file autoload.php */
6 application/config/database.php
View
@@ -77,9 +77,9 @@
$db['default']['dsn'] = '';
$db['default']['hostname'] = 'localhost';
-$db['default']['username'] = '';
-$db['default']['password'] = '';
-$db['default']['database'] = '';
+$db['default']['username'] = 'root';
+$db['default']['password'] = 'neki';
+$db['default']['database'] = 'wiki_codeigniter';
$db['default']['dbdriver'] = 'mysql';
$db['default']['dbprefix'] = '';
$db['default']['pconnect'] = FALSE;
36 application/config/datamapper.php
View
@@ -0,0 +1,36 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+
+/**
+ * Data Mapper Configuration
+ *
+ * Global configuration settings that apply to all DataMapped models.
+ */
+
+$config['prefix'] = '';
+$config['join_prefix'] = '';
+$config['error_prefix'] = '<p>';
+$config['error_suffix'] = '</p>';
+$config['created_field'] = 'created';
+$config['updated_field'] = 'updated';
+$config['local_time'] = FALSE;
+$config['unix_timestamp'] = FALSE;
+$config['timestamp_format'] = '';
+$config['lang_file_format'] = 'model_${model}';
+$config['field_label_lang_format'] = '${model}_${field}';
+$config['auto_transaction'] = FALSE;
+$config['auto_populate_has_many'] = FALSE;
+$config['auto_populate_has_one'] = FALSE;
+$config['all_array_uses_ids'] = FALSE;
+
+// set to FALSE to use the same DB instance across the board (breaks subqueries)
+// Set to any acceptable parameters to $CI->database() to override the default.
+$config['db_params'] = '';
+
+// Uncomment to enable the production cache
+// $config['production_cache'] = APPPATH.'cache';
+
+$config['extensions_path'] = '../extensions';
+$config['extensions'] = array();
+
+/* End of file datamapper.php */
+/* Location: ./sparks/Datamapper-ORM/config/datamapper.php */
28 application/controllers/login.php
View
@@ -0,0 +1,28 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+
+class Login extends MY_Controller{
+
+ protected $module = 'login';
+
+ public function index ()
+ {
+ if ($_POST)
+ {
+ $this->form_validation->set_rules('username','Username','trim|required');
+ $this->form_validation->set_rules('password','Password','trim|required');
+
+ if ($this->form_validation->run())
+ {
+ if ($this->auth_m->login())
+ {
+ setSucces('User finded');
+ }
+ else
+ {
+ setError('User not found');
+ }
+ }
+ }
+ parent :: index ();
+ }
+}
1  application/core/MY_Controller.php
View
@@ -33,7 +33,6 @@ public function __construct ()
// load spark
$this->load->spark('template/1.9.0');
-
$this->method = ucwords(preg_replace('/[_]+/', ' ', strtolower(trim($this->router->fetch_method()))));
53 application/core/MY_Model.php
View
@@ -0,0 +1,53 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+
+class MY_Model extends CI_Model {
+
+ public $table = '';
+ public $primary_key = '';
+ public $fields = array();
+
+ public function valid ($ignoreField = FALSE)
+ {
+ foreach ($this->fields as $key=>$values)
+ {
+ if (is_array($ignoreField) && in_array($key,$ignoreField)) continue;
+
+ $rules = 'trim';
+
+ if (count($values) > 1)
+ {
+ if ( $values[1] ) $rules .= '|required';
+ if ( isset($values[2]) ) $rules .= '|'.$values[2];
+ }
+ $this->form_validation->set_rules($key, $values[0], $rules);
+ }
+ return $this->form_validation->run();
+ }
+
+ /**
+ * Save and Edit Record
+ *
+ * @access public
+ * @param string
+ * @return object
+ */
+ public function save ($id = FALSE)
+ {
+ foreach ($this->fields as $key=>$values)
+ {
+ $this->db->set($key,$this->input->post($key));
+ }
+
+ if ( ! empty($id))
+ {
+ $this->db->where($this->idx,$id);
+ $this->db->update($this->tableName);
+ }
+
+ else
+ {
+ $this->db->insert($this->tableName);
+ }
+ return ($this->db->affected_rows() == 1);
+ }
+}
221 application/extensions/array.php
View
@@ -0,0 +1,221 @@
+<?php
+
+/**
+ * Array Extension for DataMapper classes.
+ *
+ * Quickly convert DataMapper models to-and-from PHP arrays.
+ *
+ * @license MIT License
+ * @package DMZ-Included-Extensions
+ * @category DMZ
+ * @author Phil DeJarnett
+ * @link http://www.overzealous.com/dmz/pages/extensions/array.html
+ * @version 1.0
+ */
+
+// --------------------------------------------------------------------------
+
+/**
+ * DMZ_Array Class
+ *
+ * @package DMZ-Included-Extensions
+ */
+class DMZ_Array {
+
+ /**
+ * Convert a DataMapper model into an associative array.
+ * If the specified fields includes a related object, the ids from the
+ * objects are collected into an array and stored on that key.
+ * This method does not recursively add objects.
+ *
+ * @param DataMapper $object The DataMapper Object to convert
+ * @param array $fields Array of fields to include. If empty, includes all database columns.
+ * @return array An associative array of the requested fields and related object ids.
+ */
+ function to_array($object, $fields = '')
+ {
+ // assume all database columns if $fields is not provided.
+ if(empty($fields))
+ {
+ $fields = $object->fields;
+ }
+ else
+ {
+ $fields = (array) $fields;
+ }
+
+ $result = array();
+
+ foreach($fields as $f)
+ {
+ // handle related fields
+ if(array_key_exists($f, $object->has_one) || array_key_exists($f, $object->has_many))
+ {
+ // each related item is stored as an array of ids
+ // Note: this method will NOT get() the related object.
+ $rels = array();
+ foreach($object->{$f} as $item)
+ {
+ $rels[] = $item->id;
+ }
+ $result[$f] = $rels;
+ }
+ else
+ {
+ // just the field.
+ $result[$f] = $object->{$f};
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Convert the entire $object->all array result set into an array of
+ * associative arrays.
+ *
+ * @see to_array
+ * @param DataMapper $object The DataMapper Object to convert
+ * @param array $fields Array of fields to include. If empty, includes all database columns.
+ * @return array An array of associative arrays.
+ */
+ function all_to_array($object, $fields = '')
+ {
+ // loop through each object in the $all array, convert them to
+ // an array, and add them to a new array.
+ $result = array();
+ foreach($object as $o)
+ {
+ $result[] = $o->to_array($fields);
+ }
+ return $result;
+ }
+
+ /**
+ * Convert a single field from the entire $object->all array result set into an a single array
+ * with the objects' id field as key
+ *
+ * @param DataMapper $object The DataMapper Object to convert
+ * @param string $field to include
+ * @return array An array of associative arrays.
+ */
+ function all_to_single_array($object, $field = '')
+ {
+ // loop through each object in the $all array, convert them to
+ // an array, and add them to a new array.
+ $result = array();
+ if ( ! empty($field) )
+ {
+ foreach($object as $o)
+ {
+ isset($o->{$field}) and $result[$o->id] = $o->{$field};
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Convert an associative array back into a DataMapper model.
+ *
+ * If $fields is provided, missing fields are assumed to be empty checkboxes.
+ *
+ * @param DataMapper $object The DataMapper Object to save to.
+ * @param array $data A an associative array of fields to convert.
+ * @param array $fields Array of 'safe' fields. If empty, only includes the database columns.
+ * @param bool $save If TRUE, then attempt to save the object automatically.
+ * @return array|bool A list of newly related objects, or the result of the save if $save is TRUE
+ */
+ function from_array($object, $data, $fields = '', $save = FALSE)
+ {
+ // keep track of newly related objects
+ $new_related_objects = array();
+
+ // Assume all database columns.
+ // In this case, simply store $fields that are in the $data array.
+ if(empty($fields))
+ {
+ $fields = $object->fields;
+ foreach($data as $k => $v) {
+ if(in_array($k, $fields))
+ {
+ $object->{$k} = $v;
+ }
+ }
+ }
+ else
+ {
+ // If $fields is provided, assume all $fields should exist.
+ foreach($fields as $f)
+ {
+ if(array_key_exists($f, $object->has_one))
+ {
+ // Store $has_one relationships
+ $c = get_class($object->{$f});
+ $rel = new $c();
+ $id = isset($data[$f]) ? $data[$f] : 0;
+ $rel->get_by_id($id);
+ if($rel->exists())
+ {
+ // The new relationship exists, save it.
+ $new_related_objects[$f] = $rel;
+ }
+ else
+ {
+ // The new relationship does not exist, delete the old one.
+ $object->delete($object->{$f}->get());
+ }
+ }
+ else if(array_key_exists($f, $object->has_many))
+ {
+ // Store $has_many relationships
+ $c = get_class($object->{$f});
+ $rels = new $c();
+ $ids = isset($data[$f]) ? $data[$f] : FALSE;
+ if(empty($ids))
+ {
+ // if no IDs were provided, delete all old relationships.
+ $object->delete($object->{$f}->select('id')->get()->all);
+ }
+ else
+ {
+ // Otherwise, get the new ones...
+ $rels->where_in('id', $ids)->select('id')->get();
+ // Store them...
+ $new_related_objects[$f] = $rels->all;
+ // And delete any old ones that do not exist.
+ $old_rels = $object->{$f}->where_not_in('id', $ids)->select('id')->get();
+ $object->delete($old_rels->all);
+ }
+ }
+ else
+ {
+ // Otherwise, if the $data was set, store it...
+ if(isset($data[$f]))
+ {
+ $v = $data[$f];
+ }
+ else
+ {
+ // Or assume it was an unchecked checkbox, and clear it.
+ $v = FALSE;
+ }
+ $object->{$f} = $v;
+ }
+ }
+ }
+ if($save)
+ {
+ // Auto save
+ return $object->save($new_related_objects);
+ }
+ else
+ {
+ // return new objects
+ return $new_related_objects;
+ }
+ }
+
+}
+
+/* End of file array.php */
+/* Location: ./application/datamapper/array.php */
225 application/extensions/csv.php
View
@@ -0,0 +1,225 @@
+<?php
+
+/**
+ * CSV Extension for DataMapper classes.
+ *
+ * Quickly import and export a set of DataMapper models to-and-from CSV files.
+ *
+ * @license MIT License
+ * @package DMZ-Included-Extensions
+ * @category DMZ
+ * @author Phil DeJarnett
+ * @link http://www.overzealous.com/dmz/pages/extensions/csv.html
+ * @version 1.0
+ */
+
+// --------------------------------------------------------------------------
+
+/**
+ * DMZ_CSV Class
+ *
+ * @package DMZ-Included-Extensions
+ */
+class DMZ_CSV {
+
+ /**
+ * Convert a DataMapper model into an associative array.
+ *
+ * @param DataMapper $object The DataMapper Object to export
+ * @param mixed filename The filename to export to, or a file pointer. If this is a file pointer, it will not be closed.
+ * @param array $fields Array of fields to include. If empty, includes all database columns.
+ * @param bool $include_header If FALSE the header is not exported with the CSV. Not recommended if planning to import this data.
+ * @return bool TRUE on success, or FALSE on failure.
+ */
+ function csv_export($object, $filename, $fields = '', $include_header = TRUE)
+ {
+ // determine the correct field set.
+ if(empty($fields))
+ {
+ $fields = $object->fields;
+ }
+
+ $success = TRUE;
+
+ // determine if we need to open the file or not.
+ if(is_string($filename))
+ {
+ // open the file, if possible.
+ $fp = fopen($filename, 'w');
+ if($fp === FALSE)
+ {
+ log_message('error', 'CSV Extension: Unable to open file ' . $filename);
+ return FALSE;
+ }
+ }
+ else
+ {
+ // assume file pointer.
+ $fp = $filename;
+ }
+
+ if($include_header)
+ {
+ // Print out header line
+ $success = fputcsv($fp, $fields);
+ }
+
+ if($success)
+ {
+ foreach($object as $o)
+ {
+ // convert each object into an array
+ $result = array();
+ foreach($fields as $f)
+ {
+ $result[] = $o->{$f};
+ }
+ // output CSV-formatted line
+ $success = fputcsv($fp, $result);
+ if(!$success)
+ {
+ // stop on first failure.
+ break;
+ }
+ }
+ }
+
+ if(is_string($filename))
+ {
+ fclose($fp);
+ }
+
+ return $success;
+ }
+
+ /**
+ * Import objects from a CSV file.
+ *
+ * Completely empty rows are automatically skipped, as are rows that
+ * start with a # sign (assumed to be comments).
+ *
+ * @param DataMapper $object The type of DataMapper Object to import
+ * @param mixed $filename Name of CSV file, or a file pointer.
+ * @param array $fields If empty, the database fields are used. Otherwise used to limit what fields are saved.
+ * @param boolean $header_row If true, the first line is assumed to be a header row. Defaults to true.
+ * @param mixed $callback A callback method for each row. Can return FALSE on failure to save, or 'stop' to stop the import.
+ * @return array Array of imported objects, or FALSE if unable to import.
+ */
+ function csv_import($object, $filename, $fields = '', $header_row = TRUE, $callback = NULL)
+ {
+ $class = get_class($object);
+
+ if(empty($fields))
+ {
+ $fields = $object->fields;
+ }
+
+ // determine if we need to open the file or not.
+ if(is_string($filename))
+ {
+ // open the file, if possible.
+ $fp = fopen($filename, 'r');
+ if($fp === FALSE)
+ {
+ log_message('error', 'CSV Extension: Unable to open file ' . $filename);
+ return FALSE;
+ }
+ }
+ else
+ {
+ // assume file pointer.
+ $fp = $filename;
+ }
+
+ if(empty($callback))
+ {
+ $result = array();
+ }
+ else
+ {
+ $result = 0;
+ }
+ $columns = NULL;
+
+ while(($data = fgetcsv($fp)) !== FALSE)
+ {
+ // get column names
+ if(is_null($columns))
+ {
+ if($header_row)
+ {
+ // store header row for column names
+ $columns = $data;
+ // only include columns in $fields
+ foreach($columns as $index => $name)
+ {
+ if( ! in_array($name, $fields))
+ {
+ // mark column as false to skip
+ $columns[$index] = FALSE;
+ }
+ }
+ continue;
+ }
+ else
+ {
+ $columns = $fields;
+ }
+ }
+
+ // skip on comments and empty rows
+ if(empty($data) || $data[0][0] == '#' || implode('', $data) == '')
+ {
+ continue;
+ }
+
+ // create the object to save
+ $o = new $class();
+ foreach($columns as $index => $key)
+ {
+ if(count($data) <= $index)
+ {
+ // more header columns than data columns
+ break;
+ }
+
+ // skip columns that were determined to not be needed above.
+ if($key === FALSE)
+ {
+ continue;
+ }
+
+ // finally, it's OK to save the data column.
+ $o->{$key} = $data[$index];
+ }
+
+ if( empty($callback))
+ {
+ $result[] = $o;
+ }
+ else
+ {
+ $test = call_user_func($callback, $o);
+ if($test === 'stop')
+ {
+ break;
+ }
+ if($test !== FALSE)
+ {
+ $result++;
+ }
+ }
+ }
+
+ if(is_string($filename))
+ {
+ fclose($fp);
+ }
+
+ return $result;
+ }
+
+}
+
+/* End of file csv.php */
+/* Location: ./application/datamapper/csv.php */
798 application/extensions/htmlform.php
View
@@ -0,0 +1,798 @@
+<?php
+
+
+/**
+ * HTMLForm Extension for DataMapper classes.
+ *
+ * A powerful extension that allows one to quickly
+ * generate standardized forms off a DMZ object.
+ *
+ * @license MIT License
+ * @package DMZ-Included-Extensions
+ * @category DMZ
+ * @author Phil DeJarnett
+ * @link http://www.overzealous.com/dmz/pages/extensions/htmlform.html
+ * @version 1.0
+ */
+
+// --------------------------------------------------------------------------
+
+/**
+ * DMZ_HTMLForm Class
+ *
+ * @package DMZ-Included-Extensions
+ */
+class DMZ_HTMLForm {
+
+ // this is the default template (view) to use for the overall form
+ var $form_template = 'dmz_htmlform/form';
+ // this is the default template (view) to use for the individual rows
+ var $row_template = 'dmz_htmlform/row';
+ // this is the default template (view) to use for the individual rows
+ var $section_template = 'dmz_htmlform/section';
+
+ var $auto_rule_classes = array(
+ 'integer' => 'integer',
+ 'numeric' => 'numeric',
+ 'is_natural' => 'natural',
+ 'is_natural_no_zero' => 'positive_int',
+ 'valid_email' => 'email',
+ 'valid_ip' => 'ip',
+ 'valid_base64' => 'base64',
+ 'valid_date' => 'date',
+ 'alpha_dash_dot' => 'alpha_dash_dot',
+ 'alpha_slash_dot' => 'alpha_slash_dot',
+ 'alpha' => 'alpha',
+ 'alpha_numeric' => 'alpha_numeric',
+ 'alpha_dash' => 'alpha_dash',
+ 'required' => 'required'
+ );
+
+ function __construct($options = array(), $object = NULL) {
+
+ if (is_array($options) )
+ {
+ foreach($options as $k => $v)
+ {
+ $this->{$k} = $v;
+ }
+ }
+
+ $this->CI =& get_instance();
+ $this->load = $this->CI->load;
+ }
+
+ // --------------------------------------------------------------------------
+
+ /**
+ * Render a single field. Can be used to chain together multiple fields in a column.
+ *
+ * @param object $object The DataMapper Object to use.
+ * @param string $field The field to render.
+ * @param string $type The type of field to render.
+ * @param array $options Various options to modify the output.
+ * @return Rendered String.
+ */
+ function render_field($object, $field, $type = NULL, $options = NULL)
+ {
+ $value = '';
+
+ if(array_key_exists($field, $object->has_one) || array_key_exists($field, $object->has_many))
+ {
+ // Create a relationship field
+ $one = array_key_exists($field, $object->has_one);
+
+ // attempt to look up the current value(s)
+ if( ! isset($options['value']))
+ {
+ if($this->CI->input->post($field))
+ {
+ $value = $this->CI->input->post($field);
+ }
+ else
+ {
+ // load the related object(s)
+ $sel = $object->{$field}->select('id')->get();
+ if($one)
+ {
+ // only a single value is allowed
+ $value = $sel->id;
+ }
+ else
+ {
+ // save what might be multiple values
+ $value = array();
+ foreach($sel as $s)
+ {
+ $value[] = $s->id;
+ }
+ }
+ }
+
+ }
+ else
+ {
+ // value was already set in the options
+ $value = $options['value'];
+ unset($options['value']);
+ }
+
+ // Attempt to get a list of possible values
+ if( ! isset($options['list']) || is_object($options['list']))
+ {
+ if( ! isset($options['list']))
+ {
+ // look up all of the related values
+ $c = get_class($object->{$field});
+ $total_items = new $c;
+ // See if the custom method is defined
+ if(method_exists($total_items, 'get_htmlform_list'))
+ {
+ // Get customized list
+ $total_items->get_htmlform_list($object, $field);
+ }
+ else
+ {
+ // Get all items
+ $total_items->get_iterated();
+ }
+ }
+ else
+ {
+ // process a passed-in DataMapper object
+ $total_items = $options['list'];
+ }
+ $list = array();
+ foreach($total_items as $item)
+ {
+ // use the __toString value of the item for the label
+ $list[$item->id] = (string)$item;
+ }
+ $options['list'] = $list;
+ }
+
+ // By if there can be multiple items, use a dropdown for large lists,
+ // and a set of checkboxes for a small one.
+ if($one || count($options['list']) > 6)
+ {
+ $default_type = 'dropdown';
+ if( ! $one && ! isset($options['size']))
+ {
+ // limit to no more than 8 items high.
+ $options['size'] = min(count($options['list']), 8);
+ }
+ }
+ else
+ {
+ $default_type = 'checkbox';
+ }
+ }
+ else
+ {
+ // attempt to look up the current value(s)
+ if( ! isset($options['value']))
+ {
+ if($this->CI->input->post($field))
+ {
+ $value = $this->CI->input->post($field);
+ // clear default if set
+ unset($options['default_value']);
+ }
+ else
+ {
+ if(isset($options['default_value']))
+ {
+ $value = $options['default_value'];
+ unset($options['default_value']);
+ }
+ else
+ {
+ // the field IS the value.
+ $value = $object->{$field};
+ }
+ }
+
+ }
+ else
+ {
+ // value was already set in the options
+ $value = $options['value'];
+ unset($options['value']);
+ }
+ // default to text
+ $default_type = ($field == 'id') ? 'hidden' : 'text';
+
+ // determine default attributes
+ $a = array();
+ // such as the size of the field.
+ $max = $this->_get_validation_rule($object, $field, 'max_length');
+ if($max === FALSE)
+ {
+ $max = $this->_get_validation_rule($object, $field, 'exact_length');
+ }
+ if($max !== FALSE)
+ {
+ $a['maxlength'] = $max;
+ $a['size'] = min($max, 30);
+ }
+ $list = $this->_get_validation_info($object, $field, 'values', FALSE);
+ if($list !== FALSE)
+ {
+ $a['list'] = $list;
+ }
+ $options = $options + $a;
+ $extra_class = array();
+
+ // Add any of the known rules as classes (for JS processing)
+ foreach($this->auto_rule_classes as $rule => $c)
+ {
+ if($this->_get_validation_rule($object, $field, $rule) !== FALSE)
+ {
+ $extra_class[] = $c;
+ }
+ }
+
+ // add or set the class on the field.
+ if( ! empty($extra_class))
+ {
+ $extra_class = implode(' ', $extra_class);
+ if(isset($options['class']))
+ {
+ $options['class'] .= ' ' . $extra_class;
+ }
+ else
+ {
+ $options['class'] = $extra_class;
+ }
+ }
+ }
+
+ // determine the renderer type
+ $type = $this->_get_type($object, $field, $type);
+ if(empty($type))
+ {
+ $type = $default_type;
+ }
+
+ // attempt to find the renderer function
+ if(method_exists($this, '_input_' . $type))
+ {
+ return $this->{'_input_' . $type}($object, $field, $value, $options);
+ }
+ else if(function_exists('input_' . $type))
+ {
+ return call_user_func('input_' . $type, $object, $field, $value, $options);
+ }
+ else
+ {
+ log_message('error', 'FormMaker: Unable to find a renderer for '.$type);
+ return '<span style="color: Maroon; background-color: White; font-weight: bold">FormMaker: UNABLE TO FIND A RENDERER FOR '.$type.'</span>';
+ }
+
+ }
+
+ // --------------------------------------------------------------------------
+
+ /**
+ * Render a row with a single field. If $field does not exist on
+ * $object->validation, then $field is output as-is.
+ *
+ * @param object $object The DataMapper Object to use.
+ * @param string $field The field to render (or content)
+ * @param string $type The type of field to render.
+ * @param array $options Various options to modify the output.
+ * @param string $row_template The template to use, or NULL to use the default.
+ * @return Rendered String.
+ */
+ function render_row($object, $field, $type = NULL, $options = array(), $row_template = NULL)
+ {
+ // try to determine type automatically
+ $type = $this->_get_type($object, $field, $type);
+
+ if( ! isset($object->validation[$field]) && (empty($type) || $type == 'section' || $type == 'none'))
+ {
+ // this could be a multiple-field row, or just some text.
+ // if $type is 'section, it will be rendered using the section template.
+ $error = '';
+ $label = '';
+ $content = $field;
+ $id = NULL;
+ }
+ else
+ {
+ // use validation information to render the field.
+ $content = $this->render_field($object, $field, $type, $options);
+ if(empty($row_template))
+ {
+ if($type == 'hidden' || $field == 'id')
+ {
+ $row_template = 'none';
+ }
+ else
+ {
+ $row_template = $this->row_template;
+ }
+ }
+ // determine if there is an existing error
+ $error = isset($object->error->{$field}) ? $object->error->{$field} : '';
+ // determine if there is a pre-defined label
+ $label = $this->_get_validation_info($object, $field, 'label', $field);
+ // the field IS the id
+ $id = $field;
+ }
+
+ $required = $this->_get_validation_rule($object, $field, 'required');
+
+ // Append these items. Values in $options have priority
+ $view_data = $options + array(
+ 'object' => $object,
+ 'content' => $content,
+ 'field' => $field,
+ 'label' => $label,
+ 'error' => $error,
+ 'id' => $id,
+ 'required' => $required
+ );
+
+ if(is_null($row_template))
+ {
+ if(empty($type))
+ {
+ $row_template = 'none';
+ }
+ else if($type == 'section')
+ {
+ $row_template = $this->section_template;
+ }
+ else
+ {
+ $row_template = $this->row_template;
+ }
+ }
+
+ if($row_template == 'none')
+ {
+ return $content;
+ }
+ else
+ {
+ return $this->load->view($row_template, $view_data, TRUE);
+ }
+ }
+
+ // --------------------------------------------------------------------------
+
+ /**
+ * Renders an entire form.
+ *
+ * @param object $object The DataMapper Object to use.
+ * @param string $fields An associative array that defines the form.
+ * @param string $template The template to use.
+ * @param string $row_template The template to use for rows.
+ * @param array $template_options The template to use for rows.
+ * @return Rendered String.
+ */
+ function render_form($object, $fields, $url = '', $options = array(), $template = NULL, $row_template = NULL)
+ {
+ if(empty($url))
+ {
+ // set url to current url
+ $url =$this->CI->uri->uri_string();
+ }
+
+ if(is_null($template))
+ {
+ $template = $this->form_template;
+ }
+
+ $rows = '';
+ foreach($fields as $field => $field_options)
+ {
+ $rows .= $this->_render_row_from_form($object, $field, $field_options, $row_template);
+ }
+
+ $view_data = $options + array(
+ 'object' => $object,
+ 'fields' => $fields,
+ 'url' => $url,
+ 'rows' => $rows
+ );
+
+ return $this->load->view($template, $view_data, TRUE);
+ }
+
+ // --------------------------------------------------------------------------
+ // Private Methods
+ // --------------------------------------------------------------------------
+
+ // Converts information from render_form into a row of objects.
+ function _render_row_from_form($object, $field, $options, $row_template, $row = TRUE)
+ {
+ if(is_int($field))
+ {
+ // simple form, or HTML-content
+ $field = $options;
+ $options = NULL;
+ }
+ if(is_null($options))
+ {
+ // always have an array for options
+ $options = array();
+ }
+
+ $type = '';
+ if( ! is_array($options))
+ {
+ // if options is a single string, assume it is the type.
+ $type = $options;
+ $options = array();
+ }
+
+ if(isset($options['type']))
+ {
+ // type was set in options
+ $type = $options['type'];
+ unset($options['type']);
+ }
+
+ // see if a different row_template was in the options
+ $rt = $row_template;
+ if(isset($options['template']))
+ {
+ $rt = $options['template'];
+ unset($options['template']);
+ }
+
+ // Multiple fields, render them all as one.
+ if(is_array($field))
+ {
+ if(isset($field['row_options']))
+ {
+ $options = $field['row_options'];
+ unset($field['row_options']);
+ }
+ $ret = '';
+ $sep = ' ';
+ if(isset($field['input_separator']))
+ {
+ $sep = $field['input_separator'];
+ unset($field['input_separator']);
+ }
+ foreach($field as $f => $fo)
+ {
+ // add each field to a list
+ if( ! empty($ret))
+ {
+ $ret .= $sep;
+ }
+ $ret .= $this->_render_row_from_form($object, $f, $fo, $row_template, FALSE);
+ }
+
+ // renders into a row or field below.
+ $field = $ret;
+ }
+ if($row)
+ {
+ // if row is set, render the whole row.
+ return $this->render_row($object, $field, $type, $options, $rt);
+ }
+ else
+ {
+ // render just the field.
+ return $this->render_field($object, $field, $type, $options);
+ }
+ }
+
+ // --------------------------------------------------------------------------
+
+ // Attempts to look up the field's type
+ function _get_type($object, $field, $type)
+ {
+ if(empty($type))
+ {
+ $type = $this->_get_validation_info($object, $field, 'type', NULL);
+ }
+ return $type;
+ }
+
+ // --------------------------------------------------------------------------
+
+ // Returns a field from the validation array
+ function _get_validation_info($object, $field, $val, $default = '')
+ {
+ if(isset($object->validation[$field][$val]))
+ {
+ return $object->validation[$field][$val];
+ }
+ return $default;
+ }
+
+ // --------------------------------------------------------------------------
+
+ // Returns the value (or TRUE) of the validation rule, or FALSE if it does not exist.
+ function _get_validation_rule($object, $field, $rule)
+ {
+ $r = $this->_get_validation_info($object, $field, 'rules', FALSE);
+ if($r !== FALSE)
+ {
+ if(isset($r[$rule]))
+ {
+ return $r[$rule];
+ }
+ else if(in_array($rule, $r, TRUE))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------------
+ // Input Types
+ // --------------------------------------------------------------------------
+
+ // Render a hidden input
+ function _input_hidden($object, $id, $value, $options)
+ {
+ return $this->_render_simple_input('hidden', $id, $value, $options);
+ }
+
+ // render a single-line text input
+ function _input_text($object, $id, $value, $options)
+ {
+ return $this->_render_simple_input('text', $id, $value, $options);
+ }
+
+ // render a password input
+ function _input_password($object, $id, $value, $options)
+ {
+ if(isset($options['send_value']))
+ {
+ unset($options['send_value']);
+ }
+ else
+ {
+ $value = '';
+ }
+ return $this->_render_simple_input('password', $id, $value, $options);
+ }
+
+ // render a multiline text input
+ function _input_textarea($object, $id, $value, $options)
+ {
+ if(isset($options['value']))
+ {
+ $value = $options['value'];
+ unset($options['value']);
+ }
+ $a = $options + array(
+ 'name' => $id,
+ 'id' => $id
+ );
+ return $this->_render_node('textarea', $a, htmlspecialchars($value));
+ }
+
+ // render a dropdown
+ function _input_dropdown($object, $id, $value, $options)
+ {
+ $list = $options['list'];
+ unset($options['list']);
+ $selected = $value;
+ if(isset($options['value']))
+ {
+ $selected = $options['value'];
+ unset($options['value']);
+ }
+ if( ! is_array($selected))
+ {
+ $selected = array($selected);
+ }
+ else
+ {
+ // force multiple
+ $options['multiple'] = 'multiple';
+ }
+ $l = $this->_options($list, $selected);
+
+ $name = $id;
+ if(isset($options['multiple']))
+ {
+ $name .= '[]';
+ }
+ $a = $options + array(
+ 'name' => $name,
+ 'id' => $id
+ );
+ return $this->_render_node('select', $a, $l);
+ }
+
+ // used to render an options list.
+ function _options($list, $sel)
+ {
+ $l = '';
+ foreach($list as $opt => $label)
+ {
+ if(is_array($label))
+ {
+ $l .= '<optgroup label="' . htmlspecialchars($key) . '">';
+ $l .= $this->_options($label, $sel);
+ $l .= '</optgroup>';
+ }
+ else
+ {
+ $a = array('value' => $opt);
+ if(in_array($opt, $sel))
+ {
+ $a['selected'] = 'selected';
+ }
+ $l .= $this->_render_node('option', $a, htmlspecialchars($label));
+ }
+ }
+ return $l;
+ }
+
+ // render a checkbox or series of checkboxes
+ function _input_checkbox($object, $id, $value, $options)
+ {
+ return $this->_checkbox('checkbox', $id, $value, $options);
+ }
+
+ // render a series of radio buttons
+ function _input_radio($object, $id, $value, $options)
+ {
+ return $this->_checkbox('radio', $id, $value, $options);
+ }
+
+ // renders one or more checkboxes or radio buttons
+ function _checkbox($type, $id, $value, $options, $sub_id = '', $label = '')
+ {
+ if(isset($options['value']))
+ {
+ $value = $options['value'];
+ unset($options['value']);
+ }
+ // if there is a list in options, render our multiple checkboxes.
+ if(isset($options['list']))
+ {
+ $list = $options['list'];
+ unset($options['list']);
+ $ret = '';
+ if( ! is_array($value))
+ {
+ if(is_null($value) || $value === FALSE || $value === '')
+ {
+ $value = array();
+ }
+ else
+ {
+ $value = array($value);
+ }
+ }
+ $sep = '<br/>';
+ if(isset($options['input_separator']))
+ {
+ $sep = $options['input_separator'];
+ unset($options['input_separator']);
+ }
+ foreach($list as $k => $v)
+ {
+ if( ! empty($ret))
+ {
+ // put each node on one line.
+ $ret .= $sep;
+ }
+ $ret .= $this->_checkbox($type, $id, $value, $options, $k, $v);
+ }
+ return $ret;
+ }
+ else
+ {
+ // just render the single checkbox.
+ $node_id = $id;
+ if( ! empty($sub_id))
+ {
+ // there are multiple nodes with this id, append the sub_id
+ $node_id .= '_' . $sub_id;
+ $field_value = $sub_id;
+ }
+ else
+ {
+ // sub_id is the same as the node's id
+ $sub_id = $id;
+ $field_value = '1';
+ }
+ $name = $id;
+ if(is_array($value))
+ {
+ // allow for multiple results
+ $name .= '[]';
+ }
+ // node attributes
+ $a = $options + array(
+ 'type' => $type,
+ 'id' => $node_id,
+ 'name' => $name,
+ 'value' => $field_value
+ );
+ // if checked wasn't overridden
+ if( ! isset($a['checked']))
+ {
+ // determine if this is a multiple checkbox or not.
+ $checked = $value;
+ if(is_array($checked))
+ {
+ $checked = in_array($sub_id, $value);
+ }
+ if($checked)
+ {
+ $a['checked'] = 'checked';
+ }
+ }
+ $ret = $this->_render_node('input', $a);
+ if( ! empty($label))
+ {
+ $ret .= ' ' . $this->_render_node('label', array('for' => $node_id), $label);
+ }
+ return $ret;
+ }
+ }
+
+ // render a file upload input
+ function _input_file($object, $id, $value, $options)
+ {
+ $a = $options + array(
+ 'type' => 'file',
+ 'name' => $id,
+ 'id' => $id
+ );
+ return $this->_render_node('input', $a);
+ }
+
+ // Utility method to render a normal <input>
+ function _render_simple_input($type, $id, $value, $options)
+ {
+ $a = $options + array(
+ 'type' => $type,
+ 'name' => $id,
+ 'id' => $id,
+ 'value' => $value
+ );
+ return $this->_render_node('input', $a);
+ }
+
+ // Utility method to render a node.
+ function _render_node($type, $attributes, $content = FALSE)
+ {
+ // generate node
+ $res = '<' . $type;
+ foreach($attributes as $att => $v)
+ {
+ // the special attribute '_' is rendered directly.
+ if($att == '_')
+ {
+ $res .= ' ' . $v;
+ }
+ else
+ {
+ if($att != 'label')
+ {
+ $res .= ' ' . $att . '="' . htmlspecialchars((string)$v) . '"';
+ }
+ }
+ }
+ // allow for content-containing nodes
+ if($content !== FALSE)
+ {
+ $res .= '>' . $content . '</' . $type .'>';
+ }
+ else
+ {
+ $res .= ' />';
+ }
+ return $res;
+ }
+
+}
+
+/* End of file htmlform.php */
+/* Location: ./application/datamapper/htmlform.php */
227 application/extensions/json.php
View
@@ -0,0 +1,227 @@
+<?php
+
+/**
+ * Json Extension for DataMapper classes.
+ *
+ * Quickly convert DataMapper models to-and-from JSON syntax.
+ *
+ * @license MIT License
+ * @package DMZ-Included-Extensions
+ * @category DMZ
+ * @author Phil DeJarnett
+ * @link http://www.overzealous.com/dmz/pages/extensions/json.html
+ * @version 1.1
+ */
+
+// --------------------------------------------------------------------------
+
+/**
+ * DMZ_Json Class
+ *
+ * @package DMZ-Included-Extensions
+ */
+class DMZ_Json {
+
+ /**
+ * Convert a DataMapper model into JSON code.
+ *
+ * @param DataMapper $object The DataMapper Object to convert
+ * @param array $fields Array of fields to include. If empty, includes all database columns.
+ * @param boolean $pretty_print Format the JSON code for legibility.
+ * @return string A JSON formatted String, or FALSE if an error occurs.
+ */
+ public function to_json($object, $fields = '', $pretty_print = FALSE)
+ {
+ if(empty($fields))
+ {
+ $fields = $object->fields;
+ }
+ $result = array();
+ foreach($fields as $f)
+ {
+ // handle related fields
+ if(array_key_exists($f, $object->has_one) || array_key_exists($f, $object->has_many))
+ {
+ // each related item is stored as an array of ids
+ // Note: this method will NOT get() the related object.
+ $rels = array();
+ foreach($object->{$f} as $item)
+ {
+ $rels[] = $item->id;
+ }
+ $result[$f] = $rels;
+ }
+ else
+ {
+ // just the field.
+ $result[$f] = $object->{$f};
+ }
+ }
+ $json = json_encode($result);
+ if($json === FALSE)
+ {
+ return FALSE;
+ }
+ if($pretty_print)
+ {
+ $json = $this->_json_format($json);
+ }
+ return $json;
+ }
+
+ /**
+ * Convert the entire $object->all array result set into JSON code.
+ *
+ * @param DataMapper $object The DataMapper Object to convert
+ * @param array $fields Array of fields to include. If empty, includes all database columns.
+ * @param boolean $pretty_print Format the JSON code for legibility.
+ * @return string A JSON formatted String, or FALSE if an error occurs.
+ */
+ public function all_to_json($object, $fields = '', $pretty_print = FALSE)
+ {
+ $result = array();
+ foreach($object as $o)
+ {
+ $result[] = $o->to_json($fields);
+ }
+ $json = json_encode($result);
+ if($json === FALSE)
+ {
+ return FALSE;
+ }
+ if($pretty_print)
+ {
+ $json = $this->_json_format($json);
+ }
+ return $json;
+ }
+
+ /**
+ * Convert a JSON object back into a DataMapper model.
+ *
+ * @param DataMapper $object The DataMapper Object to save to.
+ * @param string $json_code A string that contains JSON code.
+ * @param array $fields Array of 'safe' fields. If empty, only include the database columns.
+ * @return bool TRUE or FALSE on success or failure of converting the JSON string.
+ */
+ public function from_json($object, $json_code, $fields = '')
+ {
+ if(empty($fields))
+ {
+ $fields = $object->fields;
+ }
+ $data = json_decode($json_code);
+ if($data === FALSE)
+ {
+ return FALSE;
+ }
+ foreach($data as $k => $v) {
+ if(in_array($k, $fields))
+ {
+ $object->{$k} = $v;
+ }
+ }
+ return TRUE;
+ }
+
+ /**
+ * Sets the HTTP Content-Type header to application/json
+ *
+ * @param DataMapper $object
+ */
+ public function set_json_content_type($object)
+ {
+ $CI =& get_instance();
+ $CI->output->set_header('Content-Type: application/json');
+ }
+
+ /**
+ * Formats a JSON string for readability.
+ *
+ * From @link http://php.net/manual/en/function.json-encode.php
+ *
+ * @param string $json Unformatted JSON
+ * @return string Formatted JSON
+ */
+ private function _json_format($json)
+ {
+ $tab = " ";
+ $new_json = "";
+ $indent_level = 0;
+ $in_string = false;
+
+ $json_obj = json_decode($json);
+
+ if($json_obj === false)
+ return false;
+
+ $json = json_encode($json_obj);
+ $len = strlen($json);
+
+ for($c = 0; $c < $len; $c++)
+ {
+ $char = $json[$c];
+ switch($char)
+ {
+ case '{':
+ case '[':
+ if(!$in_string)
+ {
+ $new_json .= $char . "\n" . str_repeat($tab, $indent_level+1);
+ $indent_level++;
+ }
+ else
+ {
+ $new_json .= $char;
+ }
+ break;
+ case '}':
+ case ']':
+ if(!$in_string)
+ {
+ $indent_level--;
+ $new_json .= "\n" . str_repeat($tab, $indent_level) . $char;
+ }
+ else
+ {
+ $new_json .= $char;
+ }
+ break;
+ case ',':
+ if(!$in_string)
+ {
+ $new_json .= ",\n" . str_repeat($tab, $indent_level);
+ }
+ else
+ {
+ $new_json .= $char;
+ }
+ break;
+ case ':':
+ if(!$in_string)
+ {
+ $new_json .= ": ";
+ }
+ else
+ {
+ $new_json .= $char;
+ }
+ break;
+ case '"':
+ if($c > 0 && $json[$c-1] != '\\')
+ {
+ $in_string = !$in_string;
+ }
+ default:
+ $new_json .= $char;
+ break;
+ }
+ }
+
+ return $new_json;
+ }
+
+}
+
+/* End of file json.php */
+/* Location: ./application/datamapper/json.php */
1,397 application/extensions/nestedsets.php
View
@@ -0,0 +1,1397 @@
+<?php
+
+/**
+ * Nested Sets Extension for DataMapper classes.
+ *
+ * Nested Sets DataMapper model
+ *
+ * @license MIT License
+ * @package DMZ-Included-Extensions
+ * @category DMZ
+ * @author WanWizard
+ * @info Based on nstrees by Rolf Brugger, edutech
+ * http://www.edutech.ch/contribution/nstrees
+ * @version 1.0
+ */
+
+// --------------------------------------------------------------------------
+
+/**
+ * DMZ_Nestedsets Class
+ *
+ * @package DMZ-Included-Extensions
+ */
+class DMZ_Nestedsets {
+
+ /**
+ * name of the tree node left index field
+ *
+ * @var string
+ * @access private
+ */
+ private $_leftindex = 'left_id';
+
+ /**
+ * name of the tree node right index field
+ *
+ * @var string
+ * @access private
+ */
+ private $_rightindex = 'right_id';
+
+ /**
+ * name of the tree root id field. Used when the tree contains multiple roots
+ *
+ * @var string
+ * @access private
+ */
+ private $_rootfield = 'root_id';
+
+ /**
+ * value of the root field we need to filter on
+ *
+ * @var string
+ * @access private
+ */
+ private $_rootindex = NULL;
+
+ /**
+ * name of the tree node symlink index field
+ *
+ * @var string
+ * @access private
+ */
+ private $_symlinkindex = 'symlink_id';
+
+ /**
+ * name of the tree node name field, used to build a path string
+ *
+ * @var string
+ * @access private
+ */
+ private $_nodename = NULL;
+
+ /**
+ * indicates with pointers need to be used
+ *
+ * @var string
+ * @access private
+ */
+ private $use_symlink_pointers = TRUE;
+
+ // -----------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * @param mixed optional, array of load-time options or NULL
+ * @param object the DataMapper object
+ * @return void
+ * @access public
+ */
+ function __construct( $options = array(), $object = NULL )
+ {
+ // do we have the datamapper object
+ if ( ! is_null($object) )
+ {
+ // update the config
+ $this->tree_config($object, $options);
+ }
+ }
+
+ // -----------------------------------------------------------------
+
+ /**
+ * runtime configuration of this nestedsets tree
+ *
+ * @param object the DataMapper object
+ * @param mixed optional, array of options or NULL
+ * @return object the updated DataMapper object
+ * @access public
+ */
+ function tree_config($object, $options = array() )
+ {
+ // make sure the load-time options parameter is an array
+ if ( ! is_array($options) )
+ {
+ $options = array();
+ }
+
+ // make sure the model options parameter is an array
+ if ( ! isset($object->nestedsets) OR ! is_array($object->nestedsets) )
+ {
+ $object->nestedsets = array();
+ }
+
+ // loop through all options
+ foreach( array( $object->nestedsets, $options ) as $optarray )
+ {
+ foreach( $optarray as $key => $value )
+ {
+ switch ( $key )
+ {
+ case 'name':
+ $this->_nodename = (string) $value;
+ break;
+ case 'symlink':
+ $this->_symlinkindex = (string) $value;
+ break;
+ case 'left':
+ $this->_leftindex = (string) $value;
+ break;
+ case 'right':
+ $this->_rightindex = (string) $value;
+ break;
+ case 'root':
+ $this->_rootfield = (string) $value;
+ break;
+ case 'value':
+ $this->_rootindex = (int) $value;
+ break;
+ case 'follow':
+ $this->use_symlink_pointers = (bool) $value;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ // -----------------------------------------------------------------
+
+ /**
+ * select a specific root if the table contains multiple trees
+ *
+ * @param object the DataMapper object
+ * @return object the updated DataMapper object
+ * @access public
+ */
+ function select_root($object, $tree = NULL)
+ {
+ // set the filter value
+ $this->_rootindex = $tree;
+
+ // return the object
+ return $object;
+ }
+
+ // -----------------------------------------------------------------
+ // Tree constructors
+ // -----------------------------------------------------------------
+
+ /**
+ * create a new tree root
+ *
+ * @param object the DataMapper object
+ * @return object the updated DataMapper object
+ * @access public
+ */
+ function new_root($object)
+ {
+ // set the pointers for the root object
+ $object->id = NULL;
+ $object->{$this->_leftindex} = 1;
+ $object->{$this->_rightindex} = 2;
+
+ // add a root index if needed
+ if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) )
+ {
+ $object->{$this->_rootfield} = $this->_rootindex;
+ }
+
+ // create the new tree root, and return the updated object
+ return $this->_insertNew($object);
+ }
+
+ // -----------------------------------------------------------------
+
+ /**
+ * creates a new first child of 'node'
+ *
+ * @param object the DataMapper object
+ * @param object the parent node
+ * @return object the updated DataMapper object
+ * @access public
+ */
+ function new_first_child($object, $node = NULL)
+ {
+ // a node passed?
+ if ( is_null($node) )
+ {
+ // no, use the object itself
+ $node = $object->get_clone();
+ }
+
+ // we need a valid node for this to work
+ if ( ! $node->exists() )
+ {
+ return $node;
+ }
+
+ // set the pointers for the root object
+ $object->id = NULL;
+ $object->{$this->_leftindex} = $node->{$this->_leftindex} + 1;
+ $object->{$this->_rightindex} = $node->{$this->_leftindex} + 2;
+
+ // add a root index if needed
+ if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) )
+ {
+ $object->{$this->_rootfield} = $this->_rootindex;
+ }
+
+ // shift nodes to make room for the new child
+ $this->_shiftRLValues($node, $object->{$this->_leftindex}, 2);
+
+ // create the new tree node, and return the updated object
+ return $this->_insertNew($object);
+ }
+
+ // -----------------------------------------------------------------
+
+ /**
+ * creates a new last child of 'node'
+ *
+ * @param object the DataMapper object
+ * @param object the parent node
+ * @return object the updated DataMapper object
+ * @access public
+ */
+ function new_last_child($object, $node = NULL)
+ {
+ // a node passed?
+ if ( is_null($node) )
+ {
+ // no, use the object itself
+ $node = $object->get_clone();
+ }
+
+ // we need a valid node for this to work
+ if ( ! $node->exists() )
+ {
+ return $node;
+ }
+
+ // set the pointers for the root object
+ $object->id = NULL;
+ $object->{$this->_leftindex} = $node->{$this->_rightindex};
+ $object->{$this->_rightindex} = $node->{$this->_rightindex} + 1;
+
+ // add a root index if needed
+ if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) )
+ {
+ $object->{$this->_rootfield} = $this->_rootindex;
+ }
+
+ // shift nodes to make room for the new child
+ $this->_shiftRLValues($node, $object->{$this->_leftindex}, 2);
+
+ // create the new tree node, and return the updated object
+ return $this->_insertNew($object);
+ }
+
+ // -----------------------------------------------------------------
+
+ /**
+ * creates a new sibling before 'node'
+ *
+ * @param object the DataMapper object
+ * @param object the sibling node
+ * @return object the updated DataMapper object
+ * @access public
+ */
+ function new_previous_sibling($object, $node = NULL)
+ {
+ // a node passed?
+ if ( is_null($node) )
+ {
+ // no, use the object itself
+ $node = $object->get_clone();
+ }
+
+ // we need a valid node for this to work
+ if ( ! $node->exists() )
+ {
+ return $node;
+ }
+
+ // set the pointers for the root object
+ $object->id = NULL;
+ $object->{$this->_leftindex} = $node->{$this->_leftindex};
+ $object->{$this->_rightindex} = $node->{$this->_leftindex} + 1;
+
+ // add a root index if needed
+ if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) )
+ {
+ $object->{$this->_rootfield} = $this->_rootindex;
+ }
+
+ // shift nodes to make room for the new sibling
+ $this->_shiftRLValues($node, $object->{$this->_leftindex}, 2);
+
+ // create the new tree node, and return the updated object
+ return $this->_insertNew($object);
+ }
+
+ // -----------------------------------------------------------------
+
+ /**
+ * creates a new sibling after 'node'
+ *
+ * @param object the DataMapper object
+ * @param object the sibling node
+ * @return object the updated DataMapper object
+ * @access public
+ */
+ function new_next_sibling($object, $node = NULL)
+ {
+ // a node passed?
+ if ( is_null($node) )
+ {
+ // no, use the object itself
+ $node = $object->get_clone();
+ }
+
+ // we need a valid node for this to work
+ if ( ! $node->exists() )
+ {
+ return $node;
+ }
+
+ // set the pointers for the root object
+ $object->id = NULL;
+ $object->{$this->_leftindex} = $node->{$this->_rightindex} + 1;
+ $object->{$this->_rightindex} = $node->{$this->_rightindex} + 2;
+
+ // add a root index if needed
+ if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) )
+ {
+ $object->{$this->_rootfield} = $this->_rootindex;
+ }
+
+ // shift nodes to make room for the new sibling
+ $this->_shiftRLValues($node, $object->{$this->_leftindex}, 2);
+
+ // create the new tree node, and return the updated object
+ return $this->_insertNew($object);
+ }
+
+ // -----------------------------------------------------------------
+ // Tree queries
+ // -----------------------------------------------------------------
+
+
+ /**
+ * returns the root of the (selected) tree
+ *
+ * @param object the DataMapper object
+ * @return object the updated DataMapper object
+ * @access public
+ */
+ function get_root($object)
+ {
+ // add a root index if needed
+ if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) )
+ {
+ $object->db->where($this->_rootfield, $this->_rootindex);
+ }
+
+ // get the tree's root node
+ return $object->where($this->_leftindex, 1)->get();
+ }
+
+ // -----------------------------------------------------------------
+
+ /**
+ * returns the parent of the child 'node'
+ *
+ * @param object the DataMapper object
+ * @param object the child node
+ * @return object the updated DataMapper object
+ * @access public
+ */
+ function get_parent($object, $node = NULL)
+ {
+ // a node passed?
+ if ( is_null($node) )
+ {
+ // no, use the object itself
+ $node =& $object;
+ }
+
+ // we need a valid node for this to work
+ if ( ! $node->exists() )
+ {
+ return $node;
+ }
+
+ // add a root index if needed
+ if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) )
+ {
+ $object->db->where($this->_rootfield, $this->_rootindex);
+ }
+
+ // get the node's parent node
+ $object->where($this->_leftindex . ' <', $node->{$this->_leftindex});
+ $object->where($this->_rightindex . ' >', $node->{$this->_rightindex});
+ return $object->order_by($this->_rightindex, 'asc')->limit(1)->get();
+ }
+
+ // -----------------------------------------------------------------
+
+ /**
+ * returns the node with the requested left index pointer
+ *
+ * @param object the DataMapper object
+ * @param integer a node's left index value
+ * @return object the updated DataMapper object
+ * @access public
+ */
+ function get_node_where_left($object, $left_id)
+ {
+ // add a root index if needed
+ if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) )
+ {
+ $object->db->where($this->_rootfield, $this->_rootindex);
+ }
+
+ // get the node's parent node
+ $object->where($this->_leftindex, $left_id);
+ return $object->get();
+ }
+
+ // -----------------------------------------------------------------
+
+ /**
+ * returns the node with the requested right index pointer
+ *
+ * @param object the DataMapper object
+ * @param integer a node's right index value
+ * @return object the updated DataMapper object
+ * @access public
+ */
+ function get_node_where_right($object, $right_id)
+ {
+ // add a root index if needed
+ if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) )
+ {
+ $object->db->where($this->_rootfield, $this->_rootindex);
+ }
+
+ // get the node's parent node
+ $object->where($this->_rightindex, $right_id);
+ return $object->get();
+ }
+
+ // -----------------------------------------------------------------
+
+ /**
+ * returns the first child of the given node
+ *
+ * @param object the DataMapper object
+ * @param object the parent node
+ * @return object the updated DataMapper object
+ * @access public
+ */
+ function get_first_child($object, $node = NULL)
+ {
+ // a node passed?
+ if ( is_null($node) )
+ {
+ // no, use the object itself
+ $node =& $object;
+ }
+
+ // we need a valid node for this to work
+ if ( ! $node->exists() )
+ {
+ return $node;
+ }
+
+ // add a root index if needed
+ if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) )
+ {
+ $object->db->where($this->_rootfield, $this->_rootindex);
+ }
+
+ // get the node's first child node
+ $object->where($this->_leftindex, $node->{$this->_leftindex}+1);
+ return $object->get();
+ }
+
+ // -----------------------------------------------------------------
+
+ /**
+ * returns the last child of the given node
+ *
+ * @param object the DataMapper object
+ * @param object the parent node
+ * @return object the updated DataMapper object
+ * @access public
+ */
+ function get_last_child($object, $node = NULL)
+ {
+ // a node passed?
+ if ( is_null($node) )
+ {
+ // no, use the object itself
+ $node =& $object;
+ }
+
+ // we need a valid node for this to work
+ if ( ! $node->exists() )
+ {
+ return $node;
+ }
+
+ // add a root index if needed
+ if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) )
+ {
+ $object->db->where($this->_rootfield, $this->_rootindex);
+ }
+
+ // get the node's last child node
+ $object->where($this->_rightindex, $node->{$this->_rightindex}-1);
+ return $object->get();
+ }
+
+ // -----------------------------------------------------------------
+
+ /**
+ * returns the previous sibling of the given node
+ *
+ * @param object the DataMapper object
+ * @param object the sibling node
+ * @return object the updated DataMapper object
+ * @access public
+ */
+ function get_previous_sibling($object, $node = NULL)
+ {
+ // a node passed?
+ if ( is_null($node) )
+ {
+ // no, use the object itself
+ $node =& $object;
+ }
+
+ // we need a valid node for this to work
+ if ( ! $node->exists() )
+ {
+ return $node;
+ }
+
+ // add a root index if needed
+ if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) )
+ {
+ $object->db->where($this->_rootfield, $this->_rootindex);
+ }
+
+ // get the node's previous sibling node
+ $object->where($this->_rightindex, $node->{$this->_leftindex}-1);
+ return $object->get();
+ }
+
+ // -----------------------------------------------------------------
+
+ /**
+ * returns the next sibling of the given node
+ *
+ * @param object the DataMapper object
+ * @param object the sibling node
+ * @return object the updated DataMapper object
+ * @access public
+ */
+ function get_next_sibling($object, $node = NULL)
+ {
+ // a node passed?
+ if ( is_null($node) )
+ {
+ // no, use the object itself
+ $node =& $object;
+ }
+
+ // we need a valid node for this to work
+ if ( ! $node->exists() )
+ {
+ return $node;
+ }
+
+ // add a root index if needed
+ if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) )
+ {
+ $object->db->where($this->_rootfield, $this->_rootindex);
+ }
+
+ // get the node's next sibling node
+ $object->where($this->_leftindex, $node->{$this->_rightindex}+1);
+ return $object->get();
+ }
+
+ // -----------------------------------------------------------------
+ // Boolean tree functions
+ // -----------------------------------------------------------------
+
+ /**
+ * check if the object is a valid tree node
+ *
+ * @param object the DataMapper object
+ * @return boolean
+ * @access public
+ */
+ function is_valid_node($object)
+ {
+ if ( ! $object->exists() )
+ {
+ return FALSE;
+ }
+ elseif ( ! isset($object->{$this->_leftindex}) OR ! is_numeric($object->{$this->_leftindex}) OR $object->{$this->_leftindex} <=0 )
+ {
+ return FALSE;
+ }
+ elseif ( ! isset($object->{$this->_rightindex}) OR ! is_numeric($object->{$this->_rightindex}) OR $object->{$this->_rightindex} <=0 )
+ {
+ return FALSE;
+ }
+ elseif ( $object->{$this->_leftindex} >= $object->{$this->_rightindex} )
+ {
+ return FALSE;
+ }
+ elseif ( ! empty($this->