/**
  *@returns array where keys are ids, values are arrays with the following keys 'value', 'display'
  */
 public function getMapOptions($type = 'default', $show_hidden = 0, $flat = true, $add_limits = array())
 {
     $forms = $this->getSelectableForms();
     $union_fields = $this->getDisplayedFields($type);
     $limits = $this->getFormLimits($type);
     if (is_array($add_limits) && count($add_limits) > 0) {
         if (!is_array($limits) || count($limits) > 0) {
             $limits = $add_limits;
         } else {
             //need to go through each form and possibly merge limits
             foreach ($add_limits as $form => $formLimits) {
                 if (!array_key_exists($form, $limits) || !is_array($limits[$form]) || count($limits[$form]) == 0) {
                     $limits[$form] = $formLimits;
                 } else {
                     $limits[$form] = array('operator' => 'AND', 'operand' => array(0 => $limits[$form], 1 => $formLimits));
                 }
             }
         }
     }
     $orders = $this->getFormOrders($type);
     $data = array();
     foreach ($union_fields as $form => $fields) {
         $data = array_merge($data, I2CE_List::flattenDataTree(I2CE_List::buildDataTree($fields, array($form), $limits, $orders, $show_hidden)));
     }
     return $data;
 }
 protected function find_matching($form_obj)
 {
     $wheres = array();
     foreach ($form_obj as $field_name => $field_obj) {
         if (!$field_obj instanceof I2CE_FormField || !$field_obj->hasOption('unique') || !$field_obj->getOption('unique')) {
             continue;
         }
         if (!$field_obj->hasOption('unique_field')) {
             if ($field_obj->isValid()) {
                 $wheres[] = array('operator' => 'FIELD_LIMIT', 'style' => 'equals', 'field' => $field_obj->getName(), 'data' => array('value' => $field_obj->getDBValue()));
             }
         } else {
             $unique = $field_obj->getOption('unique_field');
             $unique_fields = explode(',', $unique);
             foreach ($unique_fields as $unique_field) {
                 //we need to say that this is unique only up to the other values
                 if (strpos($unique_field, ':') !== false) {
                     $main_where = array('operator' => 'FIELD_LIMIT', 'style' => 'equals', 'field' => $field_obj->getName(), 'data' => array('value' => $field_obj->getDBValue()));
                     $field_path = explode(':', $unique_field);
                     $restricted_field = false;
                     if (preg_match('/^\\[(.*?)\\](.*)$/', $field_path[0])) {
                         $restricted_field = $matches[1];
                         $field_path[0] = $matches[2];
                     } else {
                         if (preg_match('/^(.*?)\\+(.*)$/', $field_path[0], $matches)) {
                             $restricted_field = $matches[1];
                         } else {
                             $restricted_field = $field_path[0];
                         }
                     }
                     $restricted_field_obj = $form_obj->getField($restricted_field);
                     if (!$restricted_field_obj instanceof I2CE_FormField_MAP) {
                         $this->raiseError("Invalid field passed as restricted field for " . $form_obj->getName() . ": {$unique_field}");
                         return;
                     }
                     if ($restricted_field_obj->hasHeader('default')) {
                         $names[] = $restricted_field_obj->getHeader('default');
                     } else {
                         $names[] = $restricted_field_obj->getName();
                     }
                     //now let's split up field_path into the forms and the fields
                     $top_formid = I2CE_List::walkupFieldPath($field_path, $restricted_field_obj->getDBValue());
                     if ($top_formid === false) {
                         //the value is not set. or inappropriately set.  error silently.
                         //this is handled by hooked method defined in I2CE_Module_Form.
                         return;
                     }
                     //now we get all forms under $top_formid defined by the field path
                     $field_name = $field_obj->getName();
                     $field_val = $field_obj->getDBValue();
                     $form_id = $form_obj->getID();
                     $dtree_path = $field_path;
                     array_unshift($dtree_path, $form_obj->getName());
                     list($top_form, $top_id) = explode('|', $top_formid, 2);
                     $dtree_limits = array($top_form => array('operator' => 'FIELD_LIMIT', 'style' => 'equals', 'field' => 'id', 'data' => array('value' => $top_id)), $form_obj->getName() => $main_where);
                     $options = I2CE_List::buildDataTree($dtree_path, array($form_obj->getName()), $dtree_limits);
                     $options = I2CE_List::flattenDataTree($options);
                     if (count($options) == 1) {
                         // There is exactly one match.  we should use it
                         $matched_id = $options[0]['value'];
                         return $this->factory->createContainer($matched_id);
                     }
                 } else {
                     if (!($unique_field_obj = $form_obj->getField($unique_field)) instanceof I2CE_FormField) {
                         $this->raiseError("Invalid field {$unqiue_field}");
                         return;
                     }
                     if ($unique_field_obj->isValid()) {
                         $wheres[] = array('operator' => 'FIELD_LIMIT', 'style' => 'equals', 'field' => $unique_field_obj->getName(), 'data' => array('value' => $unique_field_obj->getDBValue()));
                     } else {
                         $wheres[] = array('operator' => 'OR', 'operand' => array(0 => array('operator' => 'FIELD_LIMIT', 'style' => 'equals', 'field' => $unique_field_obj->getName(), 'data' => array('value' => $unique_field_obj->getDBValue())), 1 => array('operator' => 'FIELD_LIMIT', 'style' => 'null', 'field' => $unique_field_obj->getName())));
                     }
                 }
             }
         }
     }
     if (count($wheres) == 0) {
         //no matching conditions. don't try to do anything
         return false;
     } else {
         if (count($wheres) > 1) {
             $where = array('operator' => 'AND', 'operand' => $wheres);
         } else {
             $where = current($wheres);
         }
     }
     if (!($id = I2CE_FormStorage::search($form_obj->getName(), false, $where, array(), true))) {
         return false;
     }
     $form_obj = $this->factory->createContainer(array($form_obj->getName(), $id));
     //$form_obj->populate();
     return $form_obj;
 }
 /**
  * Hooked Function to check if a field is unique
  * @param I2CE_FormField $field_obj
  */
 public function validate_formfield($field_obj)
 {
     if (!$field_obj->hasOption('unique') || !$field_obj->getOption('unique') || !$field_obj->isValid()) {
         return;
     }
     if (!$field_obj->hasOption('unique_field')) {
         return;
     }
     $unique = $field_obj->getOption('unique_field');
     if (strpos($unique, ':') === false) {
         //the value is not a mapped thing.  this is handled by hooked mehtod defined in I2CE_FormStorage
         return;
     }
     $form_obj = $field_obj->getContainer();
     if (!$form_obj instanceof I2CE_Form) {
         return;
     }
     $factory = I2CE_FormFactory::instance();
     //$unique should have the form 'unqique_field:form2(+field2):..:..:formM(+fieldM):...:formN
     //example $unique = 'region:country' or 'region+region+country:country' are the same.
     //    means that $field_obj needs to be unqiue when reseticted to the set of forms within a country and all of its regions
     //    the country and the region that is specified is the
     //    in this case, we need that region is a field of $form_obj
     //example: $unqiue = 'county:district+region:region:country' or 'county:district:region:country' are the same
     //    means that $field_obj needs to be unique when resitrcicted to a country, any of its regions any of those regions
     //    in this case, we need that county is a field of $form_obj
     //example: $unqiue = '[location]county:district+region:region:country' or 'county:district:region:country' are the same
     //    means that $field_obj needs to be unique when resitrcicted to a country, any of its regions any of those regions
     //    in this case, we need that location is a field of $form_obj
     $unique_fields = explode(',', $unique);
     $matches = null;
     $names = array();
     $main_where = array('operator' => 'FIELD_LIMIT', 'style' => 'equals', 'field' => $field_obj->getName(), 'data' => array('value' => $field_obj->getDBValue()));
     foreach ($unique_fields as $unique_field) {
         if ($matches === false) {
             break;
         }
         if (strpos($unique_field, ':') === false) {
             //this field is not mapped... just handle it as a regular value.
             if (!($unique_field_obj = $form_obj->getField($unique_field)) instanceof I2CE_FormField) {
                 I2CE::raiseError("Invalid field {$unqiue_field}");
                 return;
             }
             if ($unique_field_obj->hasHeader('default')) {
                 $names[] = $unique_field_obj->getHeader('default');
             } else {
                 $names[] = $unique_field_obj->getName();
             }
             $where = array($main_where);
             if ($unique_field_obj->getDBValue()->isValid()) {
                 $where[] = array('operator' => 'FIELD_LIMIT', 'style' => 'equals', 'field' => $unique_field_obj->getName(), 'data' => array('value' => $unique_field_obj->getDBValue()));
             } else {
                 $where[] = array('operator' => 'OR', 'operand' => array(0 => array('operator' => 'FIELD_LIMIT', 'style' => 'equals', 'field' => $unique_field_obj->getName(), 'data' => array('value' => $unique_field_obj->getDBValue())), 1 => array('operator' => 'FIELD_LIMIT', 'style' => 'null', 'field' => $unique_field_obj->getName())));
             }
             if (count($where) > 1) {
                 $where = array('operator' => 'AND', 'operand' => $where);
             }
             $found = I2CE_FormStorage::search($form_obj->getName(), false, $where);
             foreach ($found as &$f) {
                 $f = (string) $f;
             }
             $matches = count($found) > 0 && in_array((string) $form_obj->getId(), $found, true);
         } else {
             $field_path = explode(':', $unique_field);
             $restricted_field = false;
             if (preg_match('/^\\[(.*?)\\](.*)$/', $field_path[0])) {
                 $restricted_field = $matches[1];
                 $field_path[0] = $matches[2];
             } else {
                 if (preg_match('/^(.*?)\\+(.*)$/', $field_path[0], $matches)) {
                     $restricted_field = $matches[1];
                 } else {
                     $restricted_field = $field_path[0];
                 }
             }
             $restricted_field_obj = $form_obj->getField($restricted_field);
             if (!$restricted_field_obj instanceof I2CE_FormField_MAP) {
                 I2CE::raiseError("Invalid field passed as restricted field for " . $form_obj->getName() . ": {$unique_field}");
                 return;
             }
             if ($restricted_field_obj->hasHeader('default')) {
                 $names[] = $restricted_field_obj->getHeader('default');
             } else {
                 $names[] = $restricted_field_obj->getName();
             }
             //now let's split up field_path into the forms and the fields
             $top_formid = I2CE_List::walkupFieldPath($field_path, $restricted_field_obj->getDBValue());
             if ($top_formid === false) {
                 //the value is not set. or inappropriately set.  error silently.
                 //this is handled by hooked method defined in I2CE_Module_Form.
                 return;
             }
             //now we get all forms under $top_formid defined by the field path
             $field_name = $field_obj->getName();
             $field_val = $field_obj->getDBValue();
             $form_id = $form_obj->getID();
             $dtree_path = $field_path;
             array_unshift($dtree_path, $form_obj->getName());
             list($top_form, $top_id) = explode('|', $top_formid, 2);
             $dtree_limits = array($top_form => array('operator' => 'FIELD_LIMIT', 'style' => 'equals', 'field' => 'id', 'data' => array('value' => $top_id)), $form_obj->getName() => $main_where);
             $options = I2CE_List::buildDataTree($dtree_path, array($form_obj->getName()), $dtree_limits);
             $options = I2CE_List::flattenDataTree($options);
             if (count($options) == 1) {
                 // If there's only one match and it is this form then don't block
                 // changes.
                 if ($options[0]['value'] != $form_obj->getNameId()) {
                     $matches = true;
                 }
             } elseif (count($options) > 1) {
                 $matches = true;
             }
             /*
             array_pop($field_path);
             $options = I2CE_List::monsterMash(
                 $form_obj->getName(),  //facility
                 $restricted_field, //location
                 $top_formid, //country|10
                 $field_path, //array(county+district,district+region,region+country)            
                 $field_name, //name
                 false
                 );
             
             //starts get all facility where location = country|10  
             //    get all regions region|X where region+country = country|10
             //    this means we need to start with
             //           link_field = country (e.g link_field_path[$len-1] 
             //           list(sub_form,sub_link_field) = explode(+,end(subfields) ) == (region,country)
             //             
             //next get all facility where location = residence|X 
             $option_matches = false;
             foreach ($options as $id=>$data) {
                 if (array_key_exists($field_name,$data) && ($data[$field_name] == $field_val) && ( $id != $form_id)) {
                     $option_matches =true;
                     break;
                 }
             }
             $matches = ($option_matches);
             */
         }
     }
     if ($matches === true) {
         if (count($names) > 1) {
             $field_obj->setInvalidMessage('unique_fields', null, ' ' . implode(', ', $names));
         } else {
             if (count($names) == 1) {
                 $field_obj->setInvalidMessage('unique_field', null, ' ' . implode(', ', $names));
             } else {
                 $field_obj->setInvalidMessage('unique');
             }
         }
         return;
     }
 }