Xataface  2.0alpha2
Xataface Application Framework
 All Data Structures Namespaces Files Functions Variables Groups Pages
FormTool.php
Go to the documentation of this file.
1 <?php
10 
14  var $widgetHandlerPaths = array();
18  var $widgetHandlerClasses = array();
19 
23  var $widgetHandlers = array();
24 
29  public static function &getInstance(){
30  static $instance = 0;
31  if ( $instance === 0 ){
32  $instance = new Dataface_FormTool();
33  Dataface_Application::getInstance()->fireEvent('Dataface_FormTool::init', $instance);
34 
35  }
36  return $instance;
37  }
38 
39 
40 
41 
75  function pullField($record, &$field, $form, $formFieldName, $new=false){
76 
77  $element =& $this->getElement($form,$field,$formFieldName);
78  // Reference to the form element that will contain the field's value
79 
80  if ( PEAR::isError($element) ){
81 
82  return $element;
83  }
84 
85 
86  // Step 1: Load references to objects that we will need to use
87  $table =& $record->_table;
88 
89  if ( !$table->hasField($field['name']) ){
90  return PEAR::raiseError("Table ".$table->tablename." has no field $field[name] while trying to pull field value.", DATAFACE_E_NOTICE);
91  }
92  // Reference to the table
93 
94  // Reference to the field descriptor array that we are pulling
95  $widget =& $field['widget'];
96 
97  // See if there is a widgethandler registered for this widget type
98  $widgetHandler =& $this->getWidgetHandler($widget['type']);
99  if ( isset($widgetHandler) and method_exists($widgetHandler, 'pullField') ){
100  return $widgetHandler->pullField($record, $field, $form, $formFieldName, $new);
101  }
102 
103  // Reference to the widget descriptor
104  if ( !Dataface_PermissionsTool::view($record, array('field'=>$field['name'])) )
106  df_translate(
107  'scripts.Dataface.QuickForm.pullField.ERROR_NO_ACCESS_TO_FIELD',
108  "No read access on field '$field[name]'",
109  array('fieldname'=>$field['name'])
110  )
111 
112  );
113 
114 
115 
116  $raw = $record->getValue($field['name']);
117  // the raw value from the field
118  $delegate =& $table->getDelegate();
119  // Reference to the table's delegate object (may be null).
120  // Step 2: Insert the value into the form element
121 
122  $filterValue = true;
123  // In most cases we give the application an opportunity to
124  // filter the value using the 'FormTool::pullValue' event.
125  // This flag will store which case it is.
126 
127  if ( $delegate !== null and method_exists($delegate, $field['name']."__pullValue") ){
128  /*
129  *
130  * The delegate defines a conversion method that should be used.
131  *
132  */
133  $method = $field['name'].'__pullValue';
134  $val = $delegate->$method($record, $element);
135  $filterValue = false;
136 
137  } else if ( isset($widgetHandler) and method_exists($widgetHandler, 'pullValue') ){
138  $val = $widgetHandler->pullValue($record, $field, $form, $element, $new);
139 
140  } else {
141  $val = $record->getValueAsString($field['name']);
142 
143  }
144  if ( $filterValue ){
145  $evt = new stdClass;
146  $evt->record = $record;
147  $evt->field =& $field;
148  $evt->form = $form;
149  $evt->element = $element;
150  $evt->value = $val;
151  $evt->{'new'} = $new;
152  $table->app->fireEvent('FormTool::pullValue', $evt);
153  $val = $evt->value;
154  }
155 
156  $form->setDefaults( array( $formFieldName=>$val) );
157 
158 
159 
160  /*
161  *
162  * If we got this far, it must have been a success. Return true.
163  *
164  */
165  return true;
166 
167 
168  }
169 
170 
189  function pushField($record, &$field, $form, $formFieldName, $new=false){
190  if ( !is_array($field) ) throw new Exception("No field passed to pushField");
191  // See if there is a widgethandler registered for this widget type
192  $table =& $record->_table;
193 
194  $widget =& $field['widget'];
195 
196  $widgetHandler =& $this->getWidgetHandler($widget['type']);
197 
198 
199  if ( isset($widgetHandler) and method_exists($widgetHandler, 'pushField') ){
200 
201  return $widgetHandler->pushField($record, $field['name'], $form, $formFieldName, $new);
202  }
203 
204 
205  $metaValues = array(); // will store any meta values that are produced by pushValue
206  // a meta value is a field that exists only to support another field.
207  // Currently the only examples of this are filename and mimetype fields
208  // for File fields.
209 
210 
211  /*
212  *
213  * First we must obtain the value from the element on the form.
214  * $metaValues will hold an associative array of keys and values
215  * of Meta fields for this field. Meta fields are fields that describe
216  * this field. For example, if this field is a BLOB, then a meta field
217  * might contain this field's mimetype.
218  *
219  */
220  if ( is_a($formFieldName, 'HTML_QuickForm_element') ){
221  $element =& $formFieldName;
222  unset($formFieldName);
223  $formFieldName = $element->getName();
224  } else {
225  $element =& $this->getElement($form, $field, $formFieldName);
226  }
227 
228 
229  if ( PEAR::isError($element) || !is_a($element, 'HTML_QuickForm_element') || $element->isFrozen() || $element->getType() == 'static'){
230 
231  return;
232  }
233 
234  $value = $this->pushValue($record, $field, $form, $element, $metaValues);
235 
236 
237 
238 
239  $params = array();
240  if ( !$record->validate($field['name'], $value, $params) ){
241 
242  return Dataface_Error::permissionDenied($params['message']);
243  }
244 
245 
246  if ( PEAR::isError($value) ){
247  $value->addUserInfo(
248  df_translate(
249  'scripts.Dataface.QuickForm.pushField.ERROR_GETTING_VALUE',
250  "Error getting value for field '$field[name]' in QuickForm::pushField()",
251  array('file'=>"_", 'line'=>0,'fieldname'=>$field['name'])
252  )
253  );
254 
255  return $value;
256  }
257 
258 
259  if ( !$table->isMetaField($field['name']) ){
260 
261 
262 
263  /*
264  *
265  * A MetaField is a field that should not be updated on its own merit.
266  * An example of a MetaField is a mimetype field for a BLOB field. This
267  * field will be updated as a meta value for the BLOB field when the BLOB
268  * field is updated.
269  *
270  */
271  $res = $record->setValue($field['name'], $value);
272 
273  if (PEAR::isError($res) ){
274  $value->addUserInfo(
275  df_translate(
276  'scripts.Dataface.QuickForm.pushField.ERROR_SETTING_VALUE',
277  "Error setting value for field '$field[name]' in QuickForm::pushField()",
278  array('file'=>"_",'line'=>0,'fieldname'=>$field['name'])
279  )
280  );
281  throw new Exception($value->toString(), E_USER_ERROR);
282  return $res;
283  }
284  }
285 
286  /*
287  *
288  * If this field has any meta fields, then we will set them now.
289  *
290  */
291  foreach ($metaValues as $key=>$value){
292  $res = $record->setValue($key, $value);
293  if ( PEAR::isError($res) ){
294  $res->addUserInfo(
295  df_translate(
296  'scripts.Dataface.QuickForm.pushField.ERROR_SETTING_METAVALUE',
297  "Error setting value for meta field '$key' in QuickForm::pushField() ",
298  array('file'=>"_",'line'=>0,'field'=>$key)
299  )
300  );
301  throw new Exception($res->toString(), E_USER_ERROR);
302 
303  }
304  }
305 
306 
307 
308  }
309 
310 
335  function pushValue($record,&$field, $form, &$element, &$metaValues){
336  if ( is_string($field) ){
337  throw new Exception("Invalid field parameter: $field", E_USER_ERROR);
338 
339  }
340 
341  $widgetHandler =& $this->getWidgetHandler($field['widget']['type']);
342 
343  //$formFieldName = $element->getName();
344 
346  // Obtain references to frequently used objects
347  $table =& $record->_table;
348 
349  if ( PEAR::isError($field) ){
350  $field->addUserInfo(
351  df_translate(
352  'scripts.Dataface.QuickForm.pushValue.ERROR_PUSHING_VALUE',
353  "Error trying to push value onto field name in push()",
354  array('file'=>"_",'line'=>0)
355  )
356  );
357  return $field;
358  }
359 
360  $widget =& $field['widget'];
361 
362 
363 
364 
365  $delegate =& $table->getDelegate();
366 
367  $out = null;
368 
369  // chops off the relationship part of the field name if it is there.
370  if ( $delegate !== null and method_exists( $delegate, $field['name'].'__pushValue') ){
371  // A delegate is defined and contains a 'prepare' method for this field --- we use it
372  $val =& $element->getValue();
373  if ( PEAR::isError($val) ){
374  $val->addUserInfo(
375  df_translate(
376  'scripts.Dataface.QuickForm.pushValue.ERROR_GETTING_ELEMENT_VALUE',
377  "Error getting element value for element $field[name] in QuickForm::pushField ",
378  array('fieldname'=>$field['name'],'file'=>"_",'line'=>0)
379  )
380  );
381  return $val;
382  }
383 
384  // call the delegate's prepare function
385  $method = $field['name'].'__pushValue';
386  $val = $delegate->$method($record, $element);
387  if ( PEAR::isError($val) ){
388  $val->addUserInfo(
389  df_translate(
390  'scripts.Dataface.QuickForm.pushValue.ERROR_THROWN_BY_DELEGATE',
391  "Error thrown by delegate when preparing value for field '$field[name]'",
392  array('file'=>"_",'line'=>0,'fieldname'=>$field['name'])
393  )
394  );
395  return $val;
396  }
397 
398  // If the delegate class defines a custom push value then
399  // we return it directly... we don't push it through any further filters.
400  return $val;
401  } else if ( isset($widgetHandler) and method_exists($widgetHandler, 'pushValue') ){
402  $out = $widgetHandler->pushValue($record, $field, $form, $element, $metaValues);
403  } else {
404  // There is no delegate defined... we just do standard preparations based on field and widget types.
405  $out = $element->getValue();
406 
407 
408  }
409  $evt = new stdClass;
410  $evt->record = $record;
411  $evt->field =& $field;
412  $evt->form = $form;
413  $evt->element = $element;
414  $evt->metaValues =& $metaValues;
415  $evt->value = $out;
416  $app->fireEvent('FormTool::pushValue', $evt);
417  $out = $evt->value;
418 
419  return $out;
420 
421 
422 
423  }
424 
437  function &buildWidget($record, &$field, $form, $formFieldName, $new=false, $permissions=null){
438  $table =& $record->_table;
439 
440 
441  $widget =& $field['widget'];
442  if ( !isset($permissions) ){
443  //$permissions =& $record->getPermissions(array('field'=>$field['name']));
444  $permissions = Dataface_PermissionsTool::ALL();
445  // reference to widget descriptor array
446  }
447 
449  // Reference to permissions tool to operate on $permissions
450 
451 
452  $widgetHandler =& $this->getWidgetHandler($widget['type']);
453  if ( isset($widgetHandler) and method_exists($widgetHandler, 'buildWidget') ){
454  $el =& $widgetHandler->buildWidget($record, $field, $form, $formFieldName, $new);
455 
456  } else {
457  $factory =& Dataface_FormTool::factory();
458  // A dummy HTML_QuickForm used as a factory to create temporary elements.
459  // Reference to the table object.
460  $el =& $factory->addElement($widget['type'], $formFieldName, $widget['label'], array('class'=>$widget['class'], 'id'=>$formFieldName) );
461  }
462 
463 
464  if ( PEAR::isError($el) ){
465  throw new Exception($el->toString(), E_USER_ERROR);
466  }
467  $el->setFieldDef($field);
468  if ( isset( $record ) && $record && $record->_table->hasField($field['name']) ){
469  if ( $link = $record->getLink($field['name']) ){
470  $el->setProperty('link',$link);
471  }
472  $el->setProperty('record_url', $record->getURL());
473  }
474  $atts = $el->getAttributes();
475  if ( !is_array($atts) ) $atts = array();
476  $atts = array_merge($atts, $field['widget']['atts']);
477 
478  if ( !isset($atts['data-xf-field']) ){
479  $atts['data-xf-field'] = $field['name'];
480  }
481  $el->setAttributes($atts);
482 
483  if ( $new and !$pt->checkPermission('new', $permissions) ){
484  $el->freeze();
485  } else if ( !$new and !$pt->checkPermission('edit', $permissions) ){
486  $el->freeze();
487  }
488 
489  /*
490  // Deal with permissions on this field.
491  if ( $pt->view($permissions) and !$pt->edit($permissions) ){
492  if ( !($new && $pt->checkPermission('new', $permissions)) ){
493  $el->freeze();
494  }
495  }
496  */
497  $el->record =& $record;
498 
499  $form->addElement($el);
500  /*
501  *
502  * If there are any validation options set for the field, we must add these rules to the quickform
503  * element.
504  *
505  */
506  $validators = $field['validators'];
507 
508  foreach ($validators as $vname=>$validator){
509  /*
510  *
511  * $validator['arg'] would be specified in the INI file.
512  * Example ini file listing:
513  * -------------------------
514  * [FirstName]
515  * widget:label = First name
516  * widget:description = Enter your first name
517  * validators:regex = "/[0-9a-zA-Z/"
518  *
519  * This would result in $validator['arg'] = "/[0-9a-zA-Z/" in this section
520  * and $vname == "regex". Hence it would mean that a regular expression validator
521  * is being placed on this field so that only Alphanumeric characters are accepted.
522  * Please see documentation for HTML_QuickForm PEAR class for more information
523  * about QuickForm validators.
524  *
525  */
526  if ( $vname == 'required' && $widget['type'] == 'file' ){
527  continue;
528  }
529 
530  $form->addRule($formFieldName, $validator['message'], $vname, @$validator['arg'], (($widget['type'] == 'htmlarea' )?null:'client'));
531 
532  }
533 
534 
535 
536  $this->pullField($record, $field, $form, $formFieldName, $new);
537 
538 
539  $el->_persistantFreeze = true;
540  return $el;
541  }
542 
565  function registerWidgetHandler($widgetType, $path, $class){
566  $this->widgetHandlerClasses[$widgetType] = $class;
567  $this->widgetHandlerPaths[$widgetType] = $path;
568  }
569 
570 
575  function unregisterWidgetHandler($widgetType){
576  unset($this->widgetHandlerClasses[$widgetType] );
577  unset($this->widgetHandlerPaths[$widgetType]);
578  unset($this->widgetHandlers[$widgetType]);
579  }
580 
589  function &getWidgetHandler($widgetType){
590 
591  if ( !isset($this->widgetHandlers[$widgetType]) ){
592 
593  if ( !isset($this->widgetHandlerPaths[$widgetType]) and !isset($this->widgetHandlerClasses[$widgetType]) ){
594 
595  $class = 'Dataface_FormTool_'.$widgetType;
596  if ( class_exists('Dataface_FormTool_'.$widgetType) ){
597 
598  $this->widgetHandlers[$widgetType] = new $class;
599  } else if ( $this->_file_exists_incpath('Dataface/FormTool/'.$widgetType.'.php') ){
600 
601  import('Dataface/FormTool/'.$widgetType.'.php');
602  $this->widgetHandlers[$widgetType] = new $class;
603  } else {
604 
605  //$err = PEAR::raiseError("Attempt to get widget handler for '$widgetType' which is not registered.");
606  $out = null;
607 
608  return $out;
609  }
610  } else {
611 
612  if ( !class_exists($this->widgetHandlerClasses[$widgetType]) ){
613  import($this->widgetHandlerPaths[$widgetType]);
614  }
615  $class = $this->widgetHandlerClasses[$widgetType];
616  $this->widgetHandlers[$widgetType] = new $class;
617  }
618  }
619  return $this->widgetHandlers[$widgetType];
620  }
621 
622 
626  public static function &factory(){
627  static $factory = -1;
628  if ( is_scalar($factory) ){
629  $factory = new HTML_QuickFormFactory('factory');
630  }
631  return $factory;
632  }
633 
643  public static function &getVocabulary($record, &$field){
644  $res = Dataface_FormTool::_getVocabAndClasses($record, $field);
645  return $res['options'];
646  }
647 
658  public static function getVocabularyClasses($record, &$field){
659  $res = Dataface_FormTool::_getVocabAndClasses($record, $field);
660  return $res['options__classes'];
661  }
662 
675  static function _getVocabAndClasses($record, &$field){
676  if ( !$record ) {
677  throw new Exception("No record foudn in getVocabulary()", E_USER_ERROR);
678 
679  }
680  $table =& $record->_table;
681  $options = null;
682 
683  if ( isset($field['vocabulary']) and $field['vocabulary'] ){
684  /*
685  *
686  * This field has a vocabulary set on it. Let's load it and get it ready to be used
687  * as an options array for a quickform select, checkbox, or radio group element.
688  *
689  */
690  $options =& $table->getValuelist($field['vocabulary']);
691  $options__classes =& $table->getValuelist($field['vocabulary'].'__meta');
692 
693  if ( PEAR::isError($options) ){
694  $options->addUserInfo("Error getting vocabulary '$field[vocabulary]' when building widget for field '$field[name]' in QuickForm::buildWidget() ");
695  throw new Exception($options->toString(), E_USER_ERROR);
696  }
697 
698  /*
699  *
700  * We should have the option to choose none of the options, so we will add a blank option
701  * to the beginning of the options list.
702  *
703  */
704  if ( is_array($options) ){
705  $opts = array(''=>df_translate('scripts.GLOBAL.FORMS.OPTION_PLEASE_SELECT',"Please Select..."));
706  foreach ($options as $key=>$value){
707  $opts[$key] = $value;
708  }
709  $options = $opts;
710  }
711 
712  }
713  return array('options'=>&$options, 'options__classes'=>&$options__classes);
714  }
715 
726  function _file_exists_incpath ($file)
727  {
728  $paths = explode(PATH_SEPARATOR, get_include_path());
729 
730  foreach ($paths as $path) {
731  // Formulate the absolute path
732  $fullpath = $path . DIRECTORY_SEPARATOR . $file;
733 
734  // Check it
735  if (@file_exists($fullpath)) {
736  return $fullpath;
737  }
738  }
739 
740  return false;
741  }
742 
743 
754  function _formGroupExists(&$form, $group){
755  $el =& $form->getElement($group);
756  return ( $el and !PEAR::isError($el) and ($el->getType() == 'group') );
757  }
758 
771  function &getElement(&$form, &$field, $name){
772  if ( !is_array($field) ){
773  throw new Exception("Expected field definition for 2nd parameter but received something else.");
774  }
775  $fieldname = $field['name'];
776 
777  if ( isset($field['group']) and $this->_formGroupExists($form,$field['group'])){
778  /*
779  *
780  * This field is part of a larger group of fields. The widget that is used for this
781  * larger group is named after the field's group (rather than the field itself).
782  *
783  */
784  $el =& $form->getElement($field['group']);
785  if ( PEAR::isError($el) ){
786  $el->addUserInfo(
787  df_translate(
788  'scripts.Dataface.QuickForm.getElementByFieldName.ERROR_GETTING_GROUP',
789  "Failed to get group '$field[group]' when trying to load field '$fieldname' in Dataface_Quickform::pushWidget() ",
790  array('groupname'=>$field['group'], 'fieldname'=>$fieldname,'line'=>0,'file'=>"_")
791  )
792  );
793  throw new Exception($el->toString(), E_USER_ERROR);
794 
795  return $el;
796  }
797 
798  /*
799  *
800  * Find the field within this group that has the same name as the field we are looking for.
801  *
802  */
803  $els =& $el->getElements();
804  unset($el);
805  // prevent accidental change of the group element
806  foreach ( array_keys($els) as $key) {
807  $el =& $els[$key];
808  if ( $el->getName() == $name ){
809  /*
810  *
811  * We have found the element. Break out of this loop.
812  *
813  */
814  $element =& $el;
815  break;
816  }
817  unset($el);
818  }
819  unset($els);
820  if ( !isset($element) ){
821  return PEAR::raiseError(QUICKFORM_NO_SUCH_FIELD_ERROR,null,null,null,
822  df_translate(
823  'scripts.Dataface.QuickForm.getElementByFieldName.ERROR_GETTING_GROUP_FIELD',
824  "Error trying to load field '$fieldname' in group '$field[group][name]'. The group was found but not the field. in Dataface_Quickform::pushWidget()",
825  array('fieldname'=>$fieldname,'groupname'=>$field['group']['name'], 'line'=>0,'file'=>"_")
826  )
827  );
828  }
829  } else {
830  /*
831  *
832  * This field is not part of a larger group. The name of the element for this field is the
833  * same as the name of the field itself.
834  *
835  */
836  $element =& $form->getElement($name);
837  }
838 
839 
840  return $element;
841 
842  }
843 
852  function groupFields(&$fields){
854  $query =& $app->getQuery();
855 
856  // Take query parameters to set the collapsed config settings
857  // for the groups.
858  if ( isset($query['--collapsed']) ){
859  $collapsed = explode(',',$query['--collapsed']);
860  } else {
861  $collapsed = array();
862  }
863 
864  if ( isset($query['--expanded']) ){
865  $expanded = explode(',', $query['--expanded']);
866  } else {
867  $expanded = array();
868  }
869  $groups = array();
870  $groupOrders = array();
871  foreach ( array_keys($fields) as $fieldname){
872  $groupname = ( @$fields[$fieldname]['group'] ? $fields[$fieldname]['group'] : '__global__');
873  $groups[$groupname][] =& $fields[$fieldname];
874  if ( !isset($groupOrders[$groupname]) ){
875  $table =& Dataface_Table::loadTable($fields[$fieldname]['tablename']);
876  $grp =& $table->getFieldgroup($groupname);
877  if ( in_array($groupname, $collapsed) ){
878  $grp['collapsed'] = 1;
879  }
880  if ( in_array($groupname, $expanded) ){
881  $grp['collapsed'] = 0;
882  }
883  if ( $grp ){
884  $groupOrders[$groupname] = array( 'order'=>( (is_array($grp) and isset($grp['order'])) ? floatval($grp['order']) : 0));
885  } else {
886  $groupOrders[$groupname] = array('order'=>0);
887  }
888  unset($table);
889  unset($grp);
890  }
891  }
892  $groupOrders['__global__']['order'] = -1;
893  uasort($groupOrders, array(&$this, '_compareGroups'));
894  foreach ( array_keys($groupOrders) as $group){
895  $groupOrders[$group] =& $groups[$group];
896  }
897  return $groupOrders;
898  }
899 
903  function _compareGroups($g1, $g2){
904  $o1 = ( isset($g1['order']) ? intval($g1['order']) : 0 );
905  $o2 = ( isset($g2['order']) ? intval($g2['order']) : 0 );
906  if ( $o1 <= $o2 ) return -1;
907  return 1;
908  }
909 
923  function display(&$form, $template=null, $singleField=false, $useTabs=false){
924 
925 
926  import('HTML/QuickForm/Renderer/ArrayDataface.php');
927  //$skinTool =& Dataface_SkinTool::getInstance();
928  $renderer = new HTML_QuickForm_Renderer_ArrayDataface(true);
929  $form->accept($renderer);
930  $form_data = $renderer->toArray();
931  if ( !@$form_data['sections'] ){
932  $form_data['sections'] = array('__global__'=>array('header'=>df_translate('scripts.Dataface_FormTool.LABEL_EDIT_DETAILS', 'Edit Details'), 'name'=>'Edit','elements'=>&$form_data['elements']));
933  unset($form_data['elements']);
934  }
935  if ( $useTabs ){
936  $form_data['tabs'] = array();
937  if ( @is_array($form_data['elements']) ){
938  foreach ($form_data['elements'] as $key=>$element){
939  if ( !@$element['field'] ) continue;
940  $tabname = $element['field']['tab'];
941  if ( !$tabname ) $tabname = '__main__';
942  if ( !isset($form_data['tabs'][$tabname] ) ){
943  $table =& Dataface_Table::loadTable($element['field']['table']);
944  if ( PEAR::isError($table) ) throw new Exception($table->getMessage(), E_USER_ERROR);
945 
946  $form_data['tabs'][$tabname] = $table->getTab($element['field']['tab']);
947  $form_data['tabs'][$tabname]['elements'] = array();
948  $form_data['tabs'][$tabname]['sections'] = array();
949  unset($table);
950  }
951  $form_data['tabs'][$tabname]['elements'][$key] =& $form_data['elements'][$key];
952 
953 
954  }
955  }
956  if ( is_array($form_data['sections']) ){
957  foreach ($form_data['sections'] as $skey=>$section ){
958  if ( !@$section['elements'] ) continue;
959  foreach ($section['elements'] as $ekey=>$element ){
960  if ( !@$element['field'] ) continue;
961  $tabname = $element['field']['tab'];
962  if ( !$tabname ) $tabname = '__main__';
963  if ( !isset($form_data['tabs'][$tabname] ) ){
964  $table =& Dataface_Table::loadTable($element['field']['tablename']);
965  if ( PEAR::isError($table) ) throw new Exception($table->getMessage(), E_USER_ERROR);
966 
967  $form_data['tabs'][$tabname] = $table->getTab($tabname);
968  $form_data['tabs'][$tabname]['elements'] = array();
969  $form_data['tabs'][$tabname]['sections'] = array();
970  unset($table);
971  }
972 
973  if ( !isset($form_data['tabs'][$tabname]['sections'][$skey]) ){
974  $section_copy = $section;
975  $section_copy['elements'] = array();
976  $form_data['tabs'][$tabname]['sections'][$skey] =& $section_copy;
977  unset($section_copy);
978  }
979 
980  $form_data['tabs'][$tabname]['sections'][$skey]['elements'][$ekey] =& $form_data['sections'][$skey]['elements'][$ekey];
981 
982  }
983  }
984  }
985 
986  uasort($form_data['tabs'], array(&$this, '_sortTabs'));
987  }
988  $context = array('form_data'=>$form_data);
989 
990  if ( isset($form->_record) and method_exists($form->_record, 'getId') ){
991  $context['form_record_id'] = $form->_record->getId();
992  }
993  if ( $singleField ){
994  $context['singleField'] =& $form->getElement($singleField);
995  $context['singleField']->updateAttributes(array('onblur'=>'submitThisForm(this.form);'));
996  if (!isset($template) ) $template = 'Dataface_Form_single_field_template.html';
997  }
998  if ( !isset($template) ) $template = 'Dataface_Form_Template.html';
999  df_display($context, $template);
1000  }
1001 
1005  function _sortTabs($a,$b){
1006  if ( @$a['order'] == @$b['order'] ) return 0;
1007  return (@$a['order'] < @$b['order']) ? -1 : 1;
1008  }
1009 
1010 
1020  function createRecordForm($record, $new=false, $tab=null, $query=null){
1021 
1022  $table =& $record->_table;
1023 
1024  if ( $table->hasJoinTable($tab, $record) ){
1025  $query['--tab'] = null;
1026  $jrecord = $record->getJoinRecord($tab, true /*null if not found*/);
1027  if ( !isset($jrecord) ) $new = true;
1028  else $new = false;
1029  $form = $this->createRecordForm($record->getJoinRecord($tab), $new, null, $query);
1030  $form->overrideNoQuery = true;
1031  return $form;
1032 
1033  } else {
1034  // TO DO: Finish this method
1035  $form = new Dataface_QuickForm($table->tablename, df_db(), $query, '', $new);
1036  $form->_record =& $record;
1037  $form->tab = $tab;
1038  return $form;
1039  }
1040 
1041  }
1056  function decorateRecordForm($record, &$form, $new=false, $tab=null){
1057  @$form->removeElement('__keys__');
1058  if ( $new ){
1059 
1060 
1061  } else {
1062  $factory =& self::factory();
1063  $els = array();
1064  foreach ( array_keys($record->_table->keys()) as $key ){
1065  $els[] = $factory->addElement('hidden',$key);
1066  }
1067  $form->addGroup($els, '__keys__');
1068  $form->setConstants(array('__keys__'=>$record->strvals(array_keys($record->_table->keys()))));
1069  }
1070  $form->addElement('hidden', '--form-session-key', $this->getSessionKey());
1071  @$form->removeElement('--session:save');
1072  $form->addElement('header','__submit__','Submit');
1073  $grp =& $form->addGroup($this->createRecordButtons($record, $tab));
1074  $grp->_separator = "\n";
1075 
1076  $data = $this->getSessionData($tab);
1077  if ( isset($data) ){
1078  //$form->setDefaults($data);
1079  if ( $_POST ){
1080  $form->_setSubmitValues($data['_submitValues'], $data['_submitFiles']);
1081  } else {
1082 
1083  // TODO: File uploads will not be handled properly with this method.
1084  // Need to make it so that we can support file uploads before actually
1085  // saving the record.
1086  $form->setDefaults($data['_submitValues']);
1087  }
1088  }
1089 
1090  if ( $record->_table->hasJoinTable($tab, $record ) ){
1091  foreach ( $record->getJoinKeys($tab) as $key=>$value ){
1092  @$form->removeElement($key);
1093  //if ( $new ) $value = '-1';
1094  //$form->addElement('hidden',$key, $value);
1095  }
1096  }
1097 
1098 
1099 
1100  }
1101 
1102 
1111  function validateRecordForm($record, &$form, $new=false, $tab=null){
1112 
1113  if ( !$form->validate() ) return false;
1115  $query =& $app->getQuery();
1116 
1117  $targets = preg_grep('/^--session:target:/', array_keys($query));
1118 
1119  if ( count($targets) > 0 ) return true;
1120 
1121  $tabs = $record->tabs();
1122 
1123  if ( count($tabs) <= 1 ) return true;
1124  // There is only one tab so we don't have to do anything fancy.
1125  //$post = $_POST;
1126  //$_POST=array();
1127  //echo "About to validate each tab";
1128  $session_data =& $this->getSessionData();
1129  foreach ( array_keys($tabs) as $tabname ){
1130  if ($tabname == $tab) continue;
1131  if ( !$session_data or !$session_data['tabs'] or !in_array($tabname, array_keys($session_data['tabs'])) ) continue;
1132  $currForm =& $this->createRecordForm($record, $new, $tabname);
1133  $currForm->_build();
1134 
1135  //$currForm->setConstants($currForm->_defaultValues);
1136  //$_POST = $currForm->exportValues();
1137  $this->decorateRecordForm($record, $currForm, $new, $tabname);
1138  //$currForm->_submitValues = $currForm->_defaultValues;
1139  $currForm->_flagSubmitted = true;
1140  if ( !$currForm->validate() ){
1141 
1142  $form->setElementError('global.'.$tabname, df_translate('classes.FormTool.errors.ERROR_IN_TAB', 'A validation error occurred in the '.$tabs[$tabname]['label'].' tab. Please verify that this tab\'s input is correct before saving.', array('tab'=>$tabs[$tabname]['label'])));
1143 
1144  }
1145  unset($currForm);
1146  }
1147 
1148 
1149 
1150  return (count($form->_errors) == 0 );
1151 
1152 
1153  }
1154 
1168  function handleTabSubmit($record, $form, $tab=null){
1169  $tabs = $record->tabs();
1170  $tabnames = array_keys($tabs);
1171  if ( count($tabs) > 1 and isset($tab) ){
1172  // We are working with tabs, so before we save, we should store the data in
1173  // a session variable.
1174  $this->storeSessionData(array('_submitValues'=>$form->_submitValues,'_submitFiles'=>$form->_submitFiles), $tab, null, $record->getId());
1175 
1176  }
1178  $query =& $app->getQuery();
1179 
1180  $targets = preg_grep('/^--session:target:/', array_keys($query));
1181 
1182  if ( isset($tab) and count($targets) >0 ){
1183  // We aren't saving this session, so we'll just forward to
1184  // the next tab.
1185  $target = reset($targets);
1186 
1187  $res = preg_match('/--session:target:(.*)$/', $target, $matches);
1188  if ( !$res ) throw new Exception("Programming error: no matching target in query.", E_USER_ERROR);
1189 
1190  $target = $matches[1];
1191 
1192  if ( $target == '__default__' ) $target = $query['--session:target:__default__'];
1193  if ( $target == '__save__' ) return;
1194 
1195  $currentTabKey = intval(array_search($tab, $tabnames));
1196 
1197  if ( $currentTabKey === false ){
1198  // Current tab was not in the list of tabs.. this si
1199  // a problem
1200  return PEAR::raiseError("Sorry there was a problem finding the specified tab: ".$query['--tab']." in the tabs for the record ".$currentRecord->getId().". The available tabs are '".implode(', ', $tabnames).".");
1201 
1202  }
1203  if ( $target == '__next__' ){
1204  // The user clicked the 'next' button so they should
1205  // be directed to the next tab
1206 
1207  if ( isset($tabnames[$currentTabKey+1]) ){
1208  $target = $tabnames[$currentTabKey+1];
1209  } else {
1210  $target = $tab;
1211  }
1212  } else if ( $target == '__back__' ){
1213  // The user clicked the 'back' button so they should
1214  // be directed to the previous tab
1215 
1216  if ( isset($tabnames[$currentTabKey-1]) ){
1217  $target = $tabnames[$currentTabKey-1];
1218  } else {
1219  $target = $tab;
1220  }
1221  }
1222 
1223 
1224  // Now we just redirect to the next tab
1225  if ( isset( $query['-query'] ) ){
1226  $q = $query['-query'];
1227  } else if ( isset($_SERVER['HTTP_REFERER']) and strpos($_SERVER['HTTP_REFERER'], '?') !== false ){
1228  $q = substr($_SERVER['HTTP_REFERER'], strpos($_SERVER['HTTP_REFERER'], '?')+1);
1229 
1230  } else {
1231  $couples = array();
1232  foreach ( $record->keys() as $key=>$value ){
1233  $couples[] = urlencode($key).'='.urlencode($value);
1234  }
1235  $q = '-table='.urlencode($query['-table']).'&-action='.$query['-action'].'&'.implode('&', $couples);
1236  }
1237 
1238  if ( strpos($q, '&--form-session-key=') === false ) $q .= '&--form-session-key='.$this->getSessionKey();
1239  if ( strpos($q,'&--tab=') === false ) $q .= '&--tab='.urlencode($target);
1240  else $q = preg_replace('/([&?])--tab=[^&]*(&?)/', '${1}--tab='.urlencode($target).'$2', $q);
1241  $q = preg_replace('/[&?]--msg=[^&]*/', '',$q);
1242 
1243  $url = DATAFACE_SITE_HREF.'?'.$q;
1244  $app->redirect($url);
1245 
1246 
1247  }
1248 
1249 
1250 
1251  }
1252 
1271  function createHTMLTabs($record, $form, $selectedTab){
1272  $out = array();
1273  $formname = $form->getAttribute('name');
1274  $tabs = $record->_table->tabs($record);
1275  if ( !$tabs or count($tabs)<2 ) return null;
1276  foreach ( $tabs as $tab ){
1277  $tab['url'] = 'javascript: document.forms[\''.$formname.'\'].elements[\'--session:target:'.$tab['name'].'\'].click()';
1278 
1279  $tab['css_class'] = 'edit-form-tab'. ( ( $tab['name'] == $selectedTab ) ? ' selected tabs-selected':'');
1280  $out[] = $tab;
1281  }
1282  return $out;
1283  }
1284 
1293  function getSessionKey(){
1294  $app =& Dataface_Application::getInstance();
1295  $query =& $app->getQuery();
1296  if ( isset($query['--form-session-key']) ){
1297  return $query['--form-session-key'];
1298  } else {
1299  $key = rand().'_'.time();
1300  $query['--form-session-key'] = $key;
1301  return $key;
1302  }
1303  }
1304 
1329  function getSessionData($tab=null, $session_key = null){
1330 
1331  if ( !isset($session_key) ) $session_key = $this->getSessionKey();
1332 
1333 
1334  if ( isset($tab) and isset($_SESSION[$session_key]['tabs'][$tab]) ){
1335 
1336  return $_SESSION[$session_key]['tabs'][$tab];
1337 
1338  } else if ( !isset($tab) and isset($_SESSION[$session_key]) ) {
1339  return $_SESSION[$session_key];
1340 
1341  } else {
1342  return null;
1343  }
1344  }
1345 
1353  function clearSessionData($session_key=null){
1354  if ( !isset($session_key) ) $session_key = $this->getSessionKey();
1355 
1356  unset($_SESSION[$session_key]);
1357  }
1358 
1367  function storeSessionData($data, $tab, $session_key = null, $record_id=null){
1368  if ( !isset($session_key) ) $session_key = $this->getSessionKey();
1369  if ( !isset($record_id) ){
1370  $app =& Dataface_Application::getInstance();
1371  $record =& $app->getRecord();
1372  if ( $record ){
1373  $record_id = $record->getId();
1374  }
1375  }
1376  if ( !isset($_SESSION[$session_key]) ) $_SESSION[$session_key] = array('tabs'=>array(),'table'=>$data['_submitValues']['-table'], 'id'=>$record_id);
1377 
1378  $_SESSION[$session_key]['tabs'][$tab] = $data;
1379  return true;
1380 
1381  }
1382 
1394  function createRecordButtons($record, $currentTab=null){
1395 
1396  $factory =& self::factory();
1397  $out = array();
1398  $tabs = $record->tabs();
1399  $tabnames = array_keys($tabs);
1400  if ( count($tabnames) > 0 and !isset($currentTab) ) $currentTab = $tabnames[0];
1401  $saveButtonLabel = df_translate('tables.'.$record->table()->tablename.'.save_button_label', '');
1402  if ( !$saveButtonLabel ) $saveButtonLabel = df_translate('save_button_label','Save');
1403  $out[] = $factory->createElement('submit', '--session:save', $saveButtonLabel);
1404 
1405 
1406  if ( isset($currentTab) and count($tabnames)>1 ){
1407  if ( isset($tabs[$currentTab]['actions']['default']) ){
1408  $default = $tabs[$currentTab]['actions']['default'];
1409  } else {
1410  $default = '__save__';
1411  }
1412  $out[] = $factory->createElement('submit', '--session:target:__default__', $default, array('style'=>'display:none'));
1413 
1414  $currIndex = array_search($currentTab, $tabnames);
1415  $next = ( isset( $tabnames[$currIndex+1] ) ? $tabnames[$currIndex+1] : null);
1416  $prev = ( isset( $tabnames[$currIndex-1] ) ? $tabnames[$currIndex-1] : null);
1417  if ( isset($tabs[$currentTab]['actions']['next']) ) $next = $tabs[$currentTab]['actions']['next'];
1418  if ( isset($tabs[$currentTab]['actions']['back']) ) $prev = $tabs[$currentTab]['actions']['back'];
1419  $default = null;
1420  if ( isset($tabs[$currentTab]['actions']['default'] ) ) $default = $tabs[$currentTab]['actions']['default'];
1421 
1422  foreach ( $tabs as $tab ){
1423  if ( @$params['tab'] == $tab['name'] ) continue; // we don't include a button to the current tab
1424  $tabname = $tab['name'];
1425  $atts = array();
1426 
1427  if ( isset($tab['button']['atts']) ) $atts = $tab['button']['atts'];
1428  if ( isset($params['atts']['__global__']) ) $atts = array_merge($atts, $params['atts']['__global__']);
1429  if ( isset($params['atts'][$tab]) ) $atts = array_merge($atts, $params['atts'][$tab]);
1430  if ( !isset($atts['style']) ) $atts['style'] = 'display: none';
1431 
1432 
1433 
1434  $out[] = $factory->createElement('submit', '--session:target:'.$tabname, $tab['label'], $atts);
1435  }
1436  }
1437  if ( isset($prev) ) $out[] = $factory->createElement('submit', '--session:target:__back__', df_translate('scripts.GLOBAL.LABEL_BACK', 'Back'));
1438  if ( isset($next) ) $out[] = $factory->createElement('submit', '--session:target:__next__', df_translate('scripts.GLOBAL.LABEL_NEXT', 'Next'));
1439 
1440  return $out;
1441  }
1442 
1448  function saveSession($record, $new=false, $session_key = null ){
1449  // First get the session data
1450  $session_data = $this->getSessionData(null, $session_key);
1451  if ( !isset($session_data) ) return false;
1452 
1453  // Next make sure that the session is acting on the same record
1454  if ( !$new and $session_data['id'] != $record->getId() ){
1455 
1456  return PEAR::raiseError('Failed to save session because the session data is not registered to the same record.');
1457  }
1458 
1459  if ( $session_data['table'] != $record->_table->tablename ){
1460 
1461  return PEAR::raiseError('Failed to save session because the session data is for a different table than the record.');
1462  }
1463 
1464  $joinRecords = array(); // A form to store all of the join records
1465  // that need to be saved.
1466 
1467 
1468  foreach ( array_keys($session_data['tabs']) as $tabname ){
1469  $temp =& $this->createRecordForm($record, $new, $tabname);
1470 
1471  // Note that this form could be a form for the $record object
1472  // or it could be a form for one of its join records
1473 
1474  $temp->_build();
1475  $this->decorateRecordForm($record, $temp, $new, $tabname);
1476  $temp->push();
1477 
1478  if ( $temp->_record->getId() != $record->getId() ){
1479  $joinRecords[$tabname] =& $temp->_record;
1480  }
1481  unset($temp);
1482  }
1483 
1484  // Now we need to save the current record..
1485  $res = $record->save(null, true);
1486  if ( PEAR::isError($res) ) return $res;
1487 
1488  // Now we save all of the join records
1489 
1490  foreach ( $joinRecords as $name=>$jrecord ){
1491  // Let's make sure we have the proper join keys so that the
1492  // record is linked properly to the current record.
1493 
1494  $jrecord->setValues($record->getJoinKeys($name));
1495 
1496 
1497  $res = $jrecord->save(null, true);
1498  if ( PEAR::isError($res) ){
1499  return $res;
1500  }
1501  unset($jrecord);
1502  }
1503 
1504 
1505  $this->clearSessionData($session_key);
1506 
1507  return true;
1508 
1509 
1510 
1511  }
1512 
1513 
1514 
1515 
1516 }
1517 
1518 import('HTML/QuickForm.php');
1524 class HTML_QuickFormFactory extends HTML_QuickForm {
1525  function HTML_QuickFormFactory($name){
1526  $this->HTML_QuickForm($name);
1527  }
1528 
1529  function &addElement($element){
1530  $args = func_get_args();
1531  if ( is_object($element) and $this->elementExists($element->getName()) ){
1532  $this->removeElement($element->getName());
1533  } else {
1534 
1535  if ( $this->elementExists($args[1]) ){
1536  $this->removeElement($args[1]);
1537  }
1538  }
1539  switch ( count($args) ){
1540  case 1:
1541  $res =& parent::addElement($args[0]);break;
1542  case 2:
1543  $res =& parent::addElement($args[0],$args[1]);break;
1544  case 3:
1545  $res =& parent::addElement($args[0],$args[1],$args[2]); break;
1546  case 4:
1547  $res =& parent::addElement($args[0], $args[1], $args[2], $args[3]);break;
1548  case 5:
1549  $res =& parent::addElement($args[0], $args[1], $args[2], $args[3], $args[4]);break;
1550  case 6:
1551  $res =& parent::addElement($args[0], $args[1], $args[2], $args[3], $args[4], $args[5]);break;
1552  case 7:
1553  $res =& parent::addElement($args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6]);break;
1554  }
1555  return $res;
1556  }
1557 }