Xataface  2.0alpha2
Xataface Application Framework
 All Data Structures Namespaces Files Functions Variables Groups Pages
Table.php
Go to the documentation of this file.
1 <?php
2 /*-------------------------------------------------------------------------------
3  * Xataface Web Application Framework
4  * Copyright (C) 2005-2008 Web Lite Solutions Corp (shannah@sfu.ca)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *-------------------------------------------------------------------------------
20  */
21 
22 
23 
24 /*******************************************************************************
25  * File: Table.php
26  * Author: Steve Hannah <shannah@sfu.ca>
27  * Description:
28  * Encapsulates a table of a data driven data type.
29  *
30  ******************************************************************************/
31 
32 if ( !defined('DATAFACE_EXTENSION_LOADED_APC') ){
33 
34  define('DATAFACE_EXTENSION_LOADED_APC',extension_loaded('apc'));
35 
36 }
37 
38 import( 'PEAR.php');
39 import( 'Dataface/Error.php');
40 import( 'Dataface/Globals.php');
41 import( 'Dataface/Relationship.php');
42 import( 'Dataface/converters/date.php');
43 import( 'Dataface/Application.php');
44 //require_once dirname(__FILE__).'/../config.inc.php';
45 import( 'SQL/Parser.php');
46 import( 'SQL/Parser/wrapper.php');
47 import( 'Dataface/Serializer.php');
48 import( 'Dataface/ConfigTool.php');
49 
50 define('Dataface_Table_UseCache', false);
51 
56 $GLOBALS['Dataface_Table_DefaultFieldPermissions'] = array(
57  "view"=>'View',
58  "edit"=>'Edit'
59  );
60 
61 $GLOBALS['Dataface_Table_DefaultTablePermissions'] = array(
62  "view"=>"View",
63  "edit"=>"Edit",
64  "delete"=>"Delete"
65  );
66 
67 define( 'SCHEMA_INVALID_ADDRESS_ERROR', 1);
68 define( 'SCHEMA_NO_SUCH_FIELD_ERROR',2);
69 define( 'SCHEMA_AMBIGUOUS_FIELD_ERROR',3);
70 define( 'Dataface_SCHEMA_NO_VALUE_ASSIGNED', 4);
71 define( 'Dataface_SCHEMA_INDEX_OUT_OF_BOUNDS_ERROR', 5);
72 define( 'Dataface_SCHEMA_SQL_ERROR', 6);
73 define( 'Dataface_SCHEMA_NO_SUCH_RELATIONSHIP_ERROR',7);
74 define( 'Dataface_SCHEMA_INVALID_VALUE_ERROR',8);
75 define( 'DATAFACE_TABLE_SQL_PARSE_ERROR', 9);
76 define( 'DATAFACE_TABLE_RELATED_RECORD_CREATION_ERROR', 10);
77 define( 'DATAFACE_TABLE_RELATED_RECORD_REQUIRED_FIELD_MISSING_ERROR',12);
78 define( 'DATAFACE_TABLE_RECORD_RELATED_RECORD_BLOCKSIZE', 30);
79 define( 'DATAFACE_TABLE_SQL_ERROR',11);
80 define( 'SCHEMA_TABLE_NOT_FOUND', 12);
81 
123 
128  var $db;
129 
134  var $_fields = array();
135 
141  var $_grafted_fields = null;
142 
143 
151  var $_transient_fields = null;
152 
159  var $_parentTable = null;
160 
165  var $_cookedValuelists=array();
166 
167 
190  var $_joinTables = null;
191 
198  var $_sql;
199 
208  var $_proxyViews = array();
209 
216  var $_fieldsByTab = null;
217 
218 
223  var $_relatedFields = array();
224 
229  var $_fieldgroups = array();
230 
235  var $_tabs = array();
236 
243  var $_keys = array();
244 
250  var $_iniFile = '';
251 
256  var $_atts;
257 
262  var $_relationships = array();
263 
271  var $_relationshipRanges;
272 
277  var $_defaultRelationshipRange = array(0, DATAFACE_TABLE_RECORD_RELATED_RECORD_BLOCKSIZE);
278 
283  var $_valuelists;
284 
289  var $_delegate;
290 
295  var $status;
296 
297 
304  var $_relationshipsLoaded = false;
305 
306 
310  var $errors = array();
311 
316  var $_permissions;
317 
322  var $_serializer;
323 
330  var $_filters = array();
331 
332 
338  var $_importFilters = null;
339 
347  var $_fieldsConfig;
348 
352  var $_relationshipsConfig;
353 
357  var $_valuelistsConfig;
358 
362  var $_actionsLoaded = false;
363 
367  var $_actionsConfig = null;
368 
369  // reference to application object
373  var $app = null;
374 
375 
380  var $_permissionsLoaded = false;
381 
385  var $translations = null;
386 
387 
391  var $_cache = array();
392 
397  var $metadataColumns = null;
398 
403  var $_securityFilter = array();
404 
408  var $_securityFilterLoaded = false;
409 
410 
419  var $descriptionField;
420 
424  var $createdField;
425 
429  var $creatorField;
430 
434  var $lastUpdatedField;
435 
439  var $publicLinkTemplate;
440 
444  var $bodyField;
445 
446  var $versionField = -1;
447 
451  var $_global_field_properties;
452  //----------------------------------------------------------------------------------------------
453  // @{
474  public static function &loadTable($name, $db=null, $getAll=false, $quiet=false){
475  if ( !is_string($name) ){
476  throw new Exception("In Dataface_Table::loadTable() expected first argument to be a string but received '".get_class($name)."'", E_USER_ERROR);
477  }
478 
479  if ( $db === null and defined('DATAFACE_DB_HANDLE')) $db = DATAFACE_DB_HANDLE;
480  if ( !isset( $_tables ) ){
481  static $_tables = array();
482 
483  static $_db = '';
484  }
485  if ( $getAll ){
486  return $_tables;
487 
488  }
489  if ( $db ) $_db = $db;
490  if ( !isset( $_tables[$name] ) ){
492  $_tables[$name] = new Dataface_Table($name, $_db, $quiet);
493 
494 
495  $_tables[$name]->postInit();
496  }
497 
498  return $_tables[$name];
499 
500  }
501 
502 
510  function Dataface_Table($tablename, $db=null, $quiet=false){
511  if ( !$tablename || !is_string($tablename) ){
512  throw new Exception("Invalid tablename specified: $tablename", E_USER_ERROR);
513  }
514  if ( strpos($tablename,'`') !== false ){
515  throw new Exception("Invalid character found in table '$tablename'.", E_USER_ERROR);
516 
517  }
518  import('Dataface/Record.php');
519  $this->app =& Dataface_Application::getInstance();
520  // register this table name with the application object so we can keep
521  // track of which tables are used on each request. This helps with
522  // caching.
523  $this->app->tableNamesUsed[] = $tablename;
524  $this->tablename = $tablename;
525  if ( $db === null and defined('DATAFACE_DB_HANDLE') ) $db = DATAFACE_DB_HANDLE;
526  $this->db = $db;
527  $this->_permissions = Dataface_PermissionsTool::getRolePermissions($this->app->_conf['default_table_role']);
528 
529 
530  $this->tablename = str_replace(' ', '', $this->tablename);
531  // prevent malicious SQL injection
532 
533  $this->_atts = array();
534  $this->_atts['name'] = $this->tablename;
535 
536  $this->_atts['label'] = (isset( $this->app->_tables[$this->tablename] ) ? $this->app->_tables[$this->tablename] : $this->tablename);
537 
538  $mod_times =& self::getTableModificationTimes();
539 
540  $apc_key = DATAFACE_SITE_PATH.'-Table.php-'.$this->tablename.'-columns';
541  $apc_key_fields = $apc_key.'-fields';
542  $apc_key_keys = $apc_key.'-keys';
543  $apc_key_mtime = $apc_key.'__mtime';
544  if ( DATAFACE_EXTENSION_LOADED_APC
545  and
546  ( !@$_GET['--refresh-apc'] )
547  and
548  ( @$mod_times[$this->tablename] < apc_fetch($apc_key_mtime) )
549  and
550  ( $this->_fields = apc_fetch($apc_key_fields) )
551  and
552  ( $this->_keys = apc_fetch($apc_key_keys) )
553  ){
554  // no need to refresh the cache
555  $fieldnames = array_keys($this->_fields);
556 
557  } else {
558 
559 
560 
561 
562  $res = mysql_query("SHOW COLUMNS FROM `".$this->tablename."`", $this->db);
563  if ( !$res ){
564  if ( $quiet ){
565  return PEAR::raiseError("Error performing mysql query to get column information from table '".$this->tablename."'. The mysql error returned was : '".mysql_error($this->db));
566  } else {
567  throw new Exception("Error performing mysql query to get column information from table '".$this->tablename."'. The mysql error returned was : '".mysql_error($this->db), E_USER_ERROR);
568  }
569 
570  }
571 
572  if ( mysql_num_rows($res) > 0 ){
573  while ( $row = mysql_fetch_assoc($res) ){
574  /*
575  Example row as follows:
576  Array
577  (
578  [Field] => id
579  [Type] => int(7)
580  [Null] =>
581  [Key] => PRI
582  [Default] =>
583  [Extra] => auto_increment
584  )
585  */
586 
587 
588 
589  $widget = array();
590  $widget['label'] = ucfirst(str_replace('_',' ',$row['Field']));
591  $widget['description'] = '';
592  $widget['label_i18n'] = $this->tablename.'.'.$row['Field'].'.label';
593  $widget['description_i18n'] = $this->tablename.'.'.$row['Field'].'.description';
594  $widget['macro'] = '';
595  $widget['helper_css'] = '';
596  $widget['helper_js'] = '';
597  $widget['type'] = 'text';
598  $widget['class'] = '';
599  $widget['atts'] = array();
600  if ( preg_match( '/text/', $row['Type']) ){
601  $widget['type'] = 'textarea';
602  } else if ( preg_match( '/blob/', $row['Type']) ){
603  $widget['type'] = 'file';
604  }
605 
606 
607 
608  $widget['class'] = 'default';
609 
610  $row['tablename'] = $this->tablename;
611  $row['widget'] =& $widget;
612  $row['tableta'] = 'default';
613  $row['vocabulary'] = '';
614  $row['enforceVocabulary'] = false;
615  $row['validators'] = array();
616  $row['name'] = $row['Field'];
617  $row['permissions'] = Dataface_PermissionsTool::getRolePermissions($this->app->_conf['default_field_role']);
618  $row['repeat'] = false;
619  $row['visibility'] = array('list'=>'visible', 'browse'=>'visible', 'find'=>'visible');
620 
621 
622 
623 
624 
625 
626 
627 
628 
629 
630 
631  $this->_fields[ $row['Field'] ] = $row;
632  if ( strtolower($row['Key']) == strtolower('PRI') ){
633  $this->_keys[ $row['Field'] ] =& $this->_fields[ $row['Field'] ];
634  }
635 
636  unset($widget);
637  }
638  }
639 
640  mysql_free_result($res);
641 
642 
643 
644 
645 
646  // check for obvious field types
647  $fieldnames = array_keys($this->_fields);
648  foreach ($fieldnames as $key){
649  $matches = array();
650 
651  if ( preg_match( '/^(.*)_mimetype$/', $key, $matches) and
652  isset( $this->_fields[$matches[1]] ) /*and
653  ($this->isBlob($matches[1]) or $this->isContainer($matches[1]))*/ ){
654 
655  $this->_fields[$key]['widget']['type'] = 'hidden';
656  $this->_fields[$matches[1]]['mimetype'] = $key;
657  $this->_fields[$key]['metafield'] = true;
658  } else if ( preg_match( '/^(.*)_filename$/', $key, $matches) and
659  isset( $this->_fields[$matches[1]] ) and
660  $this->isBlob($matches[1]) ){
661  $this->_fields[$key]['widget']['type'] = 'hidden';
662  $this->_fields[$matches[1]]['filename'] = $key;
663  $this->_fields[$key]['metafield'] = true;
664  } else if ( preg_match('/password/', strtolower($key) ) ){
665  $this->_fields[$key]['widget']['type'] = 'password';
666  } else if ( $this->_fields[$key]['Extra'] == 'auto_increment'){
667  $this->_fields[$key]['widget']['type'] = 'hidden';
668  } else if ( preg_match('/^date/', strtolower($this->_fields[$key]['Type']) ) ){
669  $this->_fields[$key]['widget']['type'] = 'calendar';
670 
671  if ( !preg_match('/time/', strtolower($this->_fields[$key]['Type']) ) ){
672  $this->_fields[$key]['widget']['showsTime'] = false;
673  $this->_fields[$key]['widget']['ifFormat'] = '%Y-%m-%d';
674  }
675  } else if ( preg_match('/timestamp/', strtolower($this->_fields[$key]['Type']) ) ){
676  $this->_fields[$key]['widget']['type'] = 'static';
677  } else if ( strtolower(substr($this->_fields[$key]['Type'],0, 4)) == 'time'){
678  $this->_fields[$key]['widget']['type'] = 'time';
679  } else if ( substr($this->_fields[$key]['Type'], 0,4) == 'enum' ){
680  $this->_fields[$key]['widget']['type'] = 'select';
681  }
682  }
683  if ( DATAFACE_EXTENSION_LOADED_APC ){
684  apc_store($apc_key_fields, $this->_fields);
685  apc_store($apc_key_keys, $this->_keys);
686  apc_store($apc_key_mtime, time());
687  }
688  }
689 
690  $this->_loadFieldsIniFile();
691 
692  $parent =& $this->getParent();
693  if ( isset($parent) ){
694  foreach ( array_keys($this->keys()) as $currkey ){
695  $this->_fields[$currkey]['widget']['type'] = 'hidden';
696  }
697  }
698 
699  $curr_order = 0;
700  $needs_sort = false; // flag to indicate if any "order" attributes were set in the fields.ini file
701 
702  foreach (array_keys($this->_fields) as $field_name ){
703  if ( isset($this->_fields[$field_name]['order']) ) {
704  $needs_sort = true;
705  $curr_order++;
706  }
707  else $this->_fields[$field_name]['order'] = $curr_order++;
708  }
709 
710  //$this->_loadValuelistsIniFile();
711  // lazily created now in valuelists() and getValuelist()
712  //$this->_loadRelationshipsIniFile();
713  // had to be removed to prevent possibility of infinite loops. This is now called lazily as relationships
714  // are needed.
715 
716  //$GLOBALS['DATAFACE_QUERYBUILDER_SECURITY_CONSTRAINTS'][$this->tablename] = $this->_filters;
717 
718 
719  // get some validation information to start with
720  foreach ($fieldnames as $key){
721  $row =& $this->_fields[$key];
722 
723 
724  // handle case where this is an enumerated field
725  $matches = array();
726  if ( preg_match('/^(enum|set)\(([^\)]+)\)$/', $row['Type'], $matches )){
727 
728  $valuelists =& $this->valuelists();
729  $options = explode(',', $matches[2]);
730 
731  $vocab = array();
732  foreach ( $options as $val){
733  $val = substr($val,1,strlen($val)-2); // strip off the quotes
734  $vocab[$val] = $val;
735  }
736 
737  $valuelists[$row['name']."_values"] =& $vocab;
738  if ( !@$row['vocabulary'] ) $row['vocabulary'] = $row['name']."_values";
739 
740 
741  if ( strtolower($matches[1]) == 'set'){
742  $row['repeat'] = true;
743  } else {
744  $row['repeat'] = false;
745  }
746 
747  $row['widget']['type'] = 'select';
748 
749  $opt_keys = array_keys($vocab);
750 
751  $dummy = '';
752  if ( $this->isYesNoValuelist($row['name']."_values", $dummy, $dummy) ){
753  $widget['type'] = 'checkbox';
754  }
755 
756  unset( $valuelists);
757  unset( $vocab);
758 
759  }
760 
761 
762 
763 
764 
765  if ( !$this->isBlob($row['name']) and
766  !$this->isText($row['name']) and
767  !$this->isDate($row['name']) and
768  !$this->isPassword($row['name']) and
769  $row['Null'] != 'YES' and
770  strlen($row['Default']) == 0 and
771  $row['Extra'] != 'auto_increment' and
772  @$row['validators']['required'] !== 0){
773  $row['validators'][ 'required' ] = array('message' => $row['widget']['label'] ." is a required field.",
774  'arg' => '' );
775  } else if ( @$row['validators']['required'] === 0 ){
776  unset($row['validators']['required']);
777  }
778 
779 
780  // check for signs that this is a repeated field
781  if ( $row['widget']['type'] == 'checkbox' and isset( $row['vocabulary'] ) and ( $this->isText($key) or $this->isChar($key) ) ){
782  if ( isset( $row['repeat'] ) and !$row['repeat'] ){
783  //do nothing
784  } else {
785  $row['repeat'] = true;
786  }
787  }
788  if ( !isset($row['repeat']) ) $row['repeat'] = false;
789  if ( $row['repeat'] and !isset( $row['separator'] )){
790  $row['separator'] = "\n";
791  }
792 
793  if ( !isset($row['display']) and $this->isText($row['name']) ) $row['display'] = 'block';
794  else if ( !isset($row['display']) ) $row['display'] = 'inline';
795 
796  unset($row);
797  }
798 
799  // check for obvious signs that this is a repeating field
800 
801  // sort the fields now based on their order attribute
802  if ($needs_sort){
803  uasort($this->_fields, array(&$this, '_compareFields'));
804  }
805 
806 
807  }
808 
813  function postInit(){
814  // call init method of delegate
815  $delegate =& $this->getDelegate();
816 
817 
818  $parent =& $this->getParent();
819  if ( isset($parent) ){
820  $pdelegate =& $parent->getDelegate();
821  if ( isset($pdelegate) and method_exists($pdelegate, 'init') ){
822  $res = $pdelegate->init($this);
823  }
824  }
825 
826  if ( $delegate !== null and method_exists($delegate, 'init') ){
827  $res = $delegate->init($this);
828  }
829 
830  foreach ( array_keys($this->_fields) as $key){
831  $this->_fields[$key]['widget']['description'] = $this->getFieldProperty('widget:description', $key);
832  $this->_fields[$key]['widget']['label'] = $this->getFieldProperty('widget:label', $key);
833  $this->_fields[$key]['vocabulary'] = $this->getFieldProperty('vocabulary', $key);
834  $this->_fields[$key]['widget']['type'] = $this->getFieldProperty('widget:type', $key);
835  $this->_fields[$key]['widget'] = array_merge($this->_fields[$key]['widget'], $this->getFieldProperty('widget', $key));
836  }
837 
838  if ( count($this->_securityFilter) == 0 ){
839  $this->setSecurityFilter();
840  }
841 
842  }
843 
844 
845  // @}
846  // END Initialization
847  //----------------------------------------------------------------------------------------------
848 
849 
850  //----------------------------------------------------------------------------------------------
851  // @{
859  // Some static utility methods
860 
869  public static function &getTableField($address, $db=''){
870  $addr = array_map('trim', explode('.', $address) );
871  if ( sizeof($addr) != 2 ) {
872  return PEAR::raiseError(SCHEMA_NO_SUCH_FIELD_ERROR, null,null,null, "Call to getTableField with invalid address: '$address'. Address must be absolute in the form 'table_name.column_name'.");
873  }
874 
875  $table =& self::loadTable($addr[0], $db);
876  if ( PEAR::isError($table) ){
877  return $table;
878  }
879 
880  $fields =& $table->fields(false,true,true);
881  if ( !isset( $fields[ $addr[1] ] ) ){
882  return PEAR::raiseError(SCHEMA_NO_SUCH_FIELD_ERROR, null,null,null, "Call to getTableField with invalid address: '$address'. Column $addr[1] does not exists in table $addr[0].");
883  }
884 
885  return $fields[$addr[1]];
886  }
887 
888 
889 
890 
891 
899  public static function fieldExists($address, $db=''){
900 
901  $res = self::getTableField($address, $db);
902  return !PEAR::isError($res);
903 
904  }
905 
913  public static function tableExists($tablename, $usecache=true){
915  static $index = 0;
916  if ($index === 0 ) $index = array();
917  if ( !isset($index[$tablename]) or !$usecache ) {
918  $index[$tablename] = mysql_num_rows(mysql_query("show tables like '".addslashes($tablename)."'", $app->db()));
919  }
920  return $index[$tablename];
921  }
922 
931  function exists($fieldname, $checkParent=true){
932  return $this->hasField($fieldname, $checkParent);
933  }
934 
941  function hasField($fieldname, $checkParent=true){
942  if ( strpos($fieldname,'.') > 0 ){
943  list($rel_name, $fieldname) = explode('.', $fieldname);
944  if ( !$this->hasRelationship($rel_name) ) return false;
945  $relationship =& $this->getRelationship($rel_name);
946  if ( $relationship->hasField($fieldname, true, true) ) return true;
947  return false;
948  } else {
949  if ( array_key_exists($fieldname, $this->fields(false,true)) ) return true;
950  //if ( isset( $this->_fields[$fieldname] ) ) return true;
951  //if ( isset( $this->_graftedFields[$fieldname]) ) return true;
952  $delegate =& $this->getDelegate();
953  if ( $delegate !== null and method_exists($delegate, 'field__'.$fieldname) ) return true;
954  $transient =& $this->transientFields();
955  if ( isset($transient[$fieldname]) ) return true;
956 
957  if ( $checkParent ){
958  $parent =& $this->getParent();
959  if ( isset($parent) and $parent->hasField($fieldname, $checkParent) ) return true;
960  }
961  }
962  return false;
963 
964  }
965 
982  public static function absoluteFieldName($field, $tablenames, $db='', $columnList=null){
983  if ( self::fieldExists($field, $db) ){
984  return $field;
985  } else if ( strpos($field, '.') > 0 ){
986  return self::getTableField($field, $db);
987  } else {
988  $found = 0;
989  $name = '';
990 
991  if ( is_array($columnList) ){
992  foreach ( $columnList as $column ){
993  if ( preg_match('/^(\w+)\.'.$field.'$/', $column) ){
994  $name = $column;
995  $found++;
996  }
997  }
998  } else {
999  foreach ($tablenames as $table){
1000  if ( self::fieldExists($table.'.'.$field, $db) ){
1001  $name = $table.'.'.$field;
1002  $found++;
1003  }
1004  }
1005  }
1006  }
1007 
1008  if ( $found == 0 ){
1009  $err = PEAR::raiseError(SCHEMA_NO_SUCH_FIELD_ERROR,null,null,null, "Field $field does not exist in tables ".implode(',', $tablenames).".");
1010 
1011  throw new Exception($err->toString(), E_USER_WARNING);
1012  }
1013 
1014 
1015  return $name;
1016 
1017  }
1018 
1025  if ( strpos($fieldname,'.') !== false ){
1026  $path = explode('.', $fieldname);
1027  return $path[1];
1028  }
1029  return $fieldname;
1030 
1031  }
1032 
1033 
1038  function _fieldsIniFilePath(){
1039  return $this->basePath().'/tables/'.basename($this->tablename).'/fields.ini';
1040  }
1041 
1068  function guessField($types, $patterns, $forcePattern=false){
1069  $candidates = array();
1070  $max = null;
1071  foreach ($this->fields(false,true) as $field){
1072  $type = strtolower($this->getType($field['name']));
1073  if ( !isset($types[$type]) ){
1074  continue;
1075  }
1076 
1077  $score = $types[$type];
1078  $found=false;
1079  foreach ($patterns as $pattern=>$value){
1080  if ( preg_match($pattern, $field['name']) ){
1081  $score *= $value;
1082  $found=true;
1083  }
1084  }
1085  if ( $forcePattern and !$found ){
1086  $score = 0;
1087  }
1088  $candidates[$field['name']] = $score;
1089  if ( !isset($max) ) $max = $field['name'];
1090  else if ( $candidates[$max] < $score ){
1091  $max = $field['name'];
1092  }
1093  }
1094  return $max;
1095 
1096  }
1097 
1098 
1108  if ( !isset($this->descriptionField) ){
1109 
1110  $this->descriptionField = $this->guessField(
1111  array('text'=>10, 'mediumtext'=>10, 'shorttext'=>10, 'longtext'=>2,
1112  'varchar'=>1, 'char'=>1),
1113  array('/description|summary|overview/'=>10, '/desc/'=>2)
1114  );
1115 
1116  }
1117  return $this->descriptionField;
1118 
1119  }
1120 
1121 
1129  function getCreatedField(){
1130  if ( !isset($this->createdField) ){
1131 
1132  $this->createdField = $this->guessField(
1133  array('datetime'=>10, 'timestamp'=>10, 'date'=>1),
1134  array('/created|inserted|added|posted|creation|insertion/i'=>10, '/timestamp/i'=>5)
1135  );
1136 
1137  }
1138  return $this->createdField;
1139  }
1140 
1148  function getCreatorField(){
1149  if ( !isset($this->creatorField) ){
1150 
1151  $this->creatorField = $this->guessField(
1152  array('varchar'=>10,'char'=>10,'int'=>5),
1153  array('/(created.*by)|(owner)|(posted*by)|(author)|(creator)/i'=>10),
1154  true
1155  );
1156 
1157  }
1158  return $this->creatorField;
1159  }
1160 
1170  function getVersionField(){
1171  if ( $this->versionField == -1 ){
1172  $this->versionField = null;
1173  $fields = & $this->fields(false, true);
1174  foreach ($fields as $field){
1175  if ( @$field['version'] ){
1176  $this->versionField = $field['name'];
1177  break;
1178  }
1179  }
1180  }
1181  return $this->versionField;
1182  }
1183 
1191  $this->versionField = $field;
1192  }
1193 
1194  function isVersioned(){
1195  $vf = $this->getVersionField();
1196  return isset($vf);
1197  }
1198 
1199 
1208  if ( !isset($this->lastUpdatedField) ){
1209  $this->lastUpdatedField = $this->guessField(
1210  array('datetime'=>10,'timestamp'=>12),
1211  array('/updated|modified|change|modification|update/i'=>10,'/timestamp/i'=>5)
1212  ) ;
1213  }
1214  return $this->lastUpdatedField;
1215  }
1216 
1225  function getBodyField(){
1226  if ( !isset($this->bodyField) ){
1227  $this->bodyField = $this->guessField(
1228  array('text'=>10,'longtext'=>10,'mediumtext'=>10),
1229  array('/main|body|content|profile|writeup|bio/i'=>10)
1230  );
1231  }
1232  }
1233 
1234 
1235 
1242  function _compareFields($a,$b){
1243  if ( @$a['order'] == @$b['order'] ) return 0;
1244  return ( @$a['order'] < @$b['order'] ) ? -1 : 1;
1245  }
1246 
1247 
1266  function &getIndexes(){
1267  if ( !isset( $this->_indexes) ){
1268  $this->_indexes = array();
1269  $res = mysql_query("SHOW index FROM `".$this->tablename."`", $this->db);
1270  if ( !$res ){
1271  throw new Exception("Failed to get index list due to a mysql error: ".mysql_error($this->db), E_USER_ERROR);
1272  }
1273 
1274  while ( $row = mysql_fetch_array($res) ){
1275  if ( !isset( $this->_indexes[ $row['Key_name'] ] ) )
1276  $this->_indexes[ $row['Key_name'] ] = array();
1277  $index =& $this->_indexes[$row['Key_name']];
1278  $index['name'] = $row['Key_name'];
1279  if ( !isset( $index['columns'] ) )
1280  $index['columns'] = array();
1281  $index['columns'][] = $row['Column_name'];
1282  $index['unique'] = ( $row['Non_unique'] ? false : true );
1283  $index['type'] = $row['Index_type'];
1284  $index['comment'] = $row['Comment'];
1285  unset($index);
1286  }
1287  mysql_free_result($res);
1288 
1289  }
1290 
1291  return $this->_indexes;
1292 
1293 
1294  }
1295 
1307 
1308  $indexes =& $this->getIndexes();
1309  $fields = array();
1310  foreach ( array_keys($indexes) as $indexName ){
1311  if ( strtolower($indexes[$indexName]['type']) === 'fulltext' ){
1312  foreach ( $indexes[$indexName]['columns'] as $col ){
1313  $fields[] = $col;
1314  }
1315  }
1316  }
1317 
1318  return $fields;
1319  }
1320 
1331  function getCharFields($includeGraftedFields=false, $excludeUnsearchable=false){
1332  if ( !isset($this->_cache[__FUNCTION__]) ){
1333  $out = array();
1334  foreach ( array_keys($this->fields(false, $includeGraftedFields)) as $field){
1335  if ( $this->isChar($field) or $this->isText($field) or (strtolower($this->getType($field)) == 'enum') or ($this->getType($field) == 'container') ){
1336  if ( $excludeUnsearchable and !$this->isSearchable($field) ) continue;
1337  $out[] = $field;
1338  }
1339  }
1340  $this->_cache[__FUNCTION__] = $out;
1341  }
1342  return $this->_cache[__FUNCTION__];
1343 
1344  }
1345 
1354  $fld =& $this->getField($field);
1355  return !@$fld['not_searchable'];
1356  }
1357 
1368 
1369  $field =& $this->getField($fieldname);
1370  if ( !isset($field['metafield']) ){
1371  $fields =& $this->fields();
1372  $field_names = array_keys($fields);
1373  foreach ( $field_names as $fn){
1374  if ( ( isset($fields[$fn]['mimetype']) and $fields[$fn]['mimetype'] == $fieldname ) or
1375  ( isset($fields[$fn]['filename']) and $fields[$fn]['filename'] == $fieldname ) ) {
1376  $field['metafield'] = true;
1377  break;
1378  }
1379  }
1380  if ( !isset($field['metafield']) ){
1381  $field['metafield'] = false;
1382  }
1383  }
1384  return $field['metafield'];
1385 
1386 
1387  }
1388 
1389 
1401  if ( !isset($this->metadataColumns) ){
1402  $metatablename = $this->tablename.'__metadata';
1403  $sql = "SHOW COLUMNS FROM `{$metatablename}`";
1404  $res = mysql_query($sql, $this->db);
1405  if ( !$res || mysql_num_rows($res) == 0){
1407  $res = mysql_query($sql, $this->db);
1408  }
1409  if ( !$res ) throw new Exception(mysql_error($this->db), E_USER_ERROR);
1410  if ( mysql_num_rows($res) == 0 ) throw new Exception("No metadata table set up for table '{$this->tablename}'", E_USER_ERROR);
1411  $this->metadataColumns = array();
1412  while ($row = mysql_fetch_assoc($res) ){
1413  if ( substr($row['Field'],0,2) == '__' ){
1414  $this->metadataColumns[] = $row['Field'];
1415  }
1416  }
1417 
1418  }
1419  return $this->metadataColumns;
1420  }
1421 
1422 
1433  function &formFields($byTab=false, $includeTransient=false){
1434  if ( !isset($this->_cache[__FUNCTION__][intval($byTab)][intval($includeTransient)]) ){
1435  $fields = $this->fields($byTab,false,$includeTransient);
1436  $parent =& $this->getParent();
1437  if ( isset($parent) ){
1438 
1439  $fields = array_merge_recursive_unique($parent->fields($byTab,false,$includeTransient), $fields);
1440  uasort($fields, array(&$this, '_compareFields'));
1441  }
1442 
1443  $this->_cache[__FUNCTION__][intval($byTab)][intval($includeTransient)] =& $fields;
1444  }
1445  return $this->_cache[__FUNCTION__][intval($byTab)][intval($includeTransient)];
1446  }
1447 
1479  function &fields($byTab=false, $includeGrafted=false, $includeTransient=false){
1480  if ( !$byTab) {
1481  //if ( $includeGrafted or $includeTransient){
1482  if ( !isset($this->_cache[__FUNCTION__][intval($includeGrafted)][intval($includeTransient)]) ){
1483  //return $this->_cache[intval($includeGrafted)][intval($includeTransient)];
1484 
1485  $fields = array();
1486 
1487  if ( $includeGrafted ){
1488  $grafted_fields =& $this->graftedFields();
1489  foreach (array_keys($grafted_fields) as $fname){
1490  $fields[$fname] =& $grafted_fields[$fname];
1491  }
1492  }
1493 
1494  if ( $includeTransient ){
1495  $transient_fields =& $this->transientFields();
1496  foreach ( array_keys($transient_fields) as $fname){
1497  if ( !isset($fields[$fname]) ) $fields[$fname] =& $transient_fields[$fname];
1498 
1499  }
1500  }
1501 
1502  if ( count($fields) > 0 ){
1503  $fields = array_merge_recursive_unique($this->_fields, $fields);
1504  uasort($fields, array(&$this, '_compareFields'));
1505  $this->_cache[__FUNCTION__][intval($includeGrafted)][intval($includeTransient)] =& $fields;
1506 
1507  } else {
1508  $this->_cache[__FUNCTION__][intval($includeGrafted)][intval($includeTransient)] =& $this->_fields;
1509  }
1510  }
1511 
1512  return $this->_cache[__FUNCTION__][intval($includeGrafted)][intval($includeTransient)];
1513 
1514  }
1515  else {
1516  if ( !isset( $this->_fieldsByTab ) ){
1517  $this->_fieldsByTab = array();
1518 
1519  foreach ( $this->fields(false,$includeGrafted, $includeTransient) as $field){
1520 
1521  $tab = ( isset( $field['tab'] ) ? $field['tab'] : '__default__');
1522 
1523  if ( !isset( $this->_fieldsByTab[ $tab] ) ){
1524  $this->_fieldsByTab[ $tab ] = array();
1525  }
1526  $this->_fieldsByTab[ $tab ][$field['name']] = $field;
1527 
1528  }
1529  }
1530  return $this->_fieldsByTab;
1531 
1532  }
1533  }
1534 
1560  function &graftedFields($includeParent=true){
1561  $tsql = $this->sql();
1562  if ( $includeParent ) $includeParent = 1;
1563  else $includeParent = 0;
1564 
1565  if ( !isset($this->_cache[__FUNCTION__][intval($includeParent)]) ){
1566  //if ( !isset($this->_grafted_fields) ){
1567 
1568  $this->_grafted_fields = array();
1569  if (isset($tsql)){
1570 
1571  $this->_grafted_fields = array();
1572  import('SQL/Parser.php');
1573  $parser = new SQL_Parser(null,'MySQL');
1574  $data = $parser->parse($tsql);
1575  foreach ( $data['columns'] as $col ){
1576  if ( $col['type'] != 'glob' ){
1577  $alias = ( @$col['alias'] ? $col['alias'] : $col['value']);
1578  if ( isset($this->_fields[$alias]) ) continue;
1579  $this->_grafted_fields[$alias] = $this->_newSchema('varchar(32)', $alias);
1580  $this->_grafted_fields[$alias]['grafted']=1;
1581  if ( isset($this->_atts[$alias]) and is_array($this->_atts[$alias]) ){
1582 
1583  $this->_parseINISection($this->_atts[$alias], $this->_grafted_fields[$alias]);
1584  }
1585  //array('Field'=>$alias, 'name'=>$alias, 'Type'=>'varchar(32)', 'widget'=>array('label'=>$alias, 'description'=>''));
1586 
1587  }
1588  }
1589  }
1590  if ( $includeParent ){
1591  // We now want to load the parent table columns as well.
1592  $parent =& $this->getParent();
1593  if ( isset($parent) ){
1594  $this->_grafted_fields = array_merge( $parent->fields(false,true), $this->_grafted_fields);
1595  }
1596  }
1597  $this->_cache[__FUNCTION__][intval($includeParent)] = $this->_grafted_fields;
1598 
1599  }
1600 
1601  return $this->_cache[__FUNCTION__][intval($includeParent)];
1602  }
1603 
1614  function &transientFields($includeParent=false){
1615  if ( !isset($this->_cache[__FUNCTION__][intval($includeParent)]) ){
1616  if ( !isset($this->_transient_fields) ){
1617  $this->_transient_fields = array();
1618  foreach ( $this->_atts as $fieldname=>$field ){
1619  if ( !is_array($field) ) continue;
1620  if ( @$field['transient'] ){
1621  $curr = array();
1622  $this->_parseINISection($field, $curr);
1623  if ( @$curr['relationship'] ) $curr['repeat'] = 1;
1624 
1625  $curr = array_merge_recursive_unique($this->_global_field_properties, $curr);
1626  $schema = $this->_newSchema('text',$fieldname);
1627 
1628  $curr = array_merge_recursive_unique($schema, $curr);
1629  $this->_transient_fields[$fieldname] = $curr;
1630  }
1631  }
1632  if ( $includeParent){
1633  $parent =& $this->getParent();
1634  if ( isset($parent) ){
1635  $this->_transient_fields = array_merge( $parent->transientFields(), $this->_transient_fields);
1636  }
1637  }
1638  }
1639  $this->_cache[__FUNCTION__][intval($includeParent)] =& $this->_transient_fields;
1640 
1641 
1642  }
1643  //return $this->_transient_fields;
1644  return $this->_cache[__FUNCTION__][intval($includeParent)];
1645  }
1646 
1651  function _hasFieldsIniFile(){
1652 
1653  return file_exists( $this->_fieldsIniFilePath() );
1654 
1655  }
1656 
1666  function &delegateFields($includeParent=false){
1667  if ( !isset($this->_cache[__FUNCTION__][intval($includeParent)]) ){
1668  //if ( !isset($this->_transient_fields) ){
1669  $fields = array();
1670 
1671  $del =& $this->getDelegate();
1672  if ( isset($del) ){
1673  $delegate_methods = get_class_methods(get_class($del));
1674 
1675  $delegate_fields = preg_grep('/^field__/', $delegate_methods);
1676 
1677  foreach ($delegate_fields as $dfield){
1678  $dfieldname = substr($dfield,7);
1679  $fields[$dfieldname] = $this->_newSchema('varchar(32)', $dfieldname);
1680  $fields[$dfieldname]['visibility']['browse'] = 'hidden';
1681  if ( isset($this->_atts[$dfieldname]) and
1682  is_array($this->_atts[$dfieldname]) ){
1683  $this->_parseINISection($this->_atts[$dfieldname], $fields[$dfieldname]);
1684 
1685  }
1686 
1687 
1688  }
1689 
1690 
1691  if ( $includeParent){
1692  $parent =& $this->getParent();
1693  if ( isset($parent) ){
1694  $fields = array_merge( $parent->delegateFields(), $fields);
1695  }
1696  }
1697  }
1698  $this->_cache[__FUNCTION__][intval($includeParent)] = $fields;
1699 
1700 
1701  }
1702  //return $this->_transient_fields;
1703  return $this->_cache[__FUNCTION__][intval($includeParent)];
1704 
1705  }
1706 
1717  function sql(){
1718  $del =& $this->getDelegate();
1719  if ( isset($del) and method_exists($del,'__sql__') ){
1720  return $del->__sql__();
1721  } else if ( isset($this->_sql) ){
1722  return $this->_sql;
1723  } else {
1724  return null;
1725  }
1726 
1727  }
1728 
1729 
1744  function getProxyView(){
1745  if ( defined('XATAFACE_DISABLE_PROXY_VIEWS') and XATAFACE_DISABLE_PROXY_VIEWS ){
1746  return null;
1747  }
1748  $sql = $this->sql();
1749 
1750  // If there is no custom SQL then there is no point using a view at all
1751  if ( !$sql ){
1752  return null;
1753  }
1754  $sqlKey = md5($sql);
1755  $viewName = 'dataface__view_'.md5($this->tablename.'_'.$sqlKey);
1756  if ( isset($this->_proxyViews[$viewName]) and $this->_proxyViews[$viewName]) return $viewName;
1757  else if ( isset($this->_proxyViews[$viewName]) and !$this->_proxyViews[$viewName]) return null;
1758 
1759 
1760  if ( Dataface_Application::getInstance()->getMySQLMajorVersion() < 5 ){
1761  $this->_proxyViews[$viewName] = false;
1762  return null;
1763  }
1764 
1765  if ( @$this->app->_conf['multilingual_content'] and $this->getTranslations() ){
1766  $this->_proxyViews[$viewName] = false;
1767  return null;
1768  }
1769  //$app = Dataface_Application::getInstance();
1770  //$dbname = $app->_conf['_database']['name'];
1771  // Check if view already exists
1772  //$res = mysql_query("select TABLE_NAME from information_schema.tables where TABLE_SCHEMA='".addslashes($dbname)."' and TABLE_NAME='".addslashes($viewName)."' limit 1", df_db());
1773 
1774  $res = mysql_query("show tables like '".addslashes($viewName)."'", df_db());
1775  if ( !$res ) throw new Exception(mysql_error(df_db()));
1776  if ( mysql_num_rows($res) < 1 ){
1777  @mysql_free_result($res);
1778  // The view doesn't exist yet
1779  $res = mysql_query("create view `".str_replace('`','', $viewName)."` as ".$sql, df_db());
1780  if ( !$res ){
1781  error_log(mysql_error(df_db()));
1782  $this->_proxyViews[$viewName] = false;
1783  return null;
1784  }
1785 
1786 
1787  } else {
1788  @mysql_free_result($res);
1789  }
1790  $this->_proxyViews[$viewName] = true;
1791 
1792  return $viewName;
1793 
1794  }
1795 
1796 
1810  function &keys(){
1811  return $this->_keys;
1812  }
1813 
1814 
1821  function hasKey($name){
1822  if ( !isset( $this->_fields[$name] ) ) return false;
1823  if ( isset( $this->_fields[$name]['Key'] ) && strtolower($this->_fields[$name]['Key']) == strtolower('PRI') ){
1824  return true;
1825  }
1826  return false;
1827 
1828  }
1829 
1843  function &mandatoryFields(){
1844  $fields = array();
1845  foreach ( array_keys($this->keys()) as $key){
1846  if ( $this->_fields[$key]['Extra'] == 'auto_increment') continue;
1847  $fields[ $key ] =& $this->_fields[$key];
1848  }
1849 
1850  return $fields;
1851 
1852 
1853  }
1854 
1855 
1856 
1857 
1864  public function getDefaultValue($fieldname){
1865  $field =& $this->getField($fieldname);
1866  if ( @$field['tablename'] and $field['tablename'] != $this->tablename ){
1867  // Some fields may have been taken from another table.
1868  $table =& self::loadTable($field['tablename']);
1869  return $table->getDefaultValue($fieldname);
1870  }
1871  $delegate =& $this->getDelegate();
1872  if ( isset($delegate) and method_exists($delegate, $fieldname.'__default') ){
1873  return call_user_func(array(&$delegate, $fieldname.'__default'));
1874  } else if ( $field['Default'] ){
1875  return $field['Default'];
1876  } else {
1877  return null;
1878  }
1879  }
1880 
1881 
1882 
1888  function &getFieldsConfig(){
1889  return $this->_fieldsConfig;
1890  }
1891 
1892 
1898  function _newSchema($type, $fieldname, $tablename=null, $permissions = null){
1899  /*
1900  Example row as follows:
1901  Array
1902  (
1903  [Field] => id
1904  [Type] => int(7)
1905  [Null] =>
1906  [Key] => PRI
1907  [Default] =>
1908  [Extra] => auto_increment
1909  )
1910  */
1911  if ( !isset($tablename) ) $tablename = $this->tablename;
1912  if ( !isset($permissions) and is_a($this, 'Dataface_Table') ){
1913  $permissions = Dataface_PermissionsTool::getRolePermissions($this->app->_conf['default_field_role']);
1914  } else if ( !isset($permissions) ){
1915  $permissions = Dataface_PermissionsTool::READ_ONLY();
1916  }
1917 
1918  $schema = array("Field"=>$fieldname, "Type"=>$type, "Null"=>'', "Key"=>'', "Default"=>'', "Extra"=>'');
1919  $schema = array_merge_recursive_unique($this->_global_field_properties, $schema);
1920  $widget = array();
1921  $widget['label'] = ucfirst($schema['Field']);
1922  $widget['description'] = '';
1923  $widget['label_i18n'] = $tablename.'.'.$fieldname.'.label';
1924  $widget['description_i18n'] = $tablename.'.'.$fieldname.'.description';
1925  $widget['macro'] = '';
1926  $widget['helper_css'] = '';
1927  $widget['helper_js'] = '';
1928  $widget['class'] = '';
1929  $widget['type'] = 'text';
1930  $widget['atts'] = array(); //html attributes
1931  if ( preg_match( '/text/', $schema['Type']) ){
1932  $widget['type'] = 'textarea';
1933  } else if ( preg_match( '/blob/', $schema['Type']) ){
1934  $widget['type'] = 'file';
1935  }
1936  $schema['widget'] =& $widget;
1937  $schema['tab'] = '__main__';
1938 
1939  $schema['tablename'] = $tablename;
1940  $schema['tableta'] = 'default';
1941  $schema['vocabulary'] = '';
1942  $schema['enforceVocabulary'] = false;
1943  $schema['validators'] = array();
1944  $schema['name'] = $schema['Field'];
1945  $schema['permissions'] = $permissions;
1946  $schema['repeat'] = false;
1947  $schema['visibility'] = array('list'=>'visible', 'browse'=>'visible', 'find'=>'visible');
1948  $schema = array_merge_recursive_unique($schema, $this->_global_field_properties);
1949 
1950  return $schema;
1951  }
1952 
1953 
1954 
1989  function titleColumn(){
1990  if (!isset( $this->_atts['title'] ) ){
1991  $delegate =& $this->getDelegate();
1992  if ( $delegate !== null and method_exists($delegate, 'titleColumn') ){
1993  $this->_atts['title'] = $delegate->titleColumn();
1994  } else {
1995  $bestCandidate = null;
1996  $this->fields();
1997  $fieldnames = array_keys($this->_fields);
1998  foreach ($fieldnames as $fieldname){
1999  $field =& $this->_fields[$fieldname];
2000  if ( $bestCandidate === null and $this->isChar($fieldname) ){
2001  $bestCandidate = '`'.$fieldname.'`';
2002  }
2003  //if ( strpos(strtolower($fieldname),'title') !== false ){
2004  // $bestCandidate = $fieldname;
2005  //}
2006  }
2007  if ( $bestCandidate === null ){
2008  $keynames = array_keys($this->keys());
2009  $bestCandidate = "CONCAT(".implode(",", $keynames).")";
2010  }
2011  $this->_atts['title'] = $bestCandidate;
2012  }
2013  }
2014  return $this->_atts['title'];
2015 
2016  }
2017 
2018 
2019 
2030  function &getField($fieldname){
2031  $path = explode('.', $fieldname);
2032  if ( count($path)==1){
2033  if ( !isset( $this->_fields[$fieldname]) ){
2034  $delegate =& $this->getDelegate();
2035 
2036  if ( $delegate !== null and method_exists($delegate, "field__$fieldname")){
2037  if ( isset($this->_atts[$fieldname]) ){
2038  $schema = array_merge_recursive_unique($this->_newSchema('calculated',$fieldname), $this->_atts[$fieldname]);
2039  } else {
2040  $schema = $this->_newSchema('calculated', $fieldname);
2041  }
2042  return $schema;
2043  }
2044  $grafted =& $this->graftedFields();
2045  if ( isset($grafted[$fieldname]) ) return $grafted[$fieldname];
2046 
2047  $transient =& $this->transientFields();
2048  if ( isset($transient[$fieldname]) ) return $transient[$fieldname];
2049 
2050  $parent =& $this->getParent();
2051  if ( isset($parent) and ( $field =& $parent->getField($fieldname) ) ){
2052  if ( !PEAR::isError($field) ) return $field;
2053  }
2054 
2055  $err = PEAR::raiseError(SCHEMA_NO_SUCH_FIELD_ERROR,null,null,null, "Field $fieldname does not exist in table ".$this->tablename);
2056 
2057  return $err;
2058  }
2059  return $this->_fields[$fieldname];
2060  } else {
2061  // this field is from a relationship.
2062 
2063  // first check the cache
2064  if ( !isset( $this->_relatedFields[$path[0]] ) ) $this->_relatedFields[$path[0]] = array();
2065  if ( !isset( $this->_relatedFields[$path[0]][$path[1]] ) ) {
2066 
2067  $relationship =& $this->getRelationship($path[0]);
2068 
2069  if ( PEAR::isError($relationship) ){
2070  $err = PEAR::raiseError(SCHEMA_NO_SUCH_FIELD_ERROR,null,null,null, "Field $fieldname does not exist in table ".$this->tablename);
2071  return $err;
2072  }
2073 
2074  $this->_relatedFields[$path[0]][$path[1]] =& $relationship->getField($path[1]); //Dataface_Table::getTableField($absolute_name);
2075  }
2076 
2077  return $this->_relatedFields[$path[0]][$path[1]];
2078 
2079  }
2080 
2081  }
2082 
2103  function getFieldProperty($propertyName, $fieldname, $params=array()){
2104  $field =& $this->getField($fieldname);
2105 
2106  if ( $field['tablename'] != $this->tablename ){
2107  $table =& self::loadTable($field['tablename']);
2108  return $table->getFieldProperty($propertyName, $fieldname, $params);
2109  }
2110 
2112  if ( $this->tablename !== $table->tablename ){
2113  // THis is a related field so we will have to check the delegate
2114  // class for that table.
2115  list($tablename, $fieldname) = explode('.', $fieldname);
2116  return $table->getFieldProperty($propertyName,$fieldname, $params);
2117  }
2118 
2119 
2120  // First we will see if the delegate class defines as custom description.
2121  $delegate =& $this->getDelegate();
2122  $delegate_property_name = str_replace(':', '_', $propertyName);
2123  if ( method_exists($delegate, $fieldname.'__'.$delegate_property_name) ){
2124 
2125  if ( !isset( $params['record'] ) ) $params['record'] = null;
2126  $methodname = $fieldname.'__'.$delegate_property_name;
2127  $res =& $delegate->$methodname($params['record'], $params);
2128  //$res =& call_user_func(array(&$delegate, $fieldname.'__'.$delegate_property_name), $params['record'], $params);
2129 
2130  if ( !PEAR::isError($res) || $res->getCode() !== DATAFACE_E_REQUEST_NOT_HANDLED ){
2131  return $res;
2132  }
2133  }
2134  // The delegate class doesn't define a custom description
2135  // we will just pull the property from the schema
2136 
2137  $path = explode(':', $propertyName);
2138  $arr =& $field;
2139  while ( count($path)> 0 ){
2140  $temp =& $arr[array_shift($path)];
2141  unset($arr);
2142  $arr =& $temp;
2143  unset($temp);
2144  }
2145  return $arr;
2146  }
2147 
2148 
2149 
2150 
2158  foreach (array_keys($this->keys()) as $field){
2159  if (strtolower($this->_fields[$field]['Extra']) == 'auto_increment'){
2160  return $field;
2161  }
2162  }
2163  return null;
2164  }
2165 
2166  private static $globalFieldsConfig = null;
2167  public static function &getGlobalFieldsConfig(){
2168  if ( !isset(self::$globalFieldsConfig) ){
2169  //self::$globalFieldsConfig = array();
2170  import( 'Dataface/ConfigTool.php');
2171  $configTool =& Dataface_ConfigTool::getInstance();
2172  self::$globalFieldsConfig =& $configTool->loadConfig('fields', null);
2173 
2174  }
2175  //print_r(self::$globalFieldsConfig);
2176  return self::$globalFieldsConfig;
2177 
2178  }
2179 
2180 
2185  function _loadFieldsIniFile(){
2186 
2187 
2188  import( 'Dataface/ConfigTool.php');
2189  $configTool =& Dataface_ConfigTool::getInstance();
2190  $conf =& $configTool->loadConfig('fields', $this->tablename); //$temp['root'];
2191  $gConf =& self::getGlobalFieldsConfig();
2192  $conf = array_merge($gConf, $conf);
2194  $appDel =& $app->getDelegate();
2195  if ( method_exists($appDel,'decorateFieldsINI') ){
2196  $appDel->decorateFieldsINI($conf, $this);
2197  }
2198 
2199  $this->_global_field_properties = array();
2200  if ( isset($conf['__global__']) ) $this->_parseINISection($conf['__global__'], $this->_global_field_properties);
2201  else $this->_global_field_properties = array();
2202  //print_r($this->_fields);
2203  foreach ($this->_fields as $key=>$val){
2204  if ( isset($conf[$key]) ){
2205  $conf[$key] = array_merge_recursive_unique($this->_global_field_properties, $conf[$key]);
2206  } else {
2207  $conf[$key] = $this->_global_field_properties;
2208  }
2209  //$conf[$key] = array_merge_recursive_unique($this->_global_field_properties, $conf[$key]);
2210  }
2211  foreach ($conf as $key=>$value ){
2212  if ( $key == '__sql__' and !is_array($value) ){
2213  $this->_sql = $value;
2214  continue;
2215  }
2216 
2217  if ( is_array($value) and @$value['decorator'] ){
2218  $event = new StdClass;
2219  $event->key = $key;
2220  $event->conf =& $value;
2221  $event->table = $this;
2222 
2223  $app->fireEvent($value['decorator'].'__decorateConf', $event);
2224 
2225  }
2226 
2227  if ( is_array($value) ){
2228 
2229 
2230  if ( isset($value['extends']) and isset($conf[$value['extends']]) ){
2231  $conf[$key] = array_merge($conf[$value['extends']], $value);
2232  $value = $conf[$key];
2233  }
2234 
2235  if ( isset($value['Type']) ){
2236 
2237  $ftype = strtolower(preg_replace('/\(.*$/','',$value['Type']));
2238  //echo $ftype;
2239  if ( isset($conf['/'.$ftype]) ){
2240  $conf[$key] = $value = array_merge($conf['/'.$ftype], $value);
2241  //print_r($value);
2242  }
2243 
2244  }
2245  }
2246 
2247  /*
2248  * Iterate through all of the fields.
2249  */
2250  $matches = array(); // temp holder for preg matches
2251  if ( preg_match('/fieldgroup:(.+)/', $key, $matches) ){
2252  // This is a group description - not a field description
2253  $this->_fieldgroups[trim($matches[1])] = $value;
2254  $this->_fieldgroups[trim($matches[1])]['name'] = $matches[1];
2255 
2256  $grp =& $this->_fieldgroups[trim($matches[1])];
2257  foreach ($grp as $grpkey=>$grpval){
2258  $tmp = explode(':',$grpkey);
2259  switch (count($tmp)){
2260  case 2: $grp[$tmp[0]][$tmp[1]] =& $grp[$grpkey]; break;
2261  case 3: $grp[$tmp[0]][$tmp[1]][$tmp[2]] =& $grp[$grpkey]; break;
2262  case 4: $grp[$tmp[0]][$tmp[1]][$tmp[2]][$tmp[3]] =& $grp[$grpkey]; break;
2263  case 5: $grp[$tmp[0]][$tmp[1]][$tmp[2]][$tmp[3]][$tmp[4]] =& $grp[$grpkey]; break;
2264 
2265  }
2266  }
2267 
2268  if ( !isset( $grp['label'] ) ) $grp['label'] = ucfirst($grp['name']);
2269  if ( !isset( $grp['description']) ) $grp['description'] = '';
2270  if ( !isset( $grp['display']) ) $grp['display'] = "inline";
2271  if ( !isset( $grp['element_label_visible'])) {
2272  $grp['element_label_visible'] = true;
2273  }
2274  if ( !isset($grp['element_description_visible'])){
2275  $grp['element_description_visible'] =
2276  ($grp['display'] == 'inline' ? false : true);
2277 
2278  }
2279 
2280  // Now do the translation stuff
2281  $grp['label'] = df_translate('tables.'.$this->tablename.'.fieldgroups.'.$grp['name'].'.label', $grp['label']);
2282  $grp['description'] = df_translate('tables.'.$this->tablename.'.fieldgroups.'.$grp['name'].'.description', $grp['description']);
2283  if ( !isset($grp['order']) ) $grp['order'] = 0;
2284  unset($grp);
2285  }
2286 
2287  else if ( preg_match('/tab:(.+)/', $key, $matches) ){
2288  // This is a tab description
2289  $tabname = trim($matches[1]);
2290  $this->_parseINISection($value, $this->_tabs[$tabname]);
2291  $tabarr =& $this->_tabs[$tabname];
2292  $tabarr['name'] = $tabname;
2293 
2294  if ( !isset($tabarr['label']) ) $tabarr['label'] = ucfirst($tabname);
2295  if ( !isset($tabarr['description']) ) $tabarr['description'] = '';
2296 
2297  unset($tabarr);
2298 
2299 
2300  }
2301 
2302  else if ( $key == "__filters__"){
2303  // THis is a filter to be added to queries of this table.
2304  $this->_filters=$value;
2305 
2306  }
2307 
2308  else if ($key == "__title__"){
2309  $this->_atts['title'] = $value;
2310  }
2311 
2312  else if ( $key == '__join__' ){
2313  $this->_joinTables = $value;
2314 
2315  }
2316 
2317  else if ( strpos($key, ':') > 0 ){
2318  // This is the definition of a subfield (ie: a field within a
2319  // table.
2320  list($parent, $child) = explode(':', $key);
2321  if ( !isset( $this->_fields[$parent] ) ){
2322  throw new Exception("Error while loading definition for subfield '$key' from the fields.ini file for the table '".$this->tablename."'. The field '$parent' does not exist.", E_USER_ERROR);
2323  }
2324 
2325  $field =& $this->_fields[$parent];
2326  if ( !isset($field['fields']) ) $field['fields'] = array();
2327  $curr = $this->_newSchema('varchar(255)', $child);
2328 
2329  $this->_parseINISection($value, $curr);
2330  $field['fields'][$child] =& $curr;
2331  unset($curr);
2332  unset($field);
2333 
2334 
2335  }
2336 
2337  else if ( isset( $this->_fields[ $key ] ) ){
2338 
2339  $field =& $this->_fields[$key];
2340  $widget =& $field['widget'];
2341  $permissions =& $field['permissions'];
2342  $validators =& $field['validators'];
2343  $ftype = $field['Type'];
2344  if ( isset($value['Type']) ) $ftype = $value['Type'];
2345  $ftype = strtolower(preg_replace('/\(.*$/','',$ftype));
2346  //echo $ftype;
2347  if ( isset($conf['/'.$ftype]) ){
2348  $conf[$key] = $value = array_merge($conf['/'.$ftype], $value);
2349  //print_r($value);
2350  }
2351 
2352 
2353  // get the attributes defined in the ini file
2354  foreach ( $value as $att => $attval ){
2355  // some of the attributes will be prefixed to indicate
2356  // widget, vocabulary, etc...
2357  $attpath = explode( ":", $att );
2358 
2359  if ( count( $attpath ) == 1 ){
2360  // there was no prefix ... attribute goes straight into table
2361  if ( is_array($attval) ){
2362  $field[ $att ] = $attval;
2363  } else {
2364  $field[ $att ] = trim( $attval );
2365  if ( strcasecmp($att, 'key') === 0 and strcasecmp($attval, 'pri')===0 and !isset($this->_keys[$field['name']])){
2366  // This field is a surrogate primary key
2367  $this->_keys[$field['name']] =& $field;
2368 
2369  }
2370  }
2371  // todo handle validators which can be in the form of a list
2372  // ** ** **
2373  } else {
2374  // There was a prefix
2375 
2376  switch ( $attpath[0] ){
2377  case "widget":
2378  if ( count($attpath) > 2 ){
2379  $widget[ $attpath[1] ][ $attpath[2] ] = trim($attval);
2380  } else {
2381  $widget[ $attpath[1] ] = trim($attval);
2382  }
2383 
2384  break;
2385  case "permissions":
2386  $permissions[ $attpath[1] ] = trim($attval);
2387  break;
2388  case "validators":
2389  if ( !$attval || $attval == 'false' ) {
2390  $validators[ $attpath[1] ] = 0;
2391 
2392  continue;
2393  }
2394  if ( !isset( $validators[ $attpath[1] ] ) ){
2395  $validators[ $attpath[1] ] = array();
2396  }
2397  if ( count( $attpath ) <= 2 ){
2398  $validators[ $attpath[1] ]['arg'] = trim($attval);
2399  } else {
2400  $validators[ $attpath[1] ][ $attpath[2] ] = trim($attval);
2401  }
2402  if ( !isset( $validators[ $attpath[1] ]['message'] )){
2403  $validators[ $attpath[1] ]['message'] = "Illegal input for field ".$field['name'];
2404  }
2405  break;
2406 
2407  case "visibility":
2408  if ( !isset($field['visibility'] ) ) $field['visibility'] = array('list'=>'visible',
2409  'browse'=>'visible',
2410  'find'=>'visible');
2411  $field['visibility'][ $attpath[1] ] = trim($attval);
2412 
2413  break;
2414 
2415  default:
2416  $field[$attpath[0]][$attpath[1]] = trim($attval);
2417  break;
2418 
2419  }
2420 
2421 
2422  }
2423 
2424 
2425  }
2426 
2427  // prevent ourselves from doing damage
2428  unset($field);
2429  unset($widget);
2430  unset($permissions);
2431  unset($validators);
2432  } else {
2433  $this->_atts[$key] = $value;
2434  }
2435  }
2436 
2437  // Create missing fieldgroups
2438  // Explanation: It is possible that fields reference
2439  // groups that don't have an explicit definition in the ini files.
2440  // This step creates those implicit groups.
2441  foreach (array_keys($this->_fields) as $key){
2442  if ( isset($this->_fields[$key]['group']) ){
2443  $grpname = $this->_fields[$key]['group'];
2444  if ( !isset( $this->_fieldgroups[$grpname] ) ){
2445  $this->_fieldgroups[$grpname] = array(
2446  "name"=>$grpname,
2447  "label"=>df_translate('tables.'.$this->tablename.'.fieldgroups.'.$grpname.'.label', ucfirst($grpname)),
2448  "description"=>'',
2449  "display"=>'inline',
2450  "element_label_visible"=>true,
2451  "element_description_visible"=>false,
2452  'order'=>0
2453  );
2454  }
2455  }
2456  if ( strcasecmp($this->_fields[$key]['Type'], 'container') === 0){
2457  /*
2458  * This field is a Container field. We will need to set up the save path.
2459  * If no save path is specified we will create a directory by the name
2460  * of this field inside the table's directory.
2461  */
2462  if ( $this->_fields[$key]['widget']['type'] == 'text' ) $this->_fields[$key]['widget']['type'] = 'file';
2463  if ( !isset( $this->_fields[$key]['savepath'] ) ){
2464  $this->_fields[$key]['savepath'] = $this->basePath().'/tables/'.$this->tablename.'/'.$key;
2465  } else if ( strpos($this->_fields[$key]['savepath'], '/') !== 0 and !preg_match('/^[a-z0-9]{1,5}:\/\//', $this->_fields[$key]['savepath']) ) {
2466  $this->_fields[$key]['savepath'] = DATAFACE_SITE_PATH.'/'.$this->_fields[$key]['savepath'];
2467  }
2468 
2469  if ( !isset($this->_fields[$key]['url']) ){
2470  $this->_fields[$key]['url'] = str_replace(DATAFACE_SITE_PATH, DATAFACE_SITE_URL, $this->_fields[$key]['savepath']);
2471 
2472  } else if ( strpos( $this->_fields[$key]['url'], '/') !== 0 and strpos($this->_fields[$key]['url'], 'http://') !== 0 ){
2473  $this->_fields[$key]['url'] = DATAFACE_SITE_URL.'/'.$this->_fields[$key]['url'];
2474  }
2475  }
2476 
2477 
2478  if ( !isset($this->_fields[$key]['tab']) ) $this->_fields[$key]['tab'] = '__main__';
2479  $tab = $this->_fields[$key]['tab'];
2480  if ( !isset($this->_tabs[$tab]) ){
2481 
2482  $this->_tabs[$tab] = array('name'=>$tab, 'label'=>ucfirst(str_replace('_',' ',$tab)), 'description'=>'');
2483 
2484  }
2485 
2486 
2487  if ( $this->_fields[$key]['widget']['type'] == 'checkbox' and ($this->isText($key) || $this->isChar($key)) and @$this->_fields[$key]['vocabulary'] ){
2488  // This is a checkbox field with a vocabulary. It should be a repeating field
2489  $this->_fields[$key]['repeat'] = true;
2490  }
2491 
2492  $widget =& $this->_fields[$key]['widget'];
2493  switch ($widget['type']){
2494  case 'text':
2495  if ( !isset($widget['atts']['size']) ){
2496  if ( $this->isInt($key) or $this->isFloat($key) ){
2497  $widget['atts']['size'] = 10;
2498  } else {
2499  $widget['atts']['size'] = 30;
2500  }
2501  }
2502  break;
2503  case 'textarea':
2504  if (!isset($widget['atts']['rows']) ){
2505  $widget['atts']['rows'] = 6;
2506  }
2507  if ( !isset($widget['atts']['cols']) ){
2508  $widget['atts']['cols'] = 60;
2509  }
2510  break;
2511  }
2512 
2513  // Now we do the translation stuff
2514  $widget['label'] = df_translate('tables.'.$this->tablename.'.fields.'.$key.'.widget.label',$widget['label']);
2515  $widget['description'] = df_translate('tables.'.$this->tablename.'.fields.'.$key.'.widget.description',$widget['description']);
2516  if ( isset($widget['question']) ){
2517  $widget['question'] = df_translate('tables.'.$this->tablename.'.fields.'.$key.'.widget.question',$widget['question']);
2518 
2519  }
2520  //$this->_fields[ $key ] =
2521 
2522  unset($widget);
2523  }
2524 
2525  }
2526 
2527 
2528 
2529 
2530 
2531 
2532 
2533 
2534  // @}
2535  // END Fields methods
2536  //----------------------------------------------------------------------------------------------
2537 
2538  //----------------------------------------------------------------------------------------------
2539  // @{
2552  function readOnly(){
2553  return false;
2554  }
2555 
2556 
2584  function setSecurityFilter($filter=null){
2585 
2586  if ( !isset($filter)){
2587  $filter = array();
2589  $query =& $app->getQuery();
2590  if ( class_exists('Dataface_AuthenticationTool') ){
2592  $user =& $auth->getLoggedInUser();
2593  } else {
2594  $auth = null;
2595  $user = null;
2596  }
2597 
2598  foreach ($this->_filters as $key=>$value){
2599  if ( isset($this->_securityFilter[$key]) ) continue;
2600  if ( $value{0} == '$' ){
2601  if ( !$user and strpos($value, '$user') !== false ) continue;
2602  eval('$filter[$key] = "=".'.$value.';');
2603  } else if ( substr($value,0,4) == 'php:' ){
2604  if ( !$user and strpos($value, '$user') !== false ) continue;
2605  eval('$filter[$key] = "=".'.substr($value,4).';');
2606  } else {
2607  $filter[$key] = "=".$value;
2608  }
2609  }
2610 
2611  }
2612 
2613  $this->_securityFilter = $filter;
2614  }
2615 
2623  function getSecurityFilter($filter=array()){
2624  return array_merge($this->_securityFilter, $filter);
2625  }
2626 
2627 
2628 
2658  function getRelationshipPermissions($relationshipName, $params=array()){
2659 
2660  $params['relationship'] = $relationshipName;
2661  return $this->getPermissions($params);
2662  }
2663 
2687  function getPermissions($params=array()){
2688 
2689  // First let's try to load permissions from the cache
2691  $params['table'] = $this->tablename;
2692  if ( isset($params['record']) ) $record =& $params['record'];
2693  else $record = null;
2694  $cachedPermissions = $pt->getCachedPermissions($record, $params);
2695  if ( isset($cachedPermissions) ) return $cachedPermissions;
2696 
2697 
2698  $delegate =& $this->getDelegate();
2700  $appDelegate =& $app->getDelegate();
2701  $parent =& $this->getParent();
2702  $recordmask = @$params['recordmask'];
2703  $methods = array();
2704  if ( isset($params['relationship']) ){
2705  if ( isset($params['relationshipmask']) ) $rmask =& $params['relationshipmask'];
2706  else $rmask = array();
2707 
2708  if ( isset($params['field']) ){
2709  $relprefix = 'rel_'.$params['relationship'].'__';
2710  $methods[] = array('object'=>&$delegate, 'name'=>$relprefix.$params['field'].'__permissions', 'type'=>'permissions', 'partial'=>1);
2711  $methods[] = array('object'=>&$delegate, 'name'=>$relprefix.$params['field'].'__roles', 'type'=>'roles', 'partial'=>1);
2712  $methods[] = array('object'=>&$delegate, 'name'=>$relprefix.'__field__permissions', 'type'=>'permissions', 'partial'=>1);
2713  $methods[] = array('object'=>&$delegate, 'name'=>$relprefix.'__field__roles', 'type'=>'roles', 'partial'=>1);
2714 
2715  //if ( @$params['recordmask'] ) $methods[] = 'recordmask';
2716  if ( @$params['nobubble'] ) $methods[] = 'break';
2717 
2718  }
2719 
2720 
2721  $methods[] = array('object'=>&$delegate, 'name'=>'rel_'.$params['relationship'].'__permissions', 'type'=>'permissions', 'mask'=>&$rmask, 'partial'=>1);
2722  $methods[] = array('object'=>&$delegate, 'name'=>'rel_'.$params['relationship'].'__roles', 'type'=>'roles', 'mask'=>&$rmask, 'partial'=>1);
2723  if ( isset($parent) ) $methods[] = array('object'=>&$parent, 'name'=>'getPermissions', 'type'=>'Dataface_Table', 'partial'=>1);
2724  if ( @$params['nobubble'] ) $methods[] = 'break';
2725  }
2726  else if ( isset($params['field']) ){
2727  if ( isset($record) and is_a($record, 'Dataface_Record') ){
2728  $methods[] = array('object'=>$pt, 'name'=>'getPortalFieldPermissions', 'type'=>'permissions', 'partial'=>1);
2729  }
2730  $methods[] = array('object'=>&$delegate, 'name'=>$params['field'].'__permissions', 'type'=>'permissions', 'partial'=>1);
2731  $methods[] = array('object'=>&$delegate, 'name'=>$params['field'].'__roles', 'type'=>'roles', 'partial'=>1);
2732  $methods[] = array('object'=>&$delegate, 'name'=>'__field__permissions', 'type'=>'permissions', 'partial'=>1);
2733  $methods[] = array('object'=>&$delegate, 'name'=>'__field__roles', 'type'=>'roles', 'partial'=>1);
2734  if ( isset($parent) ) $methods[] = array('object'=>&$parent, 'name'=>'getPermissions', 'type'=>'Dataface_Table', 'partial'=>1);
2735  if ( @$params['recordmask'] ) $methods[] = 'recordmask';
2736  if ( @$params['nobubble'] ) $methods[] = 'break';
2737 
2738 
2739  }
2740  //if ( isset($params['recordmask']) ) $mask =& $params['recordmask'];
2741  //else $mask = array();
2742  if ( isset($record) and is_a($record, 'Dataface_Record') ){
2743  $methods[] = array('object'=>$pt, 'name'=>'getPortalRecordPermissions', 'type'=>'permissions', 'partial'=>1);
2744  }
2745  $methods[] = array('object'=>&$delegate, 'name'=>'getPermissions', 'type'=>'permissions');
2746  $methods[] = array('object'=>&$delegate, 'name'=>'getRoles', 'type'=>'roles');
2747  $methods[] = array('object'=>&$appDelegate, 'name'=>'getPermissions', 'type'=>'permissions');
2748  $methods[] = array('object'=>&$appDelegate, 'name'=>'getRoles', 'type'=>'roles');
2749  if ( isset($parent) ) $methods[] = array('object'=>&$parent, 'name'=>'getPermissions', 'type'=>'Dataface_Table');
2750 
2751  $perms = array();
2752  foreach ($methods as $method){
2753  if ( $method == 'break' ) {
2754  if ( !$perms ) return null;
2755  else return $perms;
2756  }
2757  if ( $method == 'recordmask' and is_array($recordmask) ){
2758  // If a record mask has been supplied we apply that here
2759  // so that unspecified permissions in a field request will
2760  // be augmented with a specified recordmask
2761  $perms = array_merge($recordmask, $perms);
2762  continue;
2763  }
2764  if ( isset($method['object']) and method_exists($method['object'], $method['name']) ){
2765  $name = $method['name'];
2766  if ( $method['type'] == 'Dataface_Table'){
2767  $res = $method['object']->$name(array_merge($params, array('nobubble'=>1)));
2768  } else {
2769  $res = $method['object']->$name($record, $params);
2770  }
2771  if ( $method['type'] == 'roles' ){
2772  $res = $this->convertRolesToPermissions($res);
2773  }
2774  if ( is_array($res) ){
2775  if ( @$method['mask'] and is_array(@$method['mask']) ) $res = array_merge($method['mask'], $res);
2776 
2777  $perms = array_merge($res, $perms);
2778  if ( !@$method['partial'] and !@$res['__partial__']){
2779  $pt->filterPermissions($record, $perms, $params);
2780  $pt->cachePermissions($record, $params, $perms);
2781  return $perms;
2782  }
2783  }
2784  }
2785  }
2786  $res = array_merge(Dataface_PermissionsTool::ALL(), $perms);
2787  $pt->filterPermissions($record, $res, $params);
2788  $pt->cachePermissions($record,$params,$res);
2789  return $res;
2790  }
2791 
2792 
2802  function convertRolesToPermissions($roles){
2803  if ( is_array($roles) ){
2804  $perms = array();
2805  foreach ($roles as $role){
2806  if ( is_string($role) ){
2807  $perms = array_merge($perms, Dataface_PermissionsTool::getRolePermissions($role));
2808  }
2809  }
2810  return $perms;
2811  } else if ( is_string($roles) ){
2813  }
2814 
2815  return $roles;
2816  }
2817 
2818 
2824  function loadPermissions(){
2825  $this->_permissionsLoaded = true;
2826  $configTool =& Dataface_ConfigTool::getInstance();
2827  $conf =& $configTool->loadConfig('permissions', $this->tablename);
2828  $permissionsTool =& Dataface_PermissionsTool::getInstance();
2829  $permissionsTool->addPermissions($conf);
2830 
2831  }
2832 
2833  // @}
2834  // END Permissions
2835  //----------------------------------------------------------------------------------------------
2836 
2837  //----------------------------------------------------------------------------------------------
2838  // @{
2853  function &getTranslations(){
2854  if ( $this->translations === null ){
2855  $this->translations = array();
2856  $res = mysql_query("SHOW TABLES LIKE '".addslashes($this->tablename)."%'", $this->db);
2857  if ( !$res ){
2858 
2859  throw new Exception(
2861  'MySQL query error loading translation tables',
2862  'MySQL query error while trying to find translation tables for table "'.addslashes($this->tablename).'". '.mysql_error($this->db).'. ',
2863  array('sql_error'=>mysql_error($this->db), 'stack_trace'=>'', 'table'=>$this->tablename)
2864  ),
2865  E_USER_ERROR
2866 
2867  );
2868  }
2869  if (mysql_num_rows($res) <= 0 ){
2870  // there should at least be the current table returned.. there is a problem
2871  // if nothing was returned.
2872  throw new Exception(
2874  'Not enough results returned loading translation tables',
2875  'No tables were returned when trying to load translation tables for table "'.$this->tablename.'". This query should have at least returned one record (the current table) so there must be a problem with the query.',
2876  array('table'=>$this->tablename)
2877  ),
2878  E_USER_ERROR
2879  );
2880  }
2881 
2882  while ( $row = mysql_fetch_array($res ) ){
2883  $tablename = $row[0];
2884  if ( $tablename == $this->tablename ){
2885  continue;
2886  }
2887 
2888  $matches = array();
2889  if ( preg_match( '/^'.$this->tablename.'_([a-zA-Z]{2})$/', $tablename, $matches) ){
2890  $this->translations[$matches[1]] = 0;
2891  }
2892 
2893  }
2894  mysql_free_result($res);
2895 
2896 
2897  }
2898  return $this->translations;
2899 
2900  }
2901 
2908  function &getTranslation($name){
2909  $translations =& $this->getTranslations();
2910  if ( isset( $translations[$name] )){
2911  // the translation exists
2912  if ( !$translations[$name] ){
2913  // the columns are not loaded yet, we need to load them.
2914  $res = mysql_query("SHOW COLUMNS FROM `".addslashes($this->tablename)."_".addslashes($name)."`", $this->db);
2915  if ( !$res ){
2916  throw new Exception(
2918  'Problem loading columns from translation table',
2919  'Problem loading columns from translation table for table "'.$this->tablename.'" in language "'.$name.'". ',
2920  array('table'=>$this->tablename,'langauge'=>$name,'stack_trace'=>'','sql_error'=>mysql_error($this->db))
2921  ),
2922  E_USER_ERROR
2923  );
2924  }
2925  $translations[$name] = array();
2926  while ( $row = mysql_fetch_assoc($res) ){
2927  $translations[$name][] = $row['Field'];
2928  }
2929  mysql_free_result($res);
2930  }
2931 
2932  return $translations[$name];
2933  }
2934  $null = null;
2935  return $null;
2936  }
2937 
2944  return array(
2945  'o-a-c'=>'Original translation',
2946  'm-u-c'=>'Machine translation - unapproved',
2947  'm-a-c'=>'Machine translation - approved',
2948  'm-u-o'=>'Machine translation - unapproved - out of date',
2949  'm-a-o'=>'Machine translation - approved - out of date',
2950  'h-u-c'=>'Human translation - unapproved',
2951  'h-a-c'=>'Human translation - approved',
2952  'h-u-o'=>'Human translation - unapproved - out of date',
2953  'h-a-o'=>'Human translation - approved - out of date'
2954  );
2955 
2956  }
2957 
2958 
2959  // @}
2960  // END Internationalization
2961  //----------------------------------------------------------------------------------------------
2962 
2963  //----------------------------------------------------------------------------------------------
2964  // @{
2975  function getLabel(){
2976  return (@$this->_atts['label'] ? $this->_atts['label'] : $this->tablename);
2977  }
2978 
2979 
2991  function getSingularLabel(){
2992  if ( !isset($this->_atts['singular_label']) ){
2993  $this->_atts['singular_label'] = df_singularize($this->getLabel());
2994  }
2995  return $this->_atts['singular_label'];
2996 
2997  }
2998 
3003  function tableInfo(){
3004 
3005  $info = array();
3006  foreach ( array_keys($this->fields()) as $fieldname){
3007  $field =& $this->getField($fieldname);
3008  $info['fields'][$fieldname]['Type'] = $field['Type'];
3009  $info['fields'][$fieldname]['Extra'] = $field['Extra'];
3010  $info['fields'][$fieldname]['Key'] = $field['Key'];
3011  $info['fields'][$fieldname]['widget'] = $field['widget'];
3012  unset($field);
3013  }
3014  return $info;
3015  }
3016 
3020  function databaseInfo(){
3021  $tables =& self::loadTable('',null,true);
3022  $info = array();
3023  foreach ( array_keys($tables) as $tablename ){
3024  $info[$tablename] =& $tables[$tablename]->tableInfo();
3025  }
3026  return $info;
3027  }
3028 
3033  function &attributes(){
3034  if ( !isset($this->_cache[__FUNCTION__]) ){
3035  $atts =& $this->_atts;
3036  $parent =& $this->getParent();
3037  if ( isset($parent) ){
3038  $atts = array_merge($parent->attributes(), $atts);
3039  }
3040  $this->_cache[__FUNCTION__] =& $atts;
3041  }
3042  return $this->_cache[__FUNCTION__];
3043  }
3044 
3045 
3046 
3054  function &getParent(){
3055  if ( !isset($this->_parentTable) ){
3056  if ( isset($this->_atts['__isa__']) ){
3057  $this->_parentTable =& self::loadTable($this->_atts['__isa__']);
3058  }
3059  }
3060  return $this->_parentTable;
3061  }
3062 
3075  function implementsOntology($ontologyName){
3076  return (isset($this->_atts['__implements__']) and isset($this->_atts['__implements__'][$ontologyName]));
3077  }
3078 
3079 
3080 
3081 
3082 
3087  function &getStatus(){
3088  if ( !isset( $this->status ) ){
3089  /*
3090  * Get the table status - when was it last updated, etc...
3091  */
3092  if ( Dataface_Application::getInstance()->getMySQLMajorVersion() < 5 ){
3093  $res = mysql_query("SHOW TABLE STATUS LIKE '".addslashes($this->tablename)."'",$this->db);
3094  } else {
3095  $dbname = Dataface_Application::getInstance()->_conf['_database']['name'];
3096  $res = mysql_query("select CREATE_TIME as Create_time, UPDATE_TIME as Update_time from information_schema.tables where TABLE_SCHEMA='".addslashes($dbname)."' and TABLE_NAME='".addslashes($this->tablename)."' limit 1", df_db());
3097 
3098  }
3099  if ( !$res ){
3100  throw new Exception("Error performing mysql query to obtain status for table '".$this->tablename."': ".mysql_error($this->db), E_USER_ERROR);
3101  }
3102 
3103  $this->status = mysql_fetch_array($res);
3104  mysql_free_result($res);
3105  }
3106 
3107  return $this->status;
3108 
3109 
3110  }
3111 
3112 
3135  public static function &getBackupModificationTimes($refresh=false){
3136  static $backup_times = 0;
3137  if ( $backup_times === 0 or $refresh ){
3138  $res = mysql_query("select * from dataface__mtimes", df_db());
3139  if ( !$res ){
3140  import('Dataface/IO.php');
3142  $res = mysql_query("select * from dataface__mtimes", df_db());
3143  if ( !$res ) throw new Exception(mysql_error(df_db()));
3144 
3145  }
3146  $backup_times = array();
3147  while ( $row = mysql_fetch_assoc($res) ){
3148  $backup_times[$row['name']] = $row['mtime'];
3149  }
3150  @mysql_free_result($res);
3151  }
3152  return $backup_times;
3153 
3154 
3155  }
3156 
3157 
3172  public static function &getTableModificationTimes($refresh=false){
3173  static $mod_times = 0;
3174  if ( $mod_times === 0 or $refresh ){
3175  $mod_times = array();
3176 
3178  $dbname = $app->_conf['_database']['name'];
3179  //if ( $app->getMySQLMajorVersion() < 5 ){
3180  // $res = mysql_query("show table status", df_db());
3181  //} else {
3182  // $res = mysql_query("select TABLE_NAME as Name, UPDATE_TIME as Update_time from information_schema.tables where TABLE_SCHEMA='".addslashes($dbname)."'", df_db());
3183  //}
3184  $res = mysql_query("show tables", df_db());
3185  if ( !$res ){
3186  throw new Exception(mysql_error(df_db()));
3187  }
3188  $backup_times = null;
3189  //while ( $row = mysql_fetch_assoc($res) ){
3190  while ($row = mysql_fetch_row($res) ){
3191  $row['Name'] = $row[0];
3192  if ( @$row['Update_time'] ){
3193  $mod_times[$row['Name']] = @strtotime($row['Update_time']);
3194  } else {
3195  if ( !$backup_times ){
3196  $backup_times =& self::getBackupModificationTimes($refresh);
3197  }
3198  if ( isset($backup_times[$row['Name']]) and $backup_times[$row['Name']] ){
3199  $mod_times[$row['Name']] = $backup_times[$row['Name']];
3200  } else {
3201  $mod_times[$row['Name']] = time();
3202  import('Dataface/IO.php');
3203  Dataface_IO::touchTable($row['Name']);
3204 
3205  }
3206  }
3207  }
3208  }
3209  return $mod_times;
3210  }
3211 
3217  function getUpdateTime(){
3218  $status =& $this->getStatus();
3219  return $status['Update_time'];
3220  }
3221 
3228  function getCreateTime(){
3229  $status =& $this->getStatus();
3230  return $status['Create_time'];
3231  }
3232 
3233  // @}
3234  // END Table Info
3235  //----------------------------------------------------------------------------------------------
3236 
3237  //----------------------------------------------------------------------------------------------
3238  // @{
3254  function isYesNoValuelist($valuelist_name, &$yes, &$no){
3255 
3256  $options =& $this->getValuelist($valuelist_name);
3257  if ( !$options ) return false;
3258 
3259  if (count($options) != 2) return false;
3260  $opt_keys = array_keys($options);
3261  $yes_val = false;
3262  $no_val = false;
3263  foreach ($opt_keys as $opt_key){
3264  if ( stristr($opt_key,'n') == $opt_key ) $no_val = $opt_key;
3265  else if ( stristr($opt_key,'y') == $opt_key) $yes_val = $opt_key;
3266  else if ( $opt_key == "0" ) $no_val = $opt_key;
3267  else if ( $opt_key == "1" ) $yes_val = $opt_key;
3268  else if ( in_array(strtolower($opt_key), array('on','active','true','t') ) ){
3269  $yes_val = $opt_key;
3270  } else if ( in_array(strtolower($opt_key), array('off','inactive','false','f') ) ){
3271  $no_val = $opt_key;
3272  }
3273  }
3274 
3275  if ( $yes_val and $no_val ){
3276  $yes = $yes_val;
3277  $no = $no_val;
3278  return true;
3279  }
3280  return false;
3281 
3282  }
3283 
3284 
3289  function _valuelistsIniFilePath(){
3290  return $this->basePath().'/tables/'.$this->tablename.'/valuelists.ini';
3291  }
3292 
3297  function _hasValuelistsIniFile(){
3298  return file_exists( $this->_valuelistsIniFilePath() );
3299  }
3300 
3301 
3307  //private $_valuelistsConfig = null;
3308 
3318  private function &_loadValuelistsIniFile(){
3319  if ( !isset($this->_valuelistsConfig) ){
3320  import( 'Dataface/ConfigTool.php');
3321  $configTool =& Dataface_ConfigTool::getInstance();
3322  $this->_valuelistsConfig =& $configTool->loadConfig('valuelists', $this->tablename);
3323  }
3324  return $this->_valuelistsConfig;
3325  }
3326 
3327 
3341  private function _loadValuelist($name){
3342  if ( !isset($this->_valuelists) ){
3343  $this->_valuelists = array();
3344  }
3345 
3346  if ( !isset($this->_valuelists[$name]) ){
3347  $conf =& $this->_loadValuelistsIniFile();
3348  if ( isset($conf[$name]) ){
3349  $vllist = $conf[$name];
3350  $vlname = $name;
3351  $valuelists =& $this->_valuelists;
3352  foreach ( $vllist as $key=>$value ){
3353  if ( $key == '__import__' ){
3354  // we import the values from another value list. The value of this
3355  // entry should be in the form tablename.valuelistname
3356  list( $ext_table, $ext_valuelist ) = explode('.', $value);
3357  if ( isset( $ext_table ) && isset( $ext_valuelist ) ){
3358  $ext_table =& self::loadTable($ext_table, $this->db);
3359  } else if ( isset( $ext_table ) ){
3360  $ext_valuelist = $ext_table;
3361  $ext_table =& $this;
3362  }
3363 
3364  if ( isset( $ext_table ) ){
3365  $ext_valuelist = $ext_table->getValuelist( $ext_valuelist );
3366  foreach ( $ext_valuelist as $ext_key=>$ext_value ){
3367  $valuelists[$vlname][$ext_key] = $ext_value;
3368  }
3369  }
3370  // clean up temp variables so they don't confuse us
3371  // in the next iteration.
3372  unset($ext_table);
3373  unset($ext_table);
3374  unset($ext_valuelist);
3375  } else if ( $key == '__sql__' ) {
3376  // we perform the sql query specified to produce our valuelist.
3377  // the sql query should return two columns only. If more are
3378  // returned, only the first two will be used. If one is returned
3379  // it will be used as both the key and value.
3380  $res = df_query($value, null, true, true);
3381  if ( is_array($res) ){
3382  //while ($row = mysql_fetch_row($res) ){
3383  foreach ($res as $row){
3384  $valuekey = $row[0];
3385  $valuevalue = count($row)>1 ? $row[1] : $row[0];
3386  $valuelists[$vlname][$valuekey] = $valuevalue;
3387 
3388  if ( count($row)>2 ){
3389  $valuelists[$vlname.'__meta'][$valuekey] = $row[2];
3390  }
3391  }
3392  //mysql_free_result($res);
3393  } else {
3394  throw new Exception("Valuelist query '".$value."' failed. :".mysql_error(df_db()), E_USER_NOTICE);
3395  }
3396 
3397  } else {
3398  $valuelists[$vlname][$key] = $value;
3399  }
3400  }
3401  }
3402  }
3403 
3404  }
3405 
3407  if ( !isset( $this->_valuelists ) ){
3408  $this->_valuelists = array();
3409  }
3410  $valuelists =& $this->_valuelists;
3411 
3412  import( 'Dataface/ConfigTool.php');
3413  $configTool =& Dataface_ConfigTool::getInstance();
3414  $conf =& $configTool->loadConfig('valuelists', $this->tablename);
3415 
3416 
3417  foreach ( $conf as $vlname=>$vllist ){
3418  $valuelists[$vlname] = array();
3419  if ( is_array( $vllist ) ){
3420  foreach ( $vllist as $key=>$value ){
3421  if ( $key == '__import__' ){
3422  // we import the values from another value list. The value of this
3423  // entry should be in the form tablename.valuelistname
3424  list( $ext_table, $ext_valuelist ) = explode('.', $value);
3425  if ( isset( $ext_table ) && isset( $ext_valuelist ) ){
3426  $ext_table =& self::loadTable($ext_table, $this->db);
3427  } else if ( isset( $ext_table ) ){
3428  $ext_valuelist = $ext_table;
3429  $ext_table =& $this;
3430  }
3431 
3432  if ( isset( $ext_table ) ){
3433  $ext_valuelist = $ext_table->getValuelist( $ext_valuelist );
3434  foreach ( $ext_valuelist as $ext_key=>$ext_value ){
3435  $valuelists[$vlname][$ext_key] = $ext_value;
3436  }
3437  }
3438  // clean up temp variables so they don't confuse us
3439  // in the next iteration.
3440  unset($ext_table);
3441  unset($ext_table);
3442  unset($ext_valuelist);
3443  } else if ( $key == '__sql__' ) {
3444  // we perform the sql query specified to produce our valuelist.
3445  // the sql query should return two columns only. If more are
3446  // returned, only the first two will be used. If one is returned
3447  // it will be used as both the key and value.
3448  $res = df_query($value, null, true, true);
3449  if ( is_array($res) ){
3450  //while ($row = mysql_fetch_row($res) ){
3451  foreach ($res as $row){
3452  $valuekey = $row[0];
3453  $valuevalue = count($row)>1 ? $row[1] : $row[0];
3454  $valuelists[$vlname][$valuekey] = $valuevalue;
3455 
3456  if ( count($row)>2 ){
3457  $valuelists[$vlname.'__meta'][$valuekey] = $row[2];
3458  }
3459  }
3460  //mysql_free_result($res);
3461  } else {
3462  throw new Exception("Valuelist query '".$value."' failed. ", E_USER_NOTICE);
3463  }
3464 
3465  } else {
3466  $valuelists[$vlname][$key] = $value;
3467  }
3468  }
3469  }
3470 
3471 
3472  }
3473 
3474  }
3475 
3476 
3482  function &getValuelistsConfig(){
3483  return $this->_valuelistsConfig;
3484  }
3485 
3486 
3491  $this->_valuelists = null;
3492  }
3493 
3503  function &getValuelist( $name ){
3504  if ( !isset($this->_cookedValuelists[$name]) ){
3505  $this->_cookedValuelists[$name] = null;
3506  $delegate =& $this->getDelegate();
3507  if ( method_exists($delegate, 'valuelist__'.$name) ){
3508  $res = call_user_func(array(&$delegate, 'valuelist__'.$name));
3509  if ( is_array($res) ) $this->_cookedValuelists[$name] = $res;
3510  }
3511 
3512  $parent =& $this->getParent();
3513  if ( !isset($this->_cookedValuelists[$name]) and isset($parent) ){
3514  $res = $parent->getValuelist($name);
3515  if ( is_array($res) ) $this->_cookedValuelists[$name] = $res;
3516  }
3517 
3519  $appdel =& $app->getDelegate();
3520  if ( !isset($this->_cookedValuelists[$name]) and isset($appdel) and method_exists($appdel, 'valuelist__'.$name) ){
3521  $res = call_user_func(array(&$appdel, 'valuelist__'.$name));
3522  if ( is_array($res) ) $this->_cookedValuelists[$name] = $res;
3523  }
3524 
3525  if ( !isset($this->_cookedValuelists[$name]) and !isset( $this->_valuelists ) ){
3526  $this->_loadValuelistsIniFile();
3527  }
3528  $this->_loadValuelist($name); // Load the valuelist from the ini file if it is defined there
3529 
3530  if ( !isset($this->_cookedValuelists[$name]) and isset( $this->_valuelists[$name] ) ){
3531  $this->_cookedValuelists[$name] = $this->_valuelists[$name];
3532 
3533  }
3534 
3535  import( 'Dataface/ValuelistTool.php');
3536  if ( !isset($this->_cookedValuelists[$name]) and Dataface_ValuelistTool::getInstance()->hasValuelist($name) ){
3537 
3538  $this->_cookedValuelists[$name] = Dataface_ValuelistTool::getInstance()->getValuelist($name);
3539  }
3540 
3541  if ( isset($this->_cookedValuelists[$name]) and isset($delegate) and method_exists($delegate, 'valuelist__'.$name.'__decorate') ){
3542 
3543  $methodname = 'valuelist__'.$name.'__decorate';
3544  $this->_cookedValuelists[$name] = $delegate->$methodname($this->_cookedValuelists[$name]);
3545  } else if ( isset($this->_cookedValuelists[$name]) and isset($appdel) and method_exists($appdel, 'valuelist__'.$name.'__decorate') ){
3546  $methodname = 'valuelist__'.$name.'__decorate';
3547  $this->_cookedValuelists[$name] = $appdel->$methodname($this->_cookedValuelists[$name]);
3548 
3549  }
3550  }
3551  return $this->_cookedValuelists[$name];
3552  }
3553 
3563  function &valuelists(){
3564 
3565  if ( !isset( $this->_valuelists ) ){
3566 
3567  $this->_loadValuelistsIniFile();
3568  }
3569  return $this->_valuelists;
3570  }
3571 
3572 
3579  $valuelists = array_keys($this->valuelists());
3580  $delegate =& $this->getDelegate();
3581  if ( isset($delegate) ){
3582  $delegate_methods = get_class_methods(get_class($delegate));
3583  $valuelist_methods = preg_grep('/^valuelist__/', $delegate_methods);
3584  foreach ( $valuelist_methods as $method ){
3585  $valuelists[] = substr($method, 11);
3586  }
3587  }
3588  import( 'Dataface/ValuelistTool.php');
3589 
3590  $valuelists = array_merge($valuelists, array_keys(Dataface_ValuelistTool::getInstance()->valuelists()));
3591  return $valuelists;
3592  }
3593 
3594 
3595  // @}
3596  // END Valuelists
3597  //----------------------------------------------------------------------------------------------
3598 
3599 
3600  //----------------------------------------------------------------------------------------------
3601  // @{
3612  function _relationshipsIniFilePath(){
3613  return $this->basePath().'/tables/'.$this->tablename.'/relationships.ini';
3614  }
3615 
3620  function _hasRelationshipsIniFile(){
3621 
3622  return file_exists( $this->_relationshipsIniFilePath() );
3623  }
3624 
3625 
3633  function _loadRelationshipsIniFile(){
3634 
3635 
3636  import( 'Dataface/ConfigTool.php');
3637  $configTool =& Dataface_ConfigTool::getInstance();
3638  $conf =& $configTool->loadConfig('relationships', $this->tablename);
3639 
3640  $r =& $this->_relationships;
3641  foreach ($conf as $rel_name => $rel_values){
3642  // Case 1: The we have an array of values - meaning that this is the definition of a relationship.
3643  // Right now there is only one case, but we could have cases with single entries also.
3644  if ( is_array( $rel_values ) ){
3645 
3646  $r[$rel_name] = new Dataface_Relationship($this->tablename, $rel_name, $rel_values);
3647 
3648  }
3649  }
3650 
3651  $parent =& $this->getParent();
3652  if ( isset($parent) ){
3653  $this->_relationships = array_merge($parent->relationships(), $this->_relationships);
3654  }
3655  $this->_relationshipsLoaded = true;
3656 
3657  }
3658 
3659 
3660 
3667  function addRelationship($name, $relationship){
3668  if ( !is_array($relationship) ){
3669  throw new Exception("In Dataface_Table::addRelationship() 2nd argument expected to be an array, but received ", E_USER_ERROR);
3670  }
3671  $this->_relationships[$name] = new Dataface_Relationship($this->tablename, $name, $relationship);
3672 
3673  }
3674 
3682  $r =& $this->relationships();
3683  if ( array_key_exists(__FUNCTION__,$this->_cache) ) return $this->_cache[__FUNCTION__];
3684  foreach ( array_keys($r) as $name ){
3685  if ( $this->_relationships[$name]->isParentRelationship() ){
3686  $this->_cache[__FUNCTION__] =& $r[$name];
3687  return $r[$name];
3688  }
3689  }
3690  $null = null;
3691  return $null;
3692  }
3693 
3700  $r =& $this->relationships();
3701  if ( array_key_exists(__FUNCTION__,$this->_cache) ) return $this->_cache[__FUNCTION__];
3702  foreach ( array_keys($r) as $name ){
3703  if ( $r[$name]->isChildrenRelationship() ){
3704  $this->_cache[__FUNCTION__] =& $r[$name];
3705  return $r[$name];
3706  }
3707  }
3708  $null = null;
3709  return $null;
3710  }
3711 
3712 
3723  function getRelationshipRange($relationshipName){
3724  if ( isset( $this->_relationshipRanges[$relationshipName] ) ){
3725  return $this->_relationshipRanges[$relationshipName];
3726  } else {
3727  return $this->_defaultRelationshipRange;
3728  }
3729  }
3730 
3743  function setRelationshipRange($relationshipName, $lower, $upper){
3744  if ( !isset( $this->_relationshipRanges ) ) $this->_relationshipRanges = array();
3745  $this->_relationshipRanges[$relationshipName] = array($lower, $upper);
3746 
3747  }
3748 
3757  return $this->_defaultRelationshipRange;
3758  }
3759 
3766  function setDefaultRelationshipRange($lower, $upper){
3767  $this->_defaultRelationshipRange = array($lower, $upper);
3768  }
3769 
3775  function &getRelationshipsConfig(){
3776  return $this->_relationshipsConfig;
3777  }
3778 
3779 
3780 
3789  function &getRelationship($name){
3790  $r =& $this->relationships();
3791  if ( !is_string($name) ) throw new Exception("Relationship name is not a string");
3792  if ( !isset($r[$name]) ){
3793  $err = PEAR::raiseError("Attempt to get relationship nonexistent '$name' from table '".$this->tablename, E_USER_ERROR);
3794  return $err;
3795  }
3796 
3797  return $r[$name];
3798  }
3799 
3807  function hasRelationship($name){
3808  $r =& $this->relationships();
3809  return isset($r[$name]);
3810  }
3811 
3812 
3813 
3827  function getRelationshipsAsActions($params=array(), $relationshipName=null, $passthru=false){
3828 
3829  $relationships =& $this->relationships();
3830  $rkeys = array_keys($relationships);
3831 
3832  if ( isset( $this->_cache['getRelationshipsAsActions']) ){
3833  $actions = $this->_cache['getRelationshipsAsActions'];
3834  } else {
3835  $actions = array();
3836  foreach ( $rkeys as $key){
3837  $srcTable =& $relationships[$key]->getSourceTable();
3838  $actions[$key] = array(
3839  'name'=>$key,
3840  'id'=>$key,
3841  'label'=>ucwords(str_replace('_',' ',$key)),
3842  'url' =>'{$this->url(\'-action=related_records_list&-relationship='.$key.'\')}',
3843  'selected_condition' => '$query[\'-relationship\'] == \''.$key.'\'',
3844  //'label_i18n' => $srcTable->tablename.'::'.$key.'.label',
3845  //'description_i18n' => $srcTable->tablename.'::'.$key.'.description',
3846  'condition' => '$record and $record->checkPermission("view related records", array("relationship"=>"'.basename($key).'"))',
3847  'order' => 1,
3848  'visible'=>(!($relationships[$key]->isParentRelationship()) ? 1 : 0)
3849  );
3850  if ( isset($relationships[$key]->_schema['action']) ){
3851  $actions[$key] = array_merge($actions[$key], $relationships[$key]->_schema['action']);
3852  }
3853  $actions[$key]['label'] = df_translate('tables.'.$srcTable->tablename.'.relationships.'.$key.'.label', @$actions[$key]['label']);
3854  $actions[$key]['description'] = df_translate('tables.'.$srcTable->tablename.'.relationships.'.$key.'.description', @$actions[$key]['description']);
3855  unset($srcTable);
3856  }
3857  $this->_cache['getRelationshipsAsActions'] = $actions;
3858  }
3859 
3860  import('Dataface/ActionTool.php');
3861  $actionsTool =& Dataface_ActionTool::getInstance();
3862  $out = $actionsTool->getActions($params, $actions);
3863  if ( isset($relationshipName) ) {
3864  if ( isset($out[$relationshipName]) ){
3865  return @$out[$relationshipName];
3866  } else {
3867  return $actionsTool->getAction($params, $actions[$relationshipName]);
3868 
3869  }
3870  }
3871  return $out;
3872  }
3873 
3880  function &relationships(){
3881  if ( !$this->_relationshipsLoaded ){
3882  $start = microtime_float();
3883  $this->_loadRelationshipsIniFile();
3884  $end = microtime_float()-$start;
3885  if ( DATAFACE_DEBUG ){
3886  $this->app->addDebugInfo("Time to load relationships: $end");
3887  }
3888  }
3889 
3890  return $this->_relationships;
3891  }
3892 
3893 
3902 
3903  if ( strpos($fieldname,'.') !== false ){
3904  $path = explode('.', $fieldname);
3905  $relationship =& $this->getRelationship($path[0]);
3906 
3907  $domainTable =& self::loadTable($relationship->getDomainTable());
3908  if ( $domainTable->hasField($path[1]) ) return $domainTable;
3909 
3910  //print_r($relationship->_schema['selected_tables']);
3911  foreach ($relationship->_schema['selected_tables'] as $table ){
3912  if ( $table == $domainTable->tablename ) continue;
3913  $table =& self::loadTable($table, $this->db);
3914  //if ( in_array( $path[1], array_keys($table->fields()) ) ){
3915  if ( $table->hasField($path[1]) ){
3916  return $table;
3917  }
3918  unset($table);
3919  }
3920 
3921  return PEAR::raiseError(SCHEMA_TABLE_NOT_FOUND,null,null,null,"Failed to find table table for field '$fieldname' in Dataface_Table::getTableTableForField() ");
3922  } else {
3923  return $this;
3924  }
3925 
3926  }
3927 
3928  // @}
3929  // End Relationships
3930  //----------------------------------------------------------------------------------------------
3931 
3932  //----------------------------------------------------------------------------------------------
3933  // @{
3947  function &getDelegate(){
3948  $out = null;
3949  if ( !isset( $this->_delegate ) ){
3950  if ( $this->_hasDelegateFile() ){
3951 
3952  $this->_loadDelegate();
3953 
3954  } else {
3955 
3956  return $out;
3957  }
3958  }
3959  return $this->_delegate;
3960 
3961  }
3962 
3963 
3964 
3965 
3966 
3967 
3968 
3969 
3970 
3971 
3976  function _delegateFilePath(){
3977  $path =$this->basePath().'/tables/'.$this->tablename.'/'.$this->tablename.'.php';
3978 
3979  return $path;
3980  }
3981 
3982 
3983 
3984 
3985 
3986 
3987 
3992  function _hasDelegateFile(){
3993  return file_exists( $this->_delegateFilePath() );
3994  }
3995 
3996 
4001  function _loadDelegate(){
4002 
4003  if ( $this->_hasDelegateFile() ){
4004 
4005  import( $this->_delegateFilePath() );
4006  $delegate_name = "tables_".$this->tablename;
4007  $this->_delegate = new $delegate_name();
4008  if ( isset($this->_delegate) and method_exists($this->_delegate, 'getDelegate') ){
4009  $del = $this->_delegate->getDelegate();
4010  if ( isset($del) ){
4011  $this->_delegate = $del;
4012  }
4013  }
4014 
4015  if ( method_exists($this->_delegate, 'tablePermissions') ){
4016  // table permissions are now just done inside the getPermissions() method.
4017  // so the tablePermissions() method is no longer supported. Let the developer
4018  // know in case he has old code.
4019  throw new Exception(
4021  'tablePermissions method no longer supported',
4022  'Dataface noticed that the delegate class for the table "'.$this->tablename.'" contains a tablePermissions() method. This method is no longer supported as of Dataface version 0.6. Please use the getPermissions() method instead with first parameter null to achieve the same results.
4023  For example:
4024  function getPermissions(&$record, $params){
4025  if ( $record === null ){
4026  // return generic table permissions
4027  } else {
4028  // return record-specific permissions
4029  }
4030  }',
4031  array('table'=>$this->tablename)
4032  ), E_USER_NOTICE
4033  );
4034  }
4035 
4036  return true;
4037  } else {
4038  return false;
4039  }
4040  }
4041 
4042 
4043  // @}
4044  // End Delegate Classes
4045  //----------------------------------------------------------------------------------------------
4046 
4047 
4048 
4049  //----------------------------------------------------------------------------------------------
4050  // @{
4069  function getTabs(){
4070  return array_keys($this->tabs());
4071  }
4072 
4098  function &tabs($record=null){
4099 
4100  $del =& $this->getDelegate();
4101  if ( isset($del) and method_exists($del, '__tabs__') ){
4102  $tabs = $del->__tabs__($record);
4103  $i=0;
4104  foreach ($tabs as $tkey=>$tdata){
4105  $tabs[$tkey]['order'] = $i++;
4106  }
4107  return $tabs;
4108  } else {
4109 
4110  $tabs = $this->_tabs;
4111  if ( isset($record) ){
4112  foreach ( $this->__join__($record) as $tablename=>$tablelabel ){
4113  if ( !isset($tabs[$tablename]) ){
4114  $tabs[$tablename] = array('name'=>$tablename, 'label'=>$tablelabel, 'description'=>'');
4115  }
4116  }
4117  }
4118  $i=0;
4119  foreach ($tabs as $tkey=>$tdata){
4120  $tabs[$tkey]['order'] = $i++;
4121  }
4122  return $tabs;
4123  }
4124  }
4125 
4133  function &getTab($tabname, $record=null){
4134  $tabs =& $this->tabs($record);
4135  return $tabs[$tabname];
4136  }
4137 
4138 
4155  function __join__(&$record){
4156  $del =& $this->getDelegate();
4157  if ( isset($del) and method_exists($del, '__join__') ){
4158  return $del->__join__($record);
4159  } else if ( isset($this->_joinTables) ){
4160  return $this->_joinTables;
4161  } else {
4162  return array();
4163  }
4164  }
4165 
4177  function hasJoinTable($tablename, &$record){
4178  return array_key_exists($tablename,$this->__join__($record));
4179  }
4180 
4181 
4182 
4183  // @}
4184  // End Tabs
4185  //----------------------------------------------------------------------------------------------
4186 
4187 
4188  //----------------------------------------------------------------------------------------------
4189  // @{
4220  function &getFieldgroup($fieldgroupname){
4221  if ( !isset( $this->_fieldgroups[$fieldgroupname] ) ){
4222  $parent =& $this->getParent();
4223  if ( isset($parent) ){
4224  $fg =& $parent->getFieldgroup($fieldgroupname);
4225  return $fg;
4226  }
4227  return PEAR::raiseError("Attempt to get nonexistent field group '$fieldgroupname' from table '".$this->tablename."'\n<br>", E_USER_ERROR);
4228  }
4229  return $this->_fieldgroups[$fieldgroupname];
4230  }
4231 
4232 
4245  function &getFieldgroups(){
4246  if ( !isset( $this->_cache[__FUNCTION__] ) ){
4247  $fg =& $this->_fieldgroups;
4248  $parent =& $this->getParent();
4249  if ( isset($parent) ){
4250  $fg = array_merge_recursive_unique($parent->getFieldgroups(), $fg);
4251  uasort($fg, array(&$this, '_compareFields'));
4252  }
4253  $this->_cache[__FUNCTION__] =& $fg;
4254  }
4255  return $this->_cache[__FUNCTION__];
4256  }
4257 
4258 
4259  // @}
4260  // End Field Groups
4261  //----------------------------------------------------------------------------------------------
4262 
4263 
4264  //----------------------------------------------------------------------------------------------
4265  // @{
4281  function &getImportFilters(){
4282  import( 'Dataface/ImportFilter.php');
4283  if ( $this->_importFilters === null ){
4284  $this->_importFilters = array();
4285  /*
4286  * Filters have not been loaded yet.. let's load them.
4287  *
4288  * Any method in the delegate file with a name of the form __import__<string>
4289  * is considered to be an import filter.
4290  *
4291  */
4292  $delegate =& $this->getDelegate();
4293  if ( $delegate !== null ) {
4294  $methods = get_class_methods(get_class( $delegate ) );
4295  foreach ( $methods as $method ){
4296  $matches = array();
4297  if ( preg_match( '/^__import__(.*)$/', $method, $matches) ){
4298  $filter = new Dataface_ImportFilter($this->tablename, $matches[1], df_translate('import_filters:'.$matches[1].':label', ucwords(str_replace('_',' ',$matches[1]))));
4299  $this->_importFilters[$matches[1]] =& $filter;
4300  unset($filter);
4301  }
4302  }
4303  }
4304 
4305  $parent =& $this->getParent();
4306  if ( isset($parent) ){
4307  $this->_importFilters = array_merge($parent->getImportFilters(), $this->_importFilters);
4308  }
4309 
4310  }
4311 
4312  return $this->_importFilters;
4313  }
4314 
4315 
4327  function registerImportFilter(&$filter){
4328  if (!is_a( $filter, 'Dataface_ImportFilter') ){
4329  throw new Exception("In Dataface_Table::registerImportFilter() 2nd argument expected to be of type 'Dataface_ImportFilter' but received '".get_class($filter)."'. ", E_USER_ERROR);
4330  }
4331  $filters =& $this->getImportFilters();
4332  $filters[$filter->name] =& $filter;
4333 
4334  }
4335 
4336 
4337 
4347  function getImportTables(){
4348  $res = mysql_query("SHOW TABLES LIKE '".$this->tablename."__import_%'", $this->db);
4349  if ( !$res ){
4350  throw new Exception("Error getting import table list for table '".$this->tablename."'.", E_USER_ERROR);
4351  }
4352 
4353  $tables = array();
4354  while ( $row = mysql_fetch_row($res) ){
4355  $tables[] = $row[0];
4356  }
4357  mysql_free_result($res);
4358  return $tables;
4359  }
4360 
4361 
4370  function createImportTable(){
4371  import('Dataface/QueryBuilder.php');
4372  /*
4373  * It is a good idea to clean the import tables before we create them.
4374  * That way they don't get cluttered
4375  */
4376  $this->cleanImportTables();
4377 
4378  $rand = rand(10000,999999);
4379  $name = $this->tablename.'__import_'.strval(time()).'_'.strval($rand);
4380  $qb = new Dataface_QueryBuilder($this->tablename);
4381  $res = mysql_query("CREATE TABLE `$name` SELECT * ".$qb->_from()." LIMIT 0", $this->db);
4382  if (!$res ){
4383  throw new Exception("Failed to create import table `$name` because a mysql error occurred: ".mysql_error($this->db)."\n", E_USER_ERROR);
4384  }
4385  return $name;
4386 
4387  }
4388 
4397  function cleanImportTables(){
4398 
4399  $tables = $this->getImportTables();
4401  $garbageLifetime = $app->_conf['garbage_collector_threshold'];
4402  foreach ($tables as $table){
4403  $matches =array();
4404  if ( preg_match('/^'.$this->tablename.'__import_(\d+)_(\d)$/', $table, $matches) ){
4405  if ( time() - intval($matches[1]) > intval($garbageLifetime) ){
4406  $res = mysql_query("DROP TABLE `$table`", $this->db);
4407  if ( !$res ){
4408  throw new Exception("Problem occurred attemtping to clean up old import table '$table'. MySQL returned an error: ".mysql_error($this->db)."\n", E_USER_ERROR);
4409  }
4410  }
4411  }
4412  }
4413  }
4414 
4415 
4425  return preg_match('/^\w+__import_(\d{5,20})_(\d{4,10})$/', $tablename);
4426 
4427 
4428  }
4429 
4430 
4482  function parseImportData($data, $importFilter=null, $defaultValues=array()){
4483  $filters =& $this->getImportFilters();
4484  $delegate =& $this->getDelegate();
4485  if ( $delegate === null ){
4486  /*
4487  * Currently the only place that Import filters can be defined is in the
4488  * delegate file. If there is no delegate file, then there are no filters.
4489  * if there are no filters, then we can't possibly do any importing so we
4490  * return an error.
4491  */
4493  }
4494  $errors = array();
4495  if ( $importFilter === null ){
4496  /*
4497  * The filter is not specified so we will try every filter until we find one
4498  * that works.
4499  */
4500  foreach (array_keys($filters) as $filtername){
4501  $parsed =& $filters[$filtername]->import($data, $defaultValues);
4502  if ( PEAR::isError($parsed) ){
4503  /*
4504  * This filter encountered an error.
4505  * Record the error, and unset the $parsed variable.
4506  */
4507  $errors[$filtername] =& $parsed;
4508  unset($parsed);
4509  continue;
4510  }
4511 
4512  break;
4513  }
4514 
4515  if ( isset($parsed) ){
4516  /*
4517  * The only way that the $parsed variable should be 'set' is if
4518  * one of the filters successfully parsed the data.
4519  */
4520  return $parsed;
4521 
4522  } else {
4524  "No suitable import filter was found to import data into table '".$this->tablename."'. The following filters were attempted: {".implode(',', array_keys($errors))."}."
4525  );
4526  }
4527  } else {
4528  /*
4529  * A particular import filter was specified so we will try with that one.
4530  */
4531  if ( !isset( $filters[$importFilter] ) ){
4532  return Dataface_Error::noImportFiltersFound("The import filter '".$importFilter."' was not found while attempting to import data into the table '".$this->tablename."'. The following import filters are available: {".implode(',', array_keys($errors))."}."
4533  );
4534  }
4535 
4536  return $filters[$importFilter]->import($data, $defaultValues);
4537  }
4538 
4539  }
4540 
4541 
4542  // @}
4543  // End Imports
4544  //----------------------------------------------------------------------------------------------
4545 
4546 
4547 
4548 
4549 
4550 
4551 
4552 
4553 
4554 
4555 
4556 
4557 
4558 
4562  function _parseINISection($section, &$curr){
4563 
4564  foreach ($section as $valuekey=>$valuevalue){
4565  if ( strpos($valuekey,':') > 0 ){
4566  $path = explode(':', $valuekey);
4567  $temp =& $curr;
4568  for ( $i=0; $i<count($path); $i++){
4569  if ( $i<count($path)-1){
4570  if ( !isset($temp[$path[$i]])) $temp[$path[$i]] = array();
4571  $temp2 =& $temp[$path[$i]];
4572  unset($temp);
4573  $temp =& $temp2;
4574  unset($temp2);
4575  } else {
4576  $temp[$path[$i]] = $valuevalue;
4577  }
4578  }
4579  } else {
4580  $curr[$valuekey] = $valuevalue;
4581  }
4582  }
4583 
4584 
4585  }
4586 
4587 
4588 
4589 
4590 
4591 
4592 
4593 
4594 
4595  //----------------------------------------------------------------------------------------------
4596  // @{
4609  function getType($fieldname){
4610  if ( !isset($this->_cache[__FUNCTION__][$fieldname]) ){
4611  $field =& $this->getField($fieldname);
4612  if ( PEAR::isError($field) ) return $field;
4613 
4614  if ( isset( $field ) ){
4615 
4616  $type = $field['Type'];
4617 
4618  $matches = array();
4619 
4620  if ( preg_match('/^([^\( ]+).*/', $type, $matches) ){
4621  $type = $matches[1];
4622  }
4623 
4624  $type = trim($type);
4625  $this->_cache[__FUNCTION__][$fieldname] = strtolower($type);
4626  }
4627  }
4628  return $this->_cache[__FUNCTION__][$fieldname];
4629 
4630 
4631  }
4632 
4639  function isDate($fieldname){
4640  $type = $this->getType($fieldname);
4641  if ( PEAR::isError($type) ){
4642  return false;
4643  }
4644  return in_array( $type, array('date','datetime','time','timestamp') );
4645  }
4646 
4647 
4654  function isBlob($fieldname){
4655  return in_array( $this->getType($fieldname), array('blob', 'longblob','tinyblob','mediumblob') );
4656  }
4657 
4668  return strtolower($this->getType($fieldname)) == 'container';
4669 
4670  }
4671 
4680  $field =& $this->getField($fieldname);
4681  //if ( !is_array($field) ) return false;
4682  return ($field['widget']['type'] == 'password');
4683  }
4684 
4690  function isText($fieldname){
4691  return in_array( $this->getType($fieldname), array('text','longtext','tinytext','mediumtext') );
4692  }
4693 
4701  function isXML($fieldname){
4702  $fld =& $this->getField($fieldname);
4703  return in_array( $fld['widget']['type'], array('table','group'));
4704  }
4705 
4711  function isChar($fieldname){
4712  return in_array( $this->getType($fieldname), array('varchar','char') );
4713  }
4714 
4720  function isInt($fieldname){
4721  return in_array(strtolower($this->getType($fieldname)), array('int','tinyint','mediumint','smallint','bigint'));
4722  }
4723 
4724 
4730  function isFloat($fieldname){
4731  return in_array(strtolower($this->getType($fieldname)), array('float','double','tinyfloat','mediumfloat','decimal'));
4732  }
4733 
4734 
4735  // @}
4736  // END Field Type Information
4737  //----------------------------------------------------------------------------------------------
4738 
4739 
4740  //----------------------------------------------------------------------------------------------
4741  // @{
4762  function parse_datetime($value){
4763  return $this->parse_datetype($value);
4764 
4765 
4766  }
4767 
4772  function parse_date($value){
4773 
4774  return $this->parse_datetype($value);
4775 
4776  }
4777 
4790  function parse_timestamp($value){
4791  if ( $value == 'CURRENT_TIMESTAMP' ){
4792  $value = date('Y-m-d H:i:s');
4793  }
4794  return $this->parse_datetype($value);
4795 
4796  }
4797 
4802  function parse_time($value){
4803  return $this->parse_datetype($value);
4804  }
4805 
4810  function isTimeStamp($value){
4811  $converter = new Dataface_converters_date;
4812  return $converter->isTimestamp($value);
4813  }
4814 
4819  function parse_datetype($value){
4820  $converter = new Dataface_converters_date;
4821  return $converter->parseDate($value);
4822 
4823 
4824  }
4825 
4826 
4836  function normalize($fieldname, $value){
4837 
4838  return $this->getValueAsString($fieldname, $this->parse($fieldname, $value));
4839  }
4840 
4853  function getValueAsString($fieldname, $value){
4854  $table =& $this->getTableTableForField($fieldname);
4855  $delegate =& $table->getDelegate();
4856  $rel_fieldname = $table->relativeFieldName($fieldname);
4857  if ( $delegate !== null and method_exists( $delegate, $rel_fieldname.'__toString') ){
4858  $value = call_user_func( array(&$delegate, $rel_fieldname.'__toString'), $value);
4859  } else
4860 
4861 
4862  if ( is_array($value) ){
4863  if ( method_exists( $this, $this->getType($fieldname)."_to_string") ){
4864  $value = call_user_func( array( &$this, $this->getType($fieldname)."_to_string"), $value );
4865  } else {
4866  $value = implode(', ', $value);
4867  }
4868  }
4869  $evt = new stdClass;
4870  $evt->table = $table;
4871  $evt->field =& $table->getField($rel_fieldname);
4872  $evt->value = $value;
4873  $evt->type = $table->getType($rel_fieldname);
4874  $this->app->fireEvent('after_getValueAsString', $evt);
4875  $value = $evt->value;
4876  return $value;
4877  }
4878 
4879 
4900  public function format($fieldname, $value){
4901 
4902  $out = $value;
4903  $table = $this->getTableTableForField($fieldname);
4904  if ( strpos($fieldname, '.') !== false ){
4905  list($rel, $fieldname) = explode('.', $fieldname);
4906  }
4907  $delegate = $table->getDelegate();
4908  $field =& $table->getField($fieldname);
4909  if ( PEAR::isError($field) ){
4910  throw new Exception('Could not load field '.$fieldname.': '.$field->getMessage(), $field->getCode());
4911  }
4912  $method = $fieldname.'__format';
4913  if ( isset($delegate) and method_exists($delegate, $method) ){
4914  return $delegate->$method($value);
4915  }
4916  // If this is a number, let's format it!!
4917  if ( $table->isInt($fieldname) or $table->isFloat($fieldname) ){
4918  if ( isset($field['number_format']) ){
4919  $locale_data = localeconv();
4920  $decimal = $locale_data['decimal_point'];
4921  $sep = $locale_data['thousands_sep'];
4922  $places = intval($field['number_format']);
4923  $out = number_format(floatval($out), $places, $decimal, $sep);
4924  } else if ( isset($field['money_format']) ){
4925 
4926  $fieldLocale = null;
4927  if ( method_exists($delegate, 'getFieldLocale') ){
4928  $fieldLocale = $delegate->getFieldLocale($this, $fieldname);
4929 
4930  }
4931  if ( !isset($fieldLocale) and @$field['locale'] ){
4932  $fieldLocale = $field['locale'];
4933  }
4934 
4935  if ( isset($fieldLocale) ){
4936  $oldLocale = setlocale(LC_MONETARY, '0');
4937  if ( $oldLocale != $fieldLocale ){
4938  setlocale(LC_MONETARY, $fieldLocale);
4939  }
4940  }
4941  $out = money_format($field['money_format'], floatval($out));
4942  if ( isset($fieldLocale) ){
4943  setlocale(LC_MONETARY, $oldLocale);
4944  }
4945 
4946  }
4947  } else if ( $table->isDate($fieldname) ){
4948 
4949  // If this is a date field, we will do some date formatting
4950 
4951  $fmt = null;
4952  switch ( strtolower($table->getType($fieldname)) ){
4953 
4954  case 'date':
4955  $fmt = '%x';break;
4956  case 'time':
4957  $fmt = '%r';break;
4958  default:
4959  $fmt = '%c';
4960 
4961  }
4962 
4963 
4964  $fieldLocale = null;
4965  if ( method_exists($delegate, 'getFieldLocale') ){
4966  $fieldLocale = $delegate->getFieldLocale($this, $fieldname);
4967 
4968  }
4969  if ( !isset($fieldLocale) and @$field['locale'] ){
4970  $fieldLocale = $field['locale'];
4971  }
4972 
4973  if ( isset($fieldLocale) ){
4974  $oldLocale = setlocale(LC_TIME, '0');
4975  if ( $oldLocale != $fieldLocale ){
4976  setlocale(LC_TIME, $fieldLocale);
4977  }
4978  }
4979 
4980  if ( isset($field['date_format']) ){
4981  $fmt = $field['date_format'];
4982  }
4983 
4984  if ( !strtotime($out) ) return '';
4985  $out = strftime($fmt, strtotime($out));
4986 
4987  if ( isset($fieldLocale) ){
4988  setlocale(LC_TIME, $oldLocale);
4989  }
4990 
4991 
4992 
4993  }
4994 
4995 
4996  // If A display format was set for this field, let's apply it
4997  if ( isset($field['display_format']) ){
4998  if ( isset($field['display_format_i18n']) ){
4999  $fmt = df_translate($field['display_format_18n'], $field['display_format']);
5000  } else {
5001  $fmt = $field['display_format'];
5002  }
5003 
5004  $out = sprintf($fmt, $out);
5005  }
5006  return $out;
5007  }
5008 
5009 
5020  function parse($fieldname, $value, $parseRepeat=true){
5021  if ( strpos($fieldname, '.') !== false ){
5022  // If this is a related field, we allow the related table to do the
5023  // parsing
5024  $table =& $this->getTableTableForField($fieldname);
5025  if ( PEAR::isError($table) ){
5026  throw new Exception( $table->toString(), E_USER_ERROR);
5027  }
5028  list($rel, $fieldname) = explode('.', $fieldname);
5029  return $table->parse($fieldname, $value, $parseRepeat);
5030  }
5031  $type = $this->getType($fieldname);
5032 
5033  $field =& $this->getField($fieldname);
5034 
5035 
5036  $delegate =& $this->getDelegate();
5037  if ( $delegate !== null and method_exists( $delegate, $fieldname.'__parse') ){
5038  $value = call_user_func( array( &$delegate, $fieldname.'__parse'), $value);
5039  }
5040 
5041  else if ( $parseRepeat and $field['repeat'] and ($this->isText($fieldname) or $this->isChar($fieldname) ) ){
5042 
5043  $value = $this->parse_repeated($fieldname, $value, @$field['separator']);
5044  }
5045 
5046  else if ( method_exists( $this, 'parse_'.strtolower($type) ) ){
5047 
5048  $value = call_user_func( array(&$this, 'parse_'.strtolower($type)), $value);
5049  }
5050 
5051  else if ( in_array($field['widget']['type'], array('group','table') ) and is_string($value) ){
5052  //error_log("\nAbout to parse $fieldname for value $value",3,'log.txt');
5053  //if ( is_string($value) and strlen($value)> 10){
5054  //error_log('About to serialize '.$fieldname, 3, 'log.txt');
5055  import( 'XML/Unserializer.php');
5056  $unserializer = new XML_Unserializer();
5057  $parsed = $unserializer->unserialize($value);
5058 
5059  if ( !PEAR::isError($parsed) ){
5060  $value = $unserializer->getUnserializedData();
5061  }
5062 
5063  //}
5064 
5065 
5066  } else if ( $value === "" and ($this->isInt($fieldname) or $this->isFloat($fieldname)) ){
5067  $value = null;
5068  }
5069 
5070  $evt = new stdClass;
5071  $evt->table = $this;
5072  $evt->field =& $field;
5073  $evt->value = $value;
5074  $evt->type = $type;
5075  $evt->parseRepeat = $parseRepeat;
5076  $this->app->fireEvent('after_parse', $evt);
5077  $value = $evt->value;
5078  return $value;
5079 
5080  }
5081 
5082 
5091  function parse_repeated($fieldname, $value, $separator="\n"){
5092  if ( !$separator ) $separator = "\n";
5093  if ( !is_array($value) ){
5094  $value = explode($separator, $value);
5095  }
5096  foreach (array_keys($value) as $key) {
5097  $value[$key] = $this->parse($fieldname, $value[$key], false);
5098  }
5099 
5100  return $value;
5101  }
5102 
5103 
5104 
5111  function parseString( $str, $values ){
5112  if ( !is_string($str) ) return $str;
5113  $matches = array();
5114  $blackString = $str;
5115  while ( preg_match( '/(?<!\\\)\$([0-9a-zA-Z\._\-]+)/', $blackString, $matches ) ){
5116  if ( $this->hasField($matches[1]) ){
5117  $replacement = $this->normalize($matches[1], $values[$matches[1]]);
5118 
5119  } else {
5120  $replacement = '\$0';
5121  }
5122  $str = preg_replace( '/(?<!\\\)\$'.$matches[1].'/', $replacement, $str);
5123  $blackString = preg_replace( '/(?<!\\\)\$'.$matches[1].'/', "", $blackString);
5124 
5125  }
5126  return $str;
5127  }
5128 
5129 
5130 
5142  function validate($fieldname, $value, &$params){
5143  $field =& $this->getField($fieldname);
5144  if ( $field['widget']['type'] == 'file' and is_uploaded_file(@$value['tmp_name']) and is_array($value)){
5145  // This bit of validation code is executed for files that have just been uploaded from the form.
5146  // It expects the value to be an array of the form:
5147  // eg: array('tmp_name'=>'/path/to/uploaded/file', 'name'=>'filename.txt', 'type'=>'image/gif').
5148 
5149  if ( !is_array(@$field['allowed_extensions']) and @$field['allowed_extensions']){
5150  $field['allowed_extensions'] = explode(',',@$field['allowed_extensions']);
5151  }
5152  if ( !is_array(@$field['allowed_mimetypes']) and @$field['allowed_mimetypes'] ){
5153  $field['allowed_mimetypes'] = explode(',',@$field['allowed_mimetypes']);
5154  }
5155  if ( !is_array(@$field['disallowed_extensions']) and @$field['disallowed_extensions'] ){
5156  $field['disallowed_extensions'] = explode(',',@$field['disallowed_extensions']);
5157  }
5158  if ( !is_array(@$field['disallowed_mimetypes']) and @$field['disallowed_extensions']){
5159  $field['disallowed_mimetypes'] = explode(',',@$field['disallowed_mimetypes']);
5160  }
5161 
5162  $field['allowed_extensions'] = @array_map('strtolower', @$field['allowed_extensions']);
5163  $field['allowed_mimetypes'] = @array_map('strtolower', @$field['allowed_mimetypes']);
5164  $field['disallowed_extensions'] = @array_map('strtolower', @$field['disallowed_extensions']);
5165  $field['disallowed_mimetypes'] = @array_map('strtolower', @$field['disallowed_mimetypes']);
5166  // We do some special validation for file uploads
5167  // Validate -- make sure that it is the proper mimetype and extension.
5168  if ( is_array( @$field['allowed_mimetypes'] ) and count($field['allowed_mimetypes']) > 0 ){
5169  if ( !in_array($value['type'], $field['allowed_mimetypes']) ){
5170  $params['message'] = "The file submitted in field '".$field['name']."' is not the correct type. Received '".$value['type']."' but require one of (".implode(',', $field['allowed_mimetypes']).").";
5171 
5172  return false;
5173  }
5174  }
5175 
5176  if ( @is_array(@$field['disallowed_mimetypes']) and in_array($value['type'], $field['disallowed_mimetypes']) ){
5177  $params['message'] = "The file submitted in field '".$fieldname."' has a restricted mime type. The mime type received was '".$value['type']."'.";
5178  return false;
5179  }
5180 
5181  $extension = '';
5182  $matches = array();
5183  if ( preg_match('/\.([^\.]+)$/', $value['name'], $matches) ){
5184  $extension = $matches[1];
5185  }
5186  $extension = strtolower($extension);
5187 
5188 
5189  if ( is_array( @$field['allowed_extensions'] ) and count($field['allowed_extensions']) > 0 ){
5190  if ( !in_array($extension, $field['allowed_extensions']) ){
5191  $params['message'] = "The file submitted does not have the correct extension. Received file has extension '".$extension."' but the field requires either ".implode(' or ', $field['allowed_extensions']).".";
5192 
5193  return false;
5194  }
5195  }
5196 
5197  if ( @is_array( @$field['disallowed_extensions'] ) and in_array($extension, $field['disallowed_extensions']) ){
5198  $params['message'] = "The file submitted in field '".$fieldname."' has a restricted extension. Its extension was '".$extension."' which is disallowed for this form.";
5199  return false;
5200  }
5201 
5202  if ( @$field['max_size'] and intval($field['max_size']) < intval(@$value['size']) ){
5203  $params['message'] = "The file submitted in field '".$fieldname."' is {$value['size']} bytes which exceeds the limit of {$field['max_size']} bytes for this field.";
5204  return false;
5205  }
5206  }
5207 
5208  //$delegate =& $this->getDelegate();
5209  //if ( $delegate !== null and method_exists($delegate, $fieldname."__validate") ){
5210  // /*
5211  // *
5212  // * The delegate defines a custom validation method for this field. Use it.
5213  // *
5214  // */
5215  // return call_user_func(array(&$delegate, $fieldname."__validate"), $this, $value, $params);
5216  //}
5217  return true;
5218  }
5219 
5220 
5221  // @}
5222  // End Field Data Manipulation
5223  //----------------------------------------------------------------------------------------------
5224 
5225 
5226 
5227 
5228  //----------------------------------------------------------------------------------------------
5229  // @{
5244  function displayBlock($blockName, $params=array()){
5245  if ( @$this->app->_conf['debug'] ) echo "<div class=\"debug_marker\">Block &quot;$blockName&quot;</div>";
5246  $delegate =& $this->getDelegate();
5247  //echo "Checking for block $blockName";
5248  $res = false;
5249 
5250  // Add the ability for Modules to define blocks without conflicting with
5251  // defined blocks in the application.
5252  // Added Feb. 28, 2007 by Steve Hannah for 0.6.14 release.
5253  $mres = false;
5254  if ( isset($this->app->_conf['_modules']) and count($this->app->_conf['_modules']) > 0){
5256  $mres = $mtool->displayBlock($blockName, $params);
5257  }
5258  if ( isset($delegate) and method_exists($delegate, 'block__'.$blockName) ){
5259  $methodname = 'block__'.$blockName;
5260  $fres = $delegate->$methodname($params);
5261  //$fres = call_user_func(array(&$delegate, 'block__'.$blockName), $params);
5262  if ( !PEAR::isError($fres) )
5263  $res = true;
5264  } else {
5265 
5266  $appDelegate =& $this->app->getDelegate();
5267  if (isset($appDelegate) and method_exists($appDelegate, 'block__'.$blockName) ){
5268  $methodname = 'block__'.$blockName;
5269  $fres = $appDelegate->$methodname($params);
5270  //$fres = call_user_func(array(&$appDelegate, 'block__'.$blockName), $params);
5271  if ( !PEAR::isError($fres) )
5272  $res = true;
5273  }
5274  }
5275  return ($res or $mres);
5276 
5277  }
5278 
5285  function getBlockContent($blockName, $params=array()){
5286  ob_start();
5287  $res = $this->displayBlock($blockName, $params);
5288  $out = ob_get_contents();
5289  ob_end_clean();
5290  if ( !$res ) return null;
5291  return $out;
5292  }
5293 
5294 
5295  // @}
5296  // End Templates
5297  //----------------------------------------------------------------------------------------------
5298 
5299 
5300 
5301 
5302  //----------------------------------------------------------------------------------------------
5303  // @{
5316  function newRecord($vals=null){
5317  return new Dataface_Record($this->tablename, $vals);
5318  }
5319 
5326  function &getRecord($query=null){
5327  return df_get_record($this->tablename, $query);
5328  }
5329 
5330  // @}
5331  // End Factory Methods
5332  //----------------------------------------------------------------------------------------------
5333 
5334 
5335  //----------------------------------------------------------------------------------------------
5336  // @{
5351  function baseUrl(){
5352  return DATAFACE_SITE_URL;
5353  //return $GLOBALS['Dataface_Globals_BaseUrl'];
5354 
5355  }
5356 
5357 
5358  private static $basePaths = array();
5359  public static function setBasePath($table, $path){
5360  self::$basePaths[$table] = $path;
5361  }
5362  public static function getBasePath($table){
5363  if ( isset(self::$basePaths[$table]) ){
5364  return self::$basePaths[$table];
5365  } else {
5366  return DATAFACE_SITE_PATH;
5367  }
5368  }
5369 
5376  function basePath(){
5377 
5378  return self::getBasePath($this->tablename);
5379  //return realpath($GLOBALS['Dataface_Globals_BasePath']);
5380  }
5381 
5382 
5383 
5384 
5385 
5386 
5391  function getIniFile(){
5392  return $this->_iniFile;
5393  }
5394 
5399  function setIniFile($file){
5400  $this->_iniFile = $file;
5401  }
5402 
5403 
5404 
5405 
5406 
5411  function clearCache(){
5412  $this->_cache = array();
5413  }
5414 
5415 
5416 
5417 
5418 
5419 
5420 
5421 
5428  function date_to_string($value){
5429 
5431 
5432 
5433  }
5434 
5439  function getSerializer(){
5440  if ( !isset( $this->_serializer ) ){
5441 
5442  $this->_serializer = new Dataface_Serializer($this->tablename);
5443  }
5444  return $this->_serializer;
5445  }
5446 
5452  function datetime_to_string($value){
5454  }
5455 
5461  function time_to_string($value){ return Dataface_converters_date::time_to_string($value); }
5462 
5468  function timestamp_to_string($value){ return Dataface_converters_date::timestamp_to_string($value); }
5469 
5470 
5471 
5472 
5473  // @}
5474  // End Utility Methods
5475  //----------------------------------------------------------------------------------------------
5476 
5477 
5478  //----------------------------------------------------------------------------------------------
5479  // @{
5500  function getActions(&$params,$noreturn=false){
5501  import( 'Dataface/ActionTool.php');
5502  $actionsTool =& Dataface_ActionTool::getInstance();
5503  if ( !$this->_actionsLoaded ){
5504  $this->_actionsLoaded = true;
5505  import( 'Dataface/ConfigTool.php');
5506  $configTool =& Dataface_ConfigTool::getInstance();
5507  $actions =& $configTool->loadConfig('actions',$this->tablename);
5508  //print_r($actions);
5509  $singularLabel = $this->getSingularLabel();
5510  $pluralLabel = $this->getLabel();
5511  foreach ($actions as $key=>$action){
5512  $action['table'] = $this->tablename;
5513  $action['name'] = $key;
5514  if ( !isset($action['id']) ) $action['id'] = $action['name'];
5515  if ( !isset($action['label']) ) $action['label'] = str_replace('_',' ',ucfirst($action['name']));
5516  if ( !isset($action['accessKey'])) $action['accessKey'] = substr($action['name'],0,1);
5517  if ( !isset($action['label_i18n']) ) $action['label_i18n'] = 'action:'.$action['name'].' label';
5518  if ( !isset($action['description_i18n'])) $action['description_i18n'] = 'action:'.$action['name'].' description';
5519  if ( isset($action['description']) ){
5520  $action['description'] = df_translate('actions.'.$action['name'].'.description', $action['description'], array('table_label'=>$singularLabel, 'table_label_plural'=>$pluralLabel));
5521  }
5522  if ( isset($action['label']) ){
5523  $action['label'] = df_translate('actions.'.$action['name'].'.label',$action['label'], array('table_label_singular'=>$singularLabel, 'table_label_plural'=>$pluralLabel));
5524  }
5525 
5526  $actionsTool->addAction($key, $action);
5527  }
5528 
5529 
5530  }
5531 
5532  $params['table'] = $this->tablename;
5533  if ( $noreturn ) return true;
5534  return $actionsTool->getActions($params);
5535 
5536  }
5537 
5538 
5539  // @}
5540  // End Actions
5541  //----------------------------------------------------------------------------------------------
5542 
5543 }