Xataface  2.0alpha2
Xataface Application Framework
 All Data Structures Namespaces Files Functions Variables Groups Pages
Record.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 import( 'Dataface/Table.php');
24 import( 'Dataface/RelatedRecord.php');
25 import('Dataface/LinkTool.php');
26 
27 
33 define('DATAFACE_RECORD_RELATED_RECORD_BLOCKSIZE', 30);
34 
35 
101 
109  var $secureDisplay = true;
110 
111 
117  var $_id;
118 
125  static function _nextId(){
126  static $id = 0;
127  return $id++;
128  }
129 
138  var $_parentRecord;
139 
140 
146  var $propertyChangeListeners=array();
147 
153  var $_values;
154 
155 
161  var $_table;
162 
163 
170 
171 
181  var $_relatedValues = array();
182 
188  var $_relatedValuesLoaded = array();
189 
193  var $_numRelatedRecords = array();
197  var $_lastRelatedRecordStart = 0;
198 
202  var $_lastRelatedRecordLimit = 30;
203 
207  var $_relationshipRanges;
208 
209 
214  var $_title;
215 
216 
223  var $_oldValues;
224 
228  var $_transientValues=array();
229 
230 
235  var $useMetaData = true;
236 
237 
244  var $_dirtyFlags = array();
245 
251  var $_relatedDirtyFlags = array();
252 
256  var $_isLoaded = array();
257 
261  var $_metaDataValues=array();
262 
263 
269  var $loadBlobs = false;
270 
275  var $_relatedMetaValues=array();
276 
277 
282  var $_delegate = null;
283 
287  var $cache=array();
288 
300  var $vetoSecurity = false;
301 
308  var $vetoFields = array();
309 
310 
315  var $pouch = array();
316 
317 
324  var $lang = null;
325 
326 
327  var $escapeOutput = true;
328 
329  //--------------------------------------------------------------------------------
330  // @{
339  function Dataface_Record($tablename, $values=null){
341  $this->_id = Dataface_Record::_nextId();
342  $this->_tablename = $tablename;
343  $this->_table =& Dataface_Table::loadTable($tablename);
344  $this->_delegate =& $this->_table->getDelegate();
345  $this->lang = $app->_conf['lang'];
346 
347  if ( $values !== null ){
348  if ( is_a($values, 'StdClass') ){
349  $values = get_object_vars($values);
350  }
351  if ( is_array($values) ){
352  foreach ($values as $k=>$v){
353  $this->setValue($k, $v);
354  }
355  $this->setSnapshot();
356  }
357  //$this->setValues($values);
358 
359  }
360  }
361 
362  // @}
363  // END Initialization
364  //---------------------------------------------------------------------------------
365 
375  function __destruct(){
376  unset($this->propertyChangeListeners);
377  unset($this->cache);
378  unset($this->pouch);
379  unset($this->vetoFields);
380  unset($this->_delegate);
381  unset($this->_metaDataValues);
382  unset($this->_transientValues);
383  unset($this->_oldValues);
384 
385  if ( isset($this->_parentRecord) ){
386  $this->_parentRecord->__destruct();
387  unset($this->_parentRecord);
388  }
389 
390 
391  }
392 
393  //------------------------------------------------------------------------------------
394  // @{
412  function callDelegateFunction($function, $fallback=null){
413  $del =& $this->_table->getDelegate();
414  $parent =& $this->getParentRecord();
415  if ( isset($del) && method_exists($del, $function) ){
416  return $del->$function($this);
417  //return call_user_func(array(&$del, $function), $this);
418  } else if ( isset($parent) ){
419 
420  return $parent->callDelegateFunction($function, $fallback);
421  } else {
422  return $fallback;
423  }
424 
425  }
426 
427 
428 
429 
430 
444  function getActions($params=array()){
445  $params['record'] =& $this;
446  $actions = $this->_table->getActions($params);
447 
448  $parent =& $this->getParentRecord();
449  if ( isset($parent) ){
450  $actions = array_merge_recursive_unique($parent->getActions($params), $actions);
451  }
452  return $actions;
453  }
454 
455 
461  function &table(){
462  return $this->_table;
463  }
464 
465 
474  function clearCache(){
475 
476  unset($this->cache);
477 
478  $this->cache=array();
479 
480  $this->_relatedValuesLoaded = array();
481 
482  $this->_relatedValues = array();
483  $this->_relatedMetaValues = array();
484 
485 
486  }
487 
488 
489 
490 
491 
516  function parseString( $str){
517  if ( !is_string($str) ) return $str;
518  $matches = array();
519  $blackString = $str;
520  while ( preg_match( '/(?<!\\\)\$([0-9a-zA-Z\._\-]+)/', $blackString, $matches ) ){
521  if ( $this->_table->hasField($matches[1]) ){
522  $replacement = $this->strval($matches[1]);
523 
524 
525  } else {
526  $replacement = $matches[1];
527  }
528  $str = preg_replace( '/(?<!\\\)\$'.$matches[1].'/', $replacement, $str);
529  $blackString = preg_replace( '/(?<!\\\)\$'.$matches[1].'/', "", $blackString);
530 
531  }
532  return $str;
533  }
534 
535  // @}
536  // END Utility Methods
537  //-------------------------------------------------------------------------------
538 
539 
540  //---------------------------------------------------------------------------------
541  //{@
542 
563  function getRelationshipRange($relationshipName){
564  if ( isset( $this->_relationshipRanges[$relationshipName] ) ){
565  return $this->_relationshipRanges[$relationshipName];
566  } else {
567  return $this->_table->getRelationshipRange($relationshipName);
568  }
569  }
570 
583  function setRelationshipRange($relationshipName, $lower, $upper){
584  if ( !isset( $this->_relationshipRanges ) ) $this->_relationshipRanges = array();
585  $this->_relationshipRanges[$relationshipName] = array($lower, $upper);
586 
587  }
588 
589 
590 
591 
592 
593 
594 
595 
609  function _relatedRecordLoaded($relname, $index, $where=0, $sort=0){
610 
611  $blockNumber = floor($index / DATAFACE_RECORD_RELATED_RECORD_BLOCKSIZE);
612  return ( isset( $this->_relatedValuesLoaded[$relname][$where][$sort][$blockNumber] ) and $this->_relatedValuesLoaded[$relname][$where][$sort][$blockNumber] );
613  }
614 
615 
628  function _translateRangeToBlocks($lower, $upper){
629 
630  $lowerBlock = floor($lower / DATAFACE_RECORD_RELATED_RECORD_BLOCKSIZE);
631  $upperBlock = floor($upper / DATAFACE_RECORD_RELATED_RECORD_BLOCKSIZE);
632  return array(intval($lowerBlock), intval($upperBlock));
633  }
634 
650  function _relatedRecordBlockLoaded($relname, $block, $where=0, $sort=0){
651  return ( isset( $this->_relatedValuesLoaded[$relname][$where][$sort][$block] ) and $this->_relatedValuesLoaded[$relname][$where][$sort][$block] );
652  }
653 
672  function _loadRelatedRecordBlock($relname, $block, $where=0, $sort=0){
673  if ( $this->_relatedRecordBlockLoaded($relname, $block, $where, $sort) ) return true;
674 
675  $relationship =& $this->_table->getRelationship($relname);
676  if ( !is_object($relationship) ){
677  throw new Exception(
678  df_translate(
679  'scripts.Dataface.Record._loadRelatedRecordBlock.ERROR_GETTING_RELATIONSHIP',
680  "Error getting relationship '$relname'. The value returned by getRelationship() was '$relationship'.",
681  array('relationship'=>$relname, 'retval'=>$relationship)
682  ), E_USER_ERROR);
683  }
684 
687 
688  if ( $start >= $this->numRelatedRecords($relname, $where) ){
689 
690 
691  return false;
692  }
693 
694  //$sql = $this->parseString($relationship->_schema['sql']);
695  $sql = $this->parseString($relationship->getSQL($this->loadBlobs, $where, $sort));
696 
697  // TODO We need to change this so that it is compatible with relationships that already specify a limit.
698  $sql .= " LIMIT ".addslashes($start).",".addslashes($limit);
699 
700 
701  //$res = mysql_query($sql, $this->_table->db);
702  $db =& Dataface_DB::getInstance();
703  $res = $db->query($sql, $this->_table->db, null, true);
704  if ( !$res and !is_array($res) ){
705 
706  throw new Exception( mysql_error($this->_table->db).
707  df_translate(
708  'scripts.Dataface.Record._loadRelatedRecordBlock.ERROR_LOADING_RELATED_RECORDS',
709  "Error loading related records for relationship '$relname' in table '".$this->_table->tablename."'. There was a problem performing the sql query '$sql'. The Mysql error returned was '".mysql_error($this->_table->db),
710  array('relationship'=>$relname,'table'=>$this->_table->tablename, 'mysql_error'=>mysql_error($this->_table->db), 'sql'=>$sql)
711  )
712  ,E_USER_ERROR);
713  }
714  $index = $start;
715  //while ( $row = mysql_fetch_assoc($res) ){
716  foreach ($res as $row){
717  $record_row = array();
718  $meta_row = array();
719  foreach ($row as $key=>$value){
720 
721  if ( strpos($key, '__') === 0 ){
722  $meta_row[$key] = $value;
723  } else {
724  $record_row[$key] = $this->_table->parse($relname.'.'.$key, $value);
725  }
726  unset($value);
727  }
728  $this->_relatedValues[$relname][$where][$sort][$index++] =& $record_row;
729  $this->_relatedMetaValues[$relname][$where][$sort][$index-1] =& $meta_row;
730  unset($record_row);
731  unset($meta_row);
732 
733  }
734 
735  $this->_relatedValuesLoaded[$relname][$where][$sort][$block] = true;
736 
737  return true;
738  }
739 
740 
773  function numRelatedRecords($relname, $where=0){
774 
775  if ( !isset( $this->_numRelatedRecords[$relname][$where]) ){
776  $relationship =& $this->_table->getRelationship($relname);
777 
778  //if ( $where !== 0 ){
779  $sql = $this->parseString($relationship->getSQL($this->loadBlobs, $where));
780  //} else {
781  // $sql = $this->parseString($relationship->_schema['sql']);
782  //}
783  $sql = stristr($sql, ' FROM ');
784  $sql = "SELECT COUNT(*) as num".$sql;
785  //$dbObj =
786  //$res = mysql_query($sql, $this->_table->db);
787  //$res = mysql_query($sql, $this->_table->db);
788  $db =& Dataface_DB::getInstance();
789  $res = $db->query($sql, $this->_table->db, null, true);
790  if ( !$res and !is_array($res) ){
791  //if ( !$res ){
792  throw new Exception(
793  df_translate(
794  'scripts.Dataface.Record.numRelatedRecords.ERROR_CALCULATING_NUM_RELATED_RECORDS',
795  "Error calculating the number of related records there are for the relationship '$relname' in the table '".$this->_table->tablename."'. There was a problem performing the sql query '$sql'. The MYSQL error returned was '".mysql_error($this->_table->db)."'.\n<br>",
796  array('relationship'=>$relname,'table'=>$this->_table->tablename,'mysql_error'=>mysql_error($this->_table->db),'sql'=>$sql)
797  ), E_USER_ERROR);
798  }
799 
800  $this->_numRelatedRecords[$relname][$where] = $res[0]['num'];
801  }
802  return $this->_numRelatedRecords[$relname][$where];
803 
804  }
805 
868  function &getRelatedRecords( $relname, $multipleRows=true , $start = null, $limit=null, $where=0, $sort=0){
869  if ( !is_bool($multipleRows) and intval($multipleRows) > 0 ){
870  /*
871  * Give caller the option of omitting the "MultipleRows" option.
872  */
873  $sort = $where;
874  $where = $limit;
875  $limit = $start;
876  $start = $multipleRows;
877  $multipleRows = true;
878  } else if ( $multipleRows === 'all'){
879  // the second argument is the 'all' specifier - meaning that all records should be returned.
880  $where = ($start === null ? 0:$start);
881  $sort = $limit;
882  $start = 0;
883  $limit = $this->numRelatedRecords($relname, $where) + 1;
884  $multipleRows = true;
885 
886 
887 
888  } else if ( is_string($multipleRows) and intval($multipleRows) === 0 and $multipleRows !== "0"){
889 
890  if ( is_string($start) and intval($start) === 0 and $start !== "0" ){
891  // $start actually contains the sort parameter
892  $sort = $start;
893  $start = $limit;
894  $limit = $where;
895  } else {
896  $sort = $where;
897 
898  }
899  $where = $multipleRows;
900  $multipleRows = 'all';
901  return $this->getRelatedRecords($relname, $multipleRows, $where, $sort);
902  }
903 
904 
905  if ( $where === null ) $where = 0;
906  if ( $sort === null ) $sort = 0;
907  list($defaultStart, $defaultEnd) = $this->getRelationshipRange($relname);
908  if ( $start === null){
909  //$start = $this->_lastRelatedRecordStart;
910  $start = $defaultStart;
911  } else {
912  $this->_lastRelatedRecordStart = $start;
913  }
914 
915  if ( $limit === null ){
916  //$limit = $this->_lastRelatedRecordLimit;
917  $limit = $defaultEnd-$defaultStart;
918  } else {
919  $this->_lastRelatedRecordLimit = $limit;
920  }
921 
922 
923  $range = $this->_translateRangeToBlocks($start,$start+$limit-1);
924  if ( $where === null ) $where = 0;
925  if ( $sort === null ) $sort = 0;
926  if ( !$sort ){
927  $relationship =& $this->_table->getRelationship($relname);
928  $order_column = $relationship->getOrderColumn();
929  if ( !PEAR::isError($order_column) and $order_column){
930  $sort = $order_column;
931  }
932  }
933  // [0]->startblock as int , [1]->endblock as int
934  for ( $i=$range[0]; $i<=$range[1]; $i++){
935  $res = $this->_loadRelatedRecordBlock($relname, $i, $where, $sort);
936 
937  // If the above returned false, that means that we have reached the end of the result set.
938  if (!$res ) break;
939  }
940 
941 
942  if ( $multipleRows === true ){
943 
944 
945  $out = array();
946  for ( $i=$start; $i<$start+$limit; $i++){
947  if ( !isset( $this->_relatedValues[$relname][$where][$sort][$i] ) ) continue;
948  $out[$i] =& $this->_relatedValues[$relname][$where][$sort][$i];
949  }
950  //return $this->_relatedValues[$relname][$where][$sort];
951  return $out;
952  } else if (is_array($multipleRows) ){
953  throw new Exception("Unsupported feature: using array query for multiple rows in getRelatedRecords", E_USER_ERROR);
954  // we are searching using a query
955  foreach ( array_keys($this->_relatedValues[$relname][$where][$sort]) as $rowIndex ){
956  $row =& $this->_relatedValues[$relname][$where][$sort][$rowIndex];
957  $match = true;
958  foreach ( $multipleRows as $key=>$value ){
959  if ( strpos($key,'.')!== false ){
960  // if the query specifies an absolute path, just parse it
961  list($dummy, $key) = explode('.', $key);
962  if ( trim($dummy) != trim($relname) ){
963  // make sure that this query is for this relationship
964  continue;
965  }
966  }
967  $fullpath = $relname.'.'.$key;
968  $nvalue = $this->_table->normalize($fullpath, $value);
969  if ( $nvalue != $this->_table->normalize($fullpath, $rowIndex) ){
970  // see if this column matches
971  $match = false;
972  }
973  }
974  if ( $match ) return $row;
975  unset($row);
976  }
977 
978 
979  } else {
980  if (@count($this->_relatedValues[$relname][$where][$sort])>0){
981  if ( is_int( $start ) ){
982  return $this->_relatedValues[$relname][$where][$sort][$start];
983  } else {
984  return reset($this->_relatedValues[$relname][$where][$sort]);
985  }
986  //$first =& array_shift($this->_relatedValues[$relname]);
987  //array_unshift($this->_relatedValues[$relname], $first);
988  //return $first;
989  } else {
990  $null = null;
991  return $null;
992  }
993  }
994 
995  }
996 
1039  function getChildren($start=null, $limit=null){
1040  $delegate =& $this->_table->getDelegate();
1041  if ( isset($delegate) and method_exists($delegate, 'getChildren')){
1042  $children =& $delegate->getChildren($this);
1043  return $children;
1044  } else if ( ( $rel =& $this->_table->getChildrenRelationship() ) !== null ){
1045  $it = $this->getRelationshipIterator($rel->getName(), $start, $limit);
1046  $out = array();
1047  while ( $it->hasNext() ){
1048  $child = $it->next();
1049  $out[] = $child->toRecord();
1050  }
1051  return $out;
1052  } else {
1053 
1054  return null;
1055  }
1056  }
1057 
1058 
1076  function getChild($index){
1077  $children =& $this->getChildren($index,1);
1078  if ( !isset($children) || count($children) == 0 ) return null;
1079  return $children[0];
1080  }
1081 
1118  function &getParent(){
1119  $delegate =& $this->_table->getDelegate();
1120  if ( isset($delegate) and method_exists($delegate, 'getParent')){
1121  $parent = $delegate->getParent($this);
1122  return $parent;
1123  } else if ( ( $rel =& $this->_table->getParentRelationship() ) !== null ){
1124  $it = $this->getRelationshipIterator($rel->getName());
1125 
1126  if ( $it->hasNext() ){
1127  $parent = $it->next();
1128  $out = $parent->toRecord();
1129  return $out;
1130  }
1131  return null;
1132  } else {
1134  $contextRecord = $app->getRecordContext($this->getId());
1135  if ( $contextRecord ){
1136  $parent = $contextRecord->getParent();
1137  return $parent;
1138  }
1139  return null;
1140  }
1141  }
1142 
1143 
1144 
1145 
1146 
1147 
1168  function getRelationshipIterator($relationshipName, $start=null, $limit=null,$where=0, $sort=0){
1169  if ( !$sort ){
1170  $relationship =& $this->_table->getRelationship($relationshipName);
1171  if ( PEAR::isError($relationship) ){
1172  throw new Exception("Relationship $relationship could not be found: ".$relationship->getMessage());
1173  }
1174  $order_column = $relationship->getOrderColumn();
1175  if ( !PEAR::isError($order_column) and $order_column){
1176  $sort = $order_column;
1177  }
1178  }
1179  return new Dataface_RelationshipIterator($this, $relationshipName, $start, $limit, $where, $sort);
1180  }
1181 
1206  function &getRelatedRecordObjects($relationshipName, $start=null, $end=null,$where=0, $sort=0){
1207  $out = array();
1208 
1209  $it = $this->getRelationshipIterator($relationshipName, $start, $end,$where,$sort);
1210  while ($it->hasNext() ){
1211  $out[] =& $it->next();
1212  }
1213  return $out;
1214  }
1215 
1216 
1235  function &getRelatedRecord($relationshipName, $index=0, $where=0, $sort=0){
1236  if ( isset($this->cache[__FUNCTION__][$relationshipName][$index][$where][$sort]) ){
1237  return $this->cache[__FUNCTION__][$relationshipName][$index][$where][$sort];
1238  }
1239  $it = $this->getRelationshipIterator($relationshipName, $index, 1, $where, $sort);
1240  if ( $it->hasNext() ){
1241  $rec =& $it->next();
1242  $this->cache[__FUNCTION__][$relationshipName][$index][$where][$sort] =& $rec;
1243  return $rec;
1244  } else {
1245  $null = null; // stupid hack because literal 'null' can't be returned by ref.
1246  return $null;
1247  }
1248  }
1249 
1250 
1251 
1252 
1272  function moveUp($relationship, $index){
1273 
1274  $r =& $this->_table->getRelationship($relationship);
1275  $order_col = $r->getOrderColumn();
1276  if ( PEAR::isError($order_col) ) return $order_col;
1277  $order_table =& $r->getTable($order_col);
1278  if ( PEAR::isError($order_table) ) return $order_table;
1279 
1280  if ( $index == 0 ) return PEAR::raiseError("Cannot move up index 0");
1281  $it =& $this->getRelationshipIterator($relationship, $index-1, 2);
1282  if ( PEAR::isError($it) ) return $it;
1283  if ( $it->hasNext() ){
1284  $prev_record =& $it->next();
1285  if ( $it->hasNext() ){
1286  $curr_record =& $it->next();
1287  }
1288  }
1289 
1290  if ( !isset($prev_record) || !isset($curr_record) ){
1291  return PEAR::raiseError('Attempt to move record up in "'.$relationship.'" but the index "'.$index.'" did not exist.');
1292  }
1293 
1294  if ( intval($prev_record->val($order_col)) == intval($curr_record->val($order_col)) ){
1295  // The relationship's records are not ordered yet (consecutive records should have distinct order values.
1296  $res = $this->sortRelationship($relationship);
1297  if ( PEAR::isError($res) ) return $res;
1298  return $this->moveUp($relationship, $index);
1299  }
1300 
1301 
1302  $prev = $prev_record->toRecord($order_table->tablename);
1303  $curr = $curr_record->toRecord($order_table->tablename);
1304  $temp = $prev->val($order_col);
1305  $res = $prev->setValue($order_col, $curr->val($order_col));
1306  if (PEAR::isError($res) ) return $res;
1307  $res = $prev->save();
1308  if ( PEAR::isError($res) ) return $res;
1309  $res = $curr->setValue($order_col, $temp);
1310  if ( PEAR::isError($res) ) return $res;
1311  $res = $curr->save();
1312  if ( PEAR::isError($res) ) return $res;
1313 
1314  return true;
1315 
1316 
1317  }
1318 
1337  function moveDown($relationship, $index){
1338  return $this->moveUp($relationship, $index+1);
1339  }
1340 
1341 
1342 
1350  function sortRelationship($relationship, $start=null, $subset=null){
1351  $r =& $this->_table->getRelationship($relationship);
1352  $order_col = $r->getOrderColumn();
1353  if ( PEAR::isError($order_col) ) return $order_col;
1354  $order_table =& $r->getTable($order_col);
1355  if ( PEAR::isError($order_table) ) return $order_table;
1356 
1357  // Our strategy for sorting only a subset.
1358  // Let R be the list of records in the relationship ordered
1359  // using the default order.
1360  //
1361  // Let A be the list of records in our subset of R using the
1362  // default order.
1363  //
1364  // Let b = A[0]
1365  // Let a be the predecessor of b.
1366  // Let c be the last record in A.
1367  // Let d be the successor of c.
1368  // Let B = A union {a,d}
1369  // For any record x in R, let ord(x) be the value of the order column
1370  // in x.
1371  //
1372  //
1373  // The algorithm we will use to sort our subset is as follows:
1374  // if ( !exists(a) or ord(a) < ord(b) )
1375  // and
1376  // ( !exists(d) or ord(c) < ord(d) )
1377  // and
1378  // ( ord(c) - ord(b) >= count(A) ){
1379  // sort(A)
1380  // } else {
1381  // sort(R)
1382  // }
1383 
1384 
1385  if ( isset($start) ){
1386  // We are dealing with a subset, so let's go through our algorithm
1387  // to see if we can get away with only sorting the subset
1388  //$countR = $this->numRelatedRecords($relationship);
1389  $aExists = ($start > 0);
1390  $countA = count($subset);
1391  $B =& $this->getRelatedRecordObjects($relationship, max($start-1,0), $countA+2);
1392  $countB = count($B);
1393 
1394 
1395  if ( $aExists ){
1396  $dExists = ( $countB-$countA >=2 );
1397  } else {
1398  $dExists = ($countB-$countA >= 1);
1399  }
1400 
1401 
1402  $AOffset = 0;
1403  if ( $aExists ) $AOffset++;
1404 
1405 
1406  if ( (!$aExists or $B[0 + $AOffset]->val($order_col) > $B[0]->val($order_col) )
1407  and
1408  (!$dExists or $B[$countA-1+$AOffset]->val($order_col) < $B[$countB-1]->val($order_col) )
1409  and
1410  ( ($B[$countA-1+$AOffset]->val($order_col) - $B[0+$AOffset]->val($order_col)) >= ($countA - 1) ) ){
1411 
1412 
1413  $sortIndex = array();
1414  $i = $B[0+$AOffset]->val($order_col);
1415  foreach ($subset as $record){
1416  $sortIndex[$record->getId()] = $i++;
1417  }
1418 
1419 
1420  $i0 = $AOffset;
1421  $i1 = $countA+$AOffset;
1422  for ( $i = $i0; $i<$i1; $i++ ){
1423  $B[$i]->setValue($order_col, $sortIndex[ $B[$i]->getId() ] );
1424  $res = $B[$i]->save();
1425  if ( PEAR::isError($res) ) echo $res->getMessage();
1426  }
1427  $this->clearCache();
1428  return true;
1429  }
1430 
1431 
1432 
1433  }
1434 
1435  $it =& $this->getRelationshipIterator($relationship, 'all');
1436  $i = 1;
1437 
1438  while ( $it->hasNext() ){
1439  $rec =& $it->next();
1440  //$rec->setValue($order_col, $i++);
1441  $orderRecord =& $rec->toRecord($order_table->tablename);
1442  $orderRecord->setValue($order_col, $i++);
1443  $res = $orderRecord->save();
1444  if ( PEAR::isError($res) ) return $res;
1445  unset($rec);
1446  unset($orderRecord);
1447 
1448  }
1449  $this->clearCache();
1450 
1451  }
1452 
1453  // @}
1454  // End Relationships
1455  //--------------------------------------------------------------------------------------
1456 
1457  // @{
1458 
1498  function setValue($key, $value, $index=0){
1499 
1500  $oldValue = $this->getValue($key, $index);
1501 
1502 
1503  if ( strpos($key, '.')!== false ){
1504  throw new Exception("Unsupported operation: setting value on related record.", E_USER_ERROR);
1505 
1506  }
1507 
1508  // This is a local field
1509  else {
1510  if ( strpos($key, "__") === 0 && $this->useMetaData ){
1511  /*
1512  * This is a meta value..
1513  */
1514  return $this->setMetaDataValue($key, $value);
1515  }
1516 
1517  $add=true;
1518 
1519 
1520  if ( !array_key_exists($key, $this->_table->fields() ) ){
1521 
1522  if ( array_key_exists($key, $this->_table->transientFields()) ){
1523 
1524  $this->_transientValues[$key] = $value;
1525  $add=false;
1526  //return;
1527  }
1528 
1529  else if ( !array_key_exists($key, $this->_table->graftedFields(false)) ){
1530  $parent =& $this->getParentRecord();
1531 
1532  if ( isset($parent) and $parent->_table->hasField($key) ){
1533 
1534  $parent->setValue($key, $value, $index);
1535 
1536  }
1537  $add=false;
1538  } else {
1539 
1540 
1541 
1542  $add=true;
1543  }
1544 
1545  }
1546  if ( $add ){
1547  $this->_values[$key] = $this->_table->parse($key, $value);
1548 
1549  $this->_isLoaded[$key] = true;
1550  }
1551  }
1552 
1553 
1554  // now set the flag to indicate that the value has been changed.
1555 
1556  $this->clearCache();
1557 
1558  if ($oldValue != $this->getValue($key, $index) ){
1559 
1560  $this->setFlag($key, $index);
1561  if ( $this->vetoSecurity ){
1562  $this->vetoFields[$key] = true;
1563  }
1564  $this->clearCache();
1565 
1566  $this->firePropertyChangeEvent($key, $oldValue, $this->getValue($key,$index));
1567 
1568  // Now we should notify the parent record if this was a key field
1569  if ( array_key_exists($key, $this->_table->keys() ) ){
1570  if ( !isset($parent) ) $parent =& $this->getParentRecord();
1571  if ( isset($parent) ){
1572  $keys = array_keys($this->_table->keys());
1573  $pkeys = array_keys($parent->_table->keys());
1574  $key_index = array_search($key, $keys);
1575 
1576  $parent->setValue($pkeys[$key_index], $this->getValue($key, $index));
1577  }
1578  }
1579  }
1580 
1581 
1582  }
1583 
1584 
1585 
1586 
1587 
1604  function setValues($values){
1605  $fields = $this->_table->fields(false, true);
1606  foreach ($values as $key=>$value){
1607  if ( isset( $fields[$key] ) ){
1608  $this->setValue($key, $value);
1609  } else if ( strpos($key,'__')===0){
1610  $this->setMetaDataValue($key,$value);
1611  }
1612  }
1613  }
1614 
1615 
1616  function getVersion(){
1617  $versionField = $this->_table->getVersionField();
1618  if ( !isset($versionField) ){
1619  return 0;
1620  } else {
1621  return intval($this->val($versionField));
1622  }
1623  }
1624 
1808  function &getValue($fieldname, $index=0, $where=0, $sort=0, $debug=false){
1809  static $callcount=0;
1810  $callcount++;
1811  if ( $debug ) echo "Num calls to getValue(): $callcount";
1812  if ( isset($this->cache[__FUNCTION__][$fieldname][$index][$where][$sort]) ){
1813  return $this->cache[__FUNCTION__][$fieldname][$index][$where][$sort];
1814  }
1815 
1816 
1817 
1818  if ( is_array($index) ){
1819  throw new Exception(
1820  df_translate(
1821  'scripts.Dataface.Record.getValue.ERROR_PARAMETER_2',
1822  "In Dataface_Record.getValue() expected 2nd parameter to be integer but received array."
1823  ), E_USER_ERROR);
1824  }
1825  if ( is_array($where) ){
1826  throw new Exception(
1827  df_translate(
1828  'scripts.Dataface.Record.getValue.ERROR_PARAMETER_3',
1829  "In Dataface_Record.getValue() expected 3rd parameter to be a string, but received array."
1830  ), E_USER_ERROR);
1831  }
1832  if ( is_array($sort) ){
1833  throw new Exception(
1834  df_translate(
1835  'scripts.Dataface.Record.getValue.ERROR_PARAMETER_4',
1836  "In Dataface_Record.getValue() expected 4th parameter to be a string but received array."
1837  ), E_USER_ERROR);
1838  }
1839 
1840  $out = null;
1841  if ( strpos($fieldname,'.') === false ){
1842  $delegate =& $this->_delegate;
1843 
1844  if ( !isset( $this->_values[$fieldname] ) ) {
1845  // The field is not set... check if there is a calculated field we can use.
1846  if ( $delegate !== null and method_exists($delegate, "field__$fieldname")){
1847  $methodname = "field__$fieldname";
1848  $out = $delegate->$methodname($this,$index);
1849  //$out =& call_user_func( array(&$delegate, "field__$fieldname"), $this, $index);
1850  //} else if ( array_key_exists($fieldname, $this->_transientValues) ){
1851  } else if ( array_key_exists($fieldname, $this->_table->transientFields()) ){
1852  $transientFields =& $this->_table->transientFields();
1853  if ( array_key_exists( $fieldname, $this->_transientValues) ){
1854  $out = $this->_transientValues[$fieldname];
1855  } else if ( isset($transientFields[$fieldname]['relationship']) and $transientFields[$fieldname]['widget']['type'] == 'grid'){
1856  $out= array();
1857  $rrecords =& $this->getRelatedRecordObjects($transientFields[$fieldname]['relationship'], 'all');
1858  $currRelationship =& $this->_table->getRelationship($transientFields[$fieldname]['relationship']);
1859  $relKeys =& $currRelationship->keys();
1860  //print_r(array_keys($currRelationship->keys()));
1861  foreach ($rrecords as $rrecord){
1862  $row = $rrecord->strvals();
1863 
1864  foreach ( array_keys($row) as $row_field ){
1865  $ptable =& $rrecord->_relationship->getTable($row_field);
1866  $precord =& $rrecord->toRecord($ptable->tablename);
1867  if ( !$precord or PEAR::isError($precord) ) continue;
1868  $row['__permissions__'][$row_field] = $precord->getPermissions(array('field'=>$row_field));
1869  if ( isset($relKeys[$row_field]) ) unset($row['__permissions__'][$row_field]['edit']);
1870  unset($precord);
1871  unset($ptable);
1872  }
1873  $row['__id__'] = $rrecord->getId();
1874 
1875  $out[] = $row;
1876  unset($rrecord);
1877  unset($row);
1878  }
1879  unset($relKeys);
1880  unset($currRelationship);
1881  unset($rrecords);
1882  $this->_transientValues[$fieldname] = $out;
1883  } else if ( isset($transientFields[$fieldname]['relationship']) and $transientFields[$fieldname]['widget']['type'] == 'checkbox'){
1884  $out= array();
1885  $rrecords =& $this->getRelatedRecordObjects($transientFields[$fieldname]['relationship'], 'all');
1886  $currRelationship =& $this->_table->getRelationship($transientFields[$fieldname]['relationship']);
1887  foreach ($rrecords as $rrecord){
1888  $row = $rrecord->strvals();
1889  $domRec = $rrecord->toRecord();
1890 
1891  $rowstr = array();
1892  foreach (array_keys($domRec->_table->keys()) as $relKey){
1893  $rowStr[] = urlencode($relKey).'='.urlencode($row[$relKey]);
1894  }
1895  $out[] = implode('&',$rowStr);
1896 
1897  unset($rowStr, $domRec);
1898  unset($rrecord);
1899  unset($row);
1900  }
1901  unset($relKeys);
1902  unset($currRelationship);
1903  unset($rrecords);
1904  $this->_transientValues[$fieldname] = $out;
1905  } else {
1906 
1907  if ( isset($delegate) and method_exists($delegate, $fieldname.'__init') ){
1908  $methodname = $fieldname.'__init';
1909  $out = $delegate->$methodname($this);
1910  if ( isset($out) ){
1911  $this->_transientValues[$fieldname] = $out;
1912  }
1913  }
1914 
1915  if ( !isset($out) ){
1916  $methodname = 'initTransientField';
1918  $appdel = $app->getDelegate();
1919  if ( isset($appdel) and method_exists($appdel, $methodname) ){
1920  $out = $appdel->$methodname($this, $transientFields[$fieldname]);
1921  if ( isset($out) ){
1922  $this->_transientValues[$fieldname] = $out;
1923  }
1924  }
1925 
1926 
1927  }
1928 
1929  if ( !isset($out) ){
1930  $event = new StdClass;
1931  $event->record = $this;
1932  $event->field = $transientFields[$fieldname];
1933  $out = null;
1934 
1935 
1937  $app->fireEvent('initTransientField', $event);
1938 
1939 
1940  $out = @$event->out;
1941  if ( isset($out) ){
1942  $this->_transientValues[$fieldname] = $out;
1943  }
1944  }
1945 
1946 
1947 
1948  $out = null;
1949  }
1950 
1951  } else if ( ( $parent =& $this->getParentRecord() ) and $parent->_table->hasField($fieldname) ){
1952 
1953  return $parent->getValue($fieldname,$index,$where,$sort,$debug);
1954  } else {
1955  $this->_values[$fieldname] = null;
1956  $out = null;
1957  }
1958  } else {
1959  $out = $this->_values[$fieldname];
1960  }
1961  if ( isset($out) ){
1962  // We only store non-null values in cache. We were having problems
1963  // with segfaulting in PHP5 when groups are used.
1964  // This seems to fix the issue, but let's revisit it later.
1965  $this->cache[strval(__FUNCTION__)][strval($fieldname)][$index][$where][$sort] = $out;
1966  }
1967  return $out;
1968  } else {
1969  list($relationship, $fieldname) = explode('.', $fieldname);
1970 
1971  $rec =& $this->getRelatedRecords($relationship, false, $index, 1, $where, $sort);
1972  $this->cache[__FUNCTION__][$relationship.'.'.$fieldname][$index][$where][$sort] =& $rec[$fieldname];
1973  return $rec[$fieldname];
1974 
1975 
1976  }
1977  }
1978 
1979 
1980 
1981 
1987  function &value($fieldname, $index=0, $where=0, $sort=0){
1988  $val =& $this->getValue($fieldname, $index,$where, $sort);
1989  return $val;
1990  }
1991 
1996  function &val($fieldname, $index=0, $where=0, $sort=0){
1997  $val =& $this->getValue($fieldname, $index, $where, $sort);
1998  return $val;
1999  }
2000 
2069  function &getValues($fields = null, $index=0, $where=0, $sort=0){
2070  if ( !isset( $this->_values ) ) $this->_values = array();
2071  $values = array();
2072  $fields = ( $fields === null ) ? array_keys($this->_table->fields(false,true)) : $fields;
2073  foreach ($fields as $field){
2074  $values[$field] =& $this->getValue($field, $index, $where, $sort);
2075  }
2076 
2077  return $values;
2078  }
2079 
2085  function &values($fields = null, $index=0, $where=0, $sort=0){
2086  $vals =& $this->getValues($fields, $index, $where, $sort);
2087  return $vals;
2088  }
2089 
2094  function &vals($fields = null, $index=0, $where=0, $sort=0){
2095  $vals =& $this->getValues($fields, $index, $where, $sort);
2096  return $vals;
2097  }
2098 
2185  function getValueAsString($fieldname, $index=0, $where=0, $sort=0){
2186  //return $this->_table->getValueAsString($fieldname, $this->getValue($fieldname), $index);
2187 
2188  $value = $this->getValue($fieldname, $index, $where, $sort);
2189 
2190 
2191  $table =& $this->_table->getTableTableForField($fieldname);
2192  $delegate =& $table->getDelegate();
2193  $rel_fieldname = $table->relativeFieldName($fieldname);
2194  if ( $delegate !== null and method_exists( $delegate, $rel_fieldname.'__toString') ){
2195  $methodname = $rel_fieldname.'__toString';
2196  $value = $delegate->$methodname($value); //call_user_func( array(&$delegate, $rel_fieldname.'__toString'), $value);
2197  } else
2198 
2199 
2200  if ( !is_scalar($value) ){
2201  $methodname = $this->_table->getType($fieldname)."_to_string";
2202  if ( method_exists( $this->_table, $methodname) ){
2203 
2204  $value = $this->_table->$methodname($value); //call_user_func( array( &$this->_table, $this->_table->getType($fieldname)."_to_string"), $value );
2205  } else {
2206  $value = $this->array2string($value);
2207 
2208  }
2209  }
2210 
2211  else if ( ( $parent =& $this->getParentRecord() ) and $parent->_table->hasField($fieldname) ){
2212  return $parent->getValueAsString($fieldname, $index, $where, $sort);
2213  }
2214 
2215  $evt = new stdClass;
2216  $evt->table = $table;
2217  $evt->field =& $table->getField($rel_fieldname);
2218  $evt->value = $value;
2219  $evt->type = $table->getType($rel_fieldname);
2220  $table->app->fireEvent('after_getValueAsString', $evt);
2221  $value = $evt->value;
2222 
2223  return $value;
2224  }
2225 
2229  function array2string($value){
2230  if ( is_string($value) ) return $value;
2231  if ( is_array($value) ){
2232  if ( count($value) > 0 and is_array($value[0]) ){
2233  $delim = "\n";
2234  } else {
2235  $delim = ', ';
2236  }
2237  return implode($delim, array_map(array(&$this,'array2string'), $value));
2238  }
2239  return '';
2240  }
2241 
2242 
2270  function getValuesAsStrings($fields='', $index=0, $where=0, $sort=0){
2271  $keys = is_array($fields) ? $fields : array_keys($this->_table->fields(false,true));
2272  $values = array();
2273  foreach ($keys as $key){
2274  $values[$key] = $this->getValueAsString($key, $index, $where, $sort);
2275  }
2276  return $values;
2277  }
2278 
2282  function strvals($fields='', $index=0, $where=0, $sort=0){
2283  return $this->getValuesAsStrings($fields, $index, $where, $sort);
2284  }
2285 
2286 
2290  function strval($fieldname, $index=0, $where=0, $sort=0){
2291  return $this->getValueAsString($fieldname, $index, $where, $sort);
2292  }
2293 
2294 
2298  function stringValue($fieldname, $index=0, $where=0, $sort=0){
2299  return $this->getValueAsString($fieldname, $index, $where, $sort);
2300  }
2301 
2302 
2317  function getSerializedValue($fieldname, $index=0, $where=0, $sort=0){
2318  $s = $this->_table->getSerializer();
2319  return $s->serialize($fieldname, $this->getValue($fieldname, 0, $where, $sort));
2320 
2321  }
2322 
2323 
2493  function display($fieldname, $index=0, $where=0, $sort=0, $urlencode=false){
2494  if ( isset($this->cache[__FUNCTION__][$fieldname][$index][$where][$sort]) ){
2495  return $this->cache[__FUNCTION__][$fieldname][$index][$where][$sort];
2496  }
2497  if ( strpos($fieldname,'.') === false ){
2498  // this is not a related field.
2499  if ( $this->secureDisplay and !Dataface_PermissionsTool::view($this, array('field'=>$fieldname)) ){
2500  $del =& $this->_table->getDelegate();
2501  if ( $del and method_exists($del, 'no_access_text') ){
2502  return $del->no_access_text($this, array('field'=>$fieldname));
2503  } else {
2504  return 'NO ACCESS';
2505  }
2506  }
2507  } else {
2508  list($relationship,$fieldname) = explode('.',$fieldname);
2509  $rrecord =& $this->getRelatedRecord($relationship,$index,$where,$sort);
2510  if ( !$rrecord ) return null;
2511  $out = $rrecord->display($fieldname);
2512  $this->cache[__FUNCTION__][$fieldname][$index][$where][$sort] = $out;
2513  return $out;
2514  }
2515 
2516 
2517 
2518  $table =& $this->_table->getTableTableForField($fieldname);
2519 
2520 
2521  $delegate =& $this->_table->getDelegate();
2522  if ( $delegate !== null and method_exists( $delegate, $fieldname."__display") ){
2523  $methodname = $fieldname."__display";
2524  $out = $delegate->$methodname($this);
2525  //$out = call_user_func(array(&$delegate, $fieldname."__display"), $this);
2526  $this->cache[__FUNCTION__][$fieldname][$index][$where][$sort] = $out;
2527  return $out;
2528 
2529  }
2530 
2531  $field =& $this->_table->getField($fieldname);
2532  if ( $this->_table->isBlob($fieldname) or ($this->_table->isContainer($fieldname) and @$field['secure']) ){
2533 
2534  unset($table);
2535  $table =& Dataface_Table::loadTable($field['tablename']);
2536  $keys = array_keys($table->keys());
2537  $qstr = '';
2538  foreach ($keys as $key){
2539  $qstr .= "&$key"."=".$this->strval($key,$index,$where,$sort);
2540  }
2541  $out = DATAFACE_SITE_HREF."?-action=getBlob&-table=".$field['tablename']."&-field=$fieldname&-index=$index$qstr";
2542  $this->cache[__FUNCTION__][$fieldname][$index][$where][$sort] = $out;
2543  return $out;
2544  }
2545 
2546  else if ( $this->_table->isContainer($fieldname) ){
2547  $field =& $this->_table->getField($fieldname);
2548  $strvl=$this->strval($fieldname,$index,$where,$sort);
2549  if ($urlencode)
2550  {
2551  $strvl=rawurlencode($strvl);
2552  }
2553  $out = $field['url'].'/'.$strvl;
2554  if ( strlen($out) > 1 and $out{0} == '/' and $out{1} == '/' ){
2555  $out = substr($out,1);
2556  }
2557  $this->cache[__FUNCTION__][$fieldname][$index][$where][$sort] = $out;
2558  return $out;
2559  }
2560 
2561  else { //if ( !$this->_table->isBlob($fieldname) ){
2562 
2563  $field =& $this->_table->getField($fieldname);
2564 
2565 
2566  if ( PEAR::isError($field) ){
2567  $field->addUserInfo("Failed to get field '$fieldname' while trying to display its value in Record::display()");
2568  return $field;
2569 
2570 
2571  }
2572  $vocab = $field['vocabulary'];
2573  if ( $vocab ){
2574  $valuelist =& $table->getValuelist($vocab);
2575  }
2576  $value = $this->getValue($fieldname, $index, $where, $sort);
2577  if ( PEAR::isError($value) ) return '';
2578  if ( isset($valuelist) && !PEAR::isError($valuelist) ){
2579  if ( $field['repeat'] and is_array($value) ){
2580  $out = "";
2581  foreach ($value as $value_item){
2582  if ( isset( $valuelist[$value_item] ) ){
2583  $out .= $valuelist[$value_item].', ';
2584  } else {
2585  $out .= $value_item.', ';
2586  }
2587  }
2588  if ( strlen($out) > 0 ) $out = substr($out, 0, strlen($out)-2);
2589  $this->cache[__FUNCTION__][$fieldname][$index][$where][$sort] = $out;
2590  return $out;
2591  }
2592 
2593  //else if ( isset( $valuelist[$value]) ){
2594  else {
2595  if ( is_array($value) ) $value = $this->strval($fieldname, $index, $where, $sort);
2596  if ( isset($valuelist[$value]) ){
2597  $out = $valuelist[$value];
2598  $this->cache[__FUNCTION__][$fieldname][$index][$where][$sort] = $out;
2599  return $out;
2600  } else {
2601  return $value;
2602  }
2603  }
2604  } else {
2605  $parent =& $this->getParentRecord();
2606 
2607 
2608 
2609  if ( isset($parent) and $parent->_table->hasField($fieldname) ){
2610 
2611  return $parent->display($fieldname, $index, $where, $sort);
2612  }
2613  $out = $this->getValueAsString($fieldname, $index, $where, $sort);
2614 
2615 
2616  $out = $table->format($fieldname, $out);
2617 
2618 
2619  // Let's pass the value through an event filter to give modules
2620  // a crack at the final output.
2621  $evt = new stdClass;
2622  $evt->record = $this;
2623  $evt->field =& $field;
2624  $evt->value = $out;
2625  $table->app->fireEvent('Record::display', $evt);
2626  $out = $evt->value;
2627 
2628 
2629 
2630  $this->cache[__FUNCTION__][$fieldname][$index][$where][$sort] = $out;
2631  return $out;
2632  }
2633 
2634 
2635  //return $this->_table->display($fieldname, $this->getValue($fieldname, $index));
2636  }
2637 
2638 
2639  }
2640 
2641 
2642 
2643 
2674  function htmlValue($fieldname, $index=0, $where=0, $sort=0,$params=array()){
2675  $recid = $this->getId();
2676  $uri = $recid.'#'.$fieldname;
2677  $domid = $uri.'-'.rand();
2678 
2679 
2680 
2681  $delegate =& $this->_table->getDelegate();
2682  if ( isset($delegate) && method_exists($delegate, $fieldname.'__htmlValue') ){
2683  $methodname = $fieldname.'__htmlValue';
2684  $res = $delegate->$methodname($this);
2685  //$res = call_user_func(array(&$delegate, $fieldname.'__htmlValue'), $this);
2686  if ( is_string($res) and DATAFACE_USAGE_MODE == 'edit' and $this->checkPermission('edit', array('field'=>$fieldname)) and !$this->_table->isMetaField($fieldname) ){
2687  $res = '<span id="'.$domid.'" df:id="'.$uri.'" class="df__editable">'.$res.'</span>';
2688  }
2689  return $res;
2690  }
2691  $parent =& $this->getParentRecord();
2692  if ( isset($parent) and $parent->_table->hasField($fieldname) ){
2693  return $parent->htmlValue($fieldname, $index, $where, $sort, $params);
2694  }
2695  $val = $this->display($fieldname, $index, $where, $sort);
2696  $field = $this->_table->getField($fieldname);
2697  if ( !@$field['passthru'] and $this->escapeOutput) $val = nl2br(htmlspecialchars($val));
2698  if ( $this->secureDisplay and !Dataface_PermissionsTool::view($this, array('field'=>$fieldname)) ){
2699  $del =& $this->_table->getDelegate();
2700  if ( $del and method_exists($del, 'no_access_link') ){
2701  $link = $del->no_access_link($this, array('field'=>$fieldname));
2702  return '<a href="'.htmlspecialchars($link).'">'.$val.'</a>';
2703  }
2704  }
2705 
2706 
2707  //if ( $field['widget']['type'] != 'htmlarea' ) $val = htmlentities($val,ENT_COMPAT, 'UTF-8');
2708  //if ( $this->_table->isText($fieldname) and $field['widget']['type'] != 'htmlarea' and $field['contenttype'] != 'text/html' ) $val = nl2br($val);
2709 
2710  if ( $this->_table->isBlob($fieldname) or $this->_table->isContainer($fieldname) ){
2711  if ( $this->getLength($fieldname, $index,$where,$sort) > 0 ){
2712  if ( $this->isImage($fieldname, $index, $where, $sort) ){
2713  $val = '<img src="'.$val.'"';
2714  if ( !isset($params['width']) and isset($field['width']) ){
2715  $params['width'] = $field['width'];
2716  }
2717  foreach ($params as $pkey=>$pval){
2718  $val .= ' '.$pkey.'="'.$pval.'"';
2719  }
2720  $val .= '/>';
2721  } else {
2722  $file_icon = df_translate(
2723  $this->getMimetype($fieldname,$index,$where,$sort).' file icon',
2724  df_absolute_url(DATAFACE_URL).'/images/document_icon.gif'
2725  );
2726  $val = '<img src="'.$file_icon.'"/><a href="'.$val.'" target="_blank"';
2727  foreach ($params as $pkey=>$pval){
2728  $val .= ' '.$pkey.'="'.$pval.'"';
2729  }
2730  $val .= '>View Field Content In New Window ('.$this->getMimetype($fieldname, $index,$where,$sort).')</a>';
2731  }
2732  } else {
2733  $val = "(Empty)";
2734  }
2735  }
2736  if ( is_string($val) and DATAFACE_USAGE_MODE == 'edit' and $this->checkPermission('edit', array('field'=>$fieldname)) and !$this->_table->isMetaField($fieldname)){
2737  $val = '<span id="'.$domid.'" df:id="'.$uri.'" class="df__editable">'.$val.'</span>';
2738  }
2739  return $val;
2740 
2741 
2742 
2743  }
2744 
2745 
2764  function preview($fieldname, $index=0, $maxlength=255, $where=0, $sort=0){
2765 
2766  $strval = strip_tags($this->display($fieldname,$index, $where, $sort));
2767  $field =& $this->table()->getField($fieldname);
2768  if ( $field['Type'] == 'container' ){
2769  $strval = strip_tags($this->val($fieldname, $index, $where, $sort));
2770  }
2771  $out = substr($strval, 0, $maxlength);
2772  if ( strlen($strval)>$maxlength) {
2773  $out .= '...';
2774  }
2775  return $out;
2776 
2777  }
2778 
2801  function thumbnail($fieldname, $params='', $index=0, $where=0, $sort=0){
2802  if ( is_array($params) ) $params = http_build_query($params);
2803  if ( !$params ) $params = 'max_width=75&max_height=100';
2804  $out = $this->display($fieldname, $index, $where, $sort);
2805  if ( strpos($out, '?') === false ) $out .= '?';
2806  else $out .= '&';
2807  $out .= $params;
2808  return $out;
2809 
2810  }
2811 
2817  function printValue($fieldname, $index=0, $where=0, $sort=0 ){
2818  return $this->display($fieldname, $index, $where, $sort);
2819  }
2820 
2826  function printval($fieldname, $index=0, $where=0, $sort=0){
2827  return $this->display($fieldname, $index, $where, $sort);
2828  }
2829 
2835  function q($fieldname, $index=0, $where=0, $sort=0){
2836  return $this->display($fieldname, $index, $where, $sort);
2837  }
2838 
2845  function qq($fieldname, $index=0, $where=0, $sort=0){
2846  return $this->htmlValue($fieldname, $index, $where, $sort);
2847  }
2848 
2865  return (isset( $this->_values) and array_key_exists($fieldname, $this->_values) );
2866  }
2867 
2868 
2899  function getAbsoluteValues(){
2900  $values = $this->getValues();
2901  $absValues = array();
2902  foreach ( $values as $key=>$value){
2903  $absValues[$this->_table->tablename.".".$key] = $value;
2904  }
2905  return $absValues;
2906  }
2907 
2908 
2909 
2934  $filename = $this->strval($fieldname);
2935  if ( strlen($filename)===0 ){
2936  return null;
2937  }
2938  $field =& $this->_table->getField($fieldname);
2939  return $field['savepath'].'/'.$filename;
2940 
2941  }
2942 
2943 
2960  function setMetaDataValue($key, $value){
2961  if ( !isset( $this->_metaDataValues ) ) $this->_metaDataValues = array();
2962  $this->_metaDataValues[$key] = $value;
2963  $parent =& $this->getParentRecord();
2964  if ( isset($parent) ){
2965  $parent->setMetaDataValue($key, $value);
2966  }
2967  }
2968 
2969 
2970 
2971 
2972 
2973 
2974 
2980  function clearValues(){
2981  $this->_values = array();
2982  $this->_relatedValues = array();
2983  $this->_valCache = array();
2984 
2985  $this->clearFlags();
2986 
2987  $parent =& $this->getParentRecord();
2988  if ( isset($parent) ){
2989  $parent->clearValues();
2990  }
2991 
2992  }
2993 
3001  function clearValue($field){
3002 
3003  unset($this->_values[$field]);
3004  $this->clearFlag($field);
3005 
3006  $parent =& $this->getParentRecord();
3007  if ( isset($parent) ){
3008  $parent->clearValue();
3009  }
3010  }
3011 
3012 
3013 
3014  // @}
3015  // End Field Values Methods
3016  //-------------------------------------------------------------------------------
3017 
3018 
3019  // @{
3053  function addPropertyChangeListener($key, &$listener){
3054  $this->propertyChangeListeners[$key][] = &$listener;
3055  }
3056 
3071  function removePropertyChangeListener($key, &$listener){
3072  if ( !isset($key) ) $key = '*';
3073  if ( !isset($callback) ) unset($this->propertyChangeListeners[$key]);
3074  else if ( isset($this->propertyChangeListeners[$key]) ){
3075  if ( ($index = array_search($listener,$this->propertyChangeListeners[$key])) !== false){
3076  unset($this->propertyChangeListeners[$key][$index]);
3077  }
3078  }
3079  }
3080 
3095  function firePropertyChangeEvent($key, $oldValue, $newValue){
3096  $origKey = $key;
3097  $keys = array('*');
3098  if ( isset($key) ) $keys[] = $key;
3099  foreach ($keys as $key){
3100  if ( !isset($this->propertyChangeListeners[$key] ) ) continue;
3101  foreach ( array_keys($this->propertyChangeListeners[$key]) as $lkey){
3102  $this->propertyChangeListeners[$key][$lkey]->propertyChanged($this,$origKey, $oldValue, $newValue);
3103 
3104  }
3105  }
3106 
3107 
3108  }
3109 
3127  function propertyChanged(&$source, $key, $oldValue, $newValue){
3128  $parentRecord =& $this->getParentRecord();
3129  if ( is_a($source, 'Dataface_Record') and is_a($parentRecord, 'Dataface_Record') and $source->_id === $parentRecord->_id ){
3130  $pkeys = $source->_table->keys();
3131  $pkey_names = array_keys($pkeys);
3132  $okeys = $this->_table->keys();
3133  $okey_names = array_keys($okeys);
3134 
3135  if ( !array_key_exists($key, $pkeys) ) return false;
3136  // The field that was changed was not a key so we don't care
3137 
3138  $key_index = array_search($key, $pkey_names);
3139  if ( $key_index === false ) throw new Exception("An error occurred trying to find the index of the parent's key. This is a code error that should be fixded by the developer.", E_USER_ERROR);
3140 
3141 
3142  if ( !isset($okey_names[$key_index]) )
3143  throw new Exception("Attempt to keep the current record in sync with its parent but they seem to have a different number of primary keys. To use Dataface inheritance, tables must have a corresponding primary key.", E_USER_ERROR);
3144 
3145 
3146  $this->setValue( $okey_names[$key_index], $newValue);
3147  }
3148  }
3149 
3150 
3151 
3152 
3153  // @}
3154  // END Property Change Events
3155  //-------------------------------------------------------------------------------------
3156 
3157  // @{
3189  function getJoinRecord($tablename, $nullIfNotFound=false){
3191  $query = $this->getJoinKeys($tablename);
3192  foreach ( $query as $key=>$val ){
3193  $query[$key] = '='.$val;
3194  }
3195 
3196  $record = df_get_record($tablename, $query);
3197  if ( !$record ){
3198  if ( $nullIfNotFound ) return null;
3199  // No record was found, so we create a new one.
3200  $record = new Dataface_Record($tablename, array());
3201  foreach ( $query as $key=>$value){
3202  $record->setValue($key, substr($value,1));
3203  }
3204  }
3205  return $record;
3206 
3207  }
3208 
3209 
3236  $query = array();
3237 
3238  $pkeys1 = array_keys($this->_table->keys());
3239  $pkeys2 = array_keys($table->keys());
3240 
3241  if ( count($pkeys1) != count($pkeys2) ){
3242  return PEAR::raiseError("Attempt to get join record [".$this->_table->tablename."] -> [".$table->tablename."] but they have a different number of columns as primary key.");
3243  }
3244 
3245  for ($i =0; $i<count($pkeys1); $i++ ){
3246  $query[$pkeys2[$i]] = $this->strval($pkeys1[$i]);
3247  }
3248 
3249  return $query;
3250 
3251  }
3252 
3261  function tabs(){
3262  return $this->_table->tabs($this);
3263  }
3264 
3265  // @}
3266  // END TAB Management
3267 
3268 
3269  //---------------------------------------------------------------------------
3270  // @{
3295  function getRoles($params=array()){
3296  return $this->_table->getRoles($this, $params);
3297 
3298  }
3299 
3306  function getRolePermissions($params=array()){
3307  return $this->_table->getRolePermissions($this, $params);
3308  }
3309 
3310 
3396  function getPermissions($params=array()){
3397  $params['record'] =& $this;
3398  return $this->_table->getPermissions($params);
3399  }
3400 
3410  function checkPermission($perm, $params=array()){
3411  $perms = $this->getPermissions($params);
3412  return ( isset($perms[$perm]) and $perms[$perm] );
3413  }
3414 
3415 
3416 
3417  // @}
3418  // END Permissions
3419  //----------------------------------------------------------------------------------
3420 
3446  function validate( $fieldname, $value, &$params){
3447  //if ( func_num_args() > 2 ){
3448  // $params =& func_get_arg(2);
3449  //}
3450  //else {
3451  // $params = array();
3452  //}
3453 
3454  if ( !is_array($params) ){
3455  $params = array('message'=> &$params);
3456  }
3457  $res = $this->_table->validate($fieldname, $value, $params);
3458 
3459  $field =& $this->_table->getField($fieldname);
3460 
3461  if ( $field['widget']['type'] == 'file' and @$field['validators']['required'] and is_array($value) and $this->getLength($fieldname) == 0 and !is_uploaded_file(@$value['tmp_name'])){
3462  // This bit of validation operates on the upload values assuming the file was just uploaded as a form. It assumes
3463  // that $value is of the form
3465  $params['message'] = "$fieldname is a required field.";
3466  $params['message_i18n_id'] = "Field is a required field";
3467  $params['message_i18n_params'] = array('field'=>$fieldname);
3468  return false;
3469  }
3470  if ( $res ){
3471  $delegate =& $this->_table->getDelegate();
3472  if ( $delegate !== null and method_exists($delegate, $fieldname."__validate") ){
3473  /*
3474  *
3475  * The delegate defines a custom validation method for this field. Use it.
3476  *
3477  */
3478  $methodname = $fieldname."__validate";
3479  $res = $delegate->$methodname($this,$value,$params);
3480  //$res = call_user_func(array(&$delegate, $fieldname."__validate"), $this, $value, $params);
3481  }
3482 
3483 
3484  }
3485 
3486  if ($res){
3487  $parent =& $this->getParentRecord();
3488  if ( isset($parent) and $parent->_table->hasField($fieldname) ){
3489  $res = $parent->validate($fieldname, $value, $params);
3490  }
3491  }
3492 
3493  return $res;
3494 
3495 
3496  }
3497 
3498 
3510  function &getParentRecord(){
3511  if ( !isset($this->_parentRecord) ){
3512  $parent =& $this->_table->getParent();
3513  if ( isset($parent) ){
3514  $this->_parentRecord = new Dataface_Record($parent->tablename, array());
3515  foreach ( array_keys($parent->keys()) as $key ){
3516  $this->_parentRecord->addPropertyChangeListener( $key, $this);
3517  }
3518  }
3519  }
3520  return $this->_parentRecord;
3521 
3522  }
3523 
3524 
3525  //------------------------------------------------------------------------------------
3526  // @{
3540  function setSnapshot(){
3541 
3542  $this->clearFlags();
3543  if ( isset($this->_values) ){
3544  // If there are no values, then we don't need to set the snapshot
3545  $this->_oldValues = $this->getValues();
3546  }
3547  $parent =& $this->getParentRecord();
3548  if ( isset($parent) ){
3549  $parent->setSnapshot();
3550  }
3551 
3552  return $this;
3553 
3554  }
3555 
3564  function snapshotExists(){
3565  return (is_array($this->_oldValues) and count($this->_oldValues) > 0);
3566  }
3567 
3576  function clearSnapshot(){
3577  $this->_oldValues = null;
3578  $parent =& $this->getParentRecord();
3579  if ( isset($parent) ){
3580  $parent->clearSnapshot();
3581  }
3582  return $this;
3583  }
3584 
3594  function &getSnapshot($fields=''){
3595  if ( is_array($fields) ){
3596  $out = array();
3597  foreach ($fields as $field){
3598  if ( isset( $this->_oldValues[$field] ) ){
3599  $out[$field] = $this->_oldValues[$field];
3600  }
3601  }
3602 
3603  return $out;
3604  } else {
3605  return $this->_oldValues;
3606  }
3607  }
3608 
3617  function snapshotKeys(){
3618  return $this->getSnapshot(array_keys($this->_table->keys()));
3619  }
3620 
3632  function valueChanged($fieldname, $index=0, $checkParent=false){
3633  if ( strpos($fieldname, '.') !== false ){
3634  // This is a related field, so we have to check the relationship for dirty flags
3635  $path = explode('.', $fieldname);
3636 
3637  if ( is_array($index) ){
3638  $index = $this->getRelatedIndex($index);
3639  }
3640 
3641  return (isset( $this->_relatedDirtyFlags[$path[0]]) and
3642  isset( $this->_relatedDirtyFlags[$path[0]][$path[1]]) and
3643  $this->_relatedDirtyFlags[$path[0]][$path[1]] === true );
3644  } else {
3645  // this is a local related field... just check the local dirty flags array.
3646  if ( $checkParent ){
3647  $parent =& $this->getParentRecord();
3648  if ( isset($parent) and $parent->_table->hasField($fieldname) ){
3649  return $parent->valueChanged($fieldname, $index);
3650  }
3651  }
3652  return (@$this->_dirtyFlags[$fieldname]);
3653  }
3654  }
3655 
3656 
3665  function recordChanged($checkParent=false){
3666  if ($checkParent){
3667  $parent =& $this->getParentRecord();
3668  if ( isset($parent) ){
3669  $res = $parent->recordChanged();
3670  if ( $res ) return true;
3671  }
3672  }
3673 
3674  $fields =& $this->_table->fields();
3675  foreach ( array_keys( $fields) as $fieldname){
3676  if ( $this->valueChanged($fieldname) ) return true;
3677  }
3678  return false;
3679  }
3680 
3681 
3682 
3690  function clearFlags(){
3691  $keys = array_keys($this->_dirtyFlags);
3692  foreach ( $keys as $i) {
3693  $this->_dirtyFlags[$i] = false;
3694  $this->vetoFields[$i] = false;
3695  }
3696  foreach (array_keys($this->_relatedDirtyFlags) as $rel_name){
3697  foreach ( array_keys($this->_relatedDirtyFlags[$rel_name]) as $field_name){
3698  $this->_relatedDirtyFlags[$rel_name][$field_name] = false;
3699  }
3700  }
3701 
3702  // Clear the snapshot of old values.
3703  $this->clearSnapshot();
3704 
3705  $parent =& $this->getParentRecord();
3706  if ( isset($parent) ){
3707  $parent->clearFlags();
3708  }
3709  return $this;
3710 
3711  }
3712 
3724  function clearFlag($name){
3725  if ( strpos($name, '.') !== false ){
3726  // This is a related field. We store dirty flags in the relationship array.
3727  $path = explode('.', $name);
3728 
3729  if ( !isset($this->_relatedDirtyFlags[$path[0]]) ){
3730  return;
3731  }
3732  if ( !isset($this->_relatedDirtyFlags[$path[0]][$path[1]]) ){
3733  return;
3734  }
3735  $this->_relatedDirtyFlags[$path[0]][$path[1]] = false;
3736  } else {
3737 
3738  $this->_dirtyFlags[$name] = false;
3739  $this->vetoFields[$name] = false;
3740  $parent =& $this->getParentRecord();
3741  if ( isset($parent) and $parent->_table->hasField($name) ){
3742  $parent->clearFlag($name);
3743  }
3744  }
3745 
3746  return $this;
3747  }
3748 
3749 
3761  function setFlag($fieldname, $index=0){
3762  if ( strpos($fieldname, '.') !== false ){
3763  // This is a related field. We store dirty flags in the relationship array.
3764  $path = explode('.', $fieldname);
3765 
3766 
3767  if ( !isset($this->_relatedDirtyFlags[$path[0]]) ){
3768  $this->_relatedDirtyFlags[$path[0]] = array();
3769  }
3770  $this->_relatedDirtyFlags[$path[0]][$path[1]] = true;
3771  } else {
3772  // This is a local field
3773  $this->_dirtyFlags[$fieldname] = true;
3774  $parent =& $this->getParentRecord();
3775  if ( isset($parent) and $parent->_table->hasField($fieldname)){
3776  $parent->setFlag($fieldname, $index);
3777  }
3778  }
3779  }
3780 
3781 
3790  $parent =& $this->getParentRecord();
3791  if ( isset($parent) and $parent->_table->hasField($fieldname) ){
3792  return $parent->isLoaded($fieldname);
3793  }
3794  return ( isset( $this->_isLoaded[$fieldname] ) and $this->_isLoaded[$fieldname]);
3795  }
3796 
3797 
3798  // @}
3799  // END OF TRANSACTIONS
3800  //-----------------------------------------------------------------------------------------
3801 
3810  function getLink($fieldname){
3811 
3812  $field =& $this->_table->getField($fieldname);
3813  if ( PEAR::isError($field) ){
3814  return null;
3815  }
3816  $table =& Dataface_Table::loadTable($field['tablename']);
3817  $delegate =& $table->getDelegate();
3818  if ( !$table->hasField($fieldname) ) return null;
3819 
3820 
3821 
3822  // Case 1: Delegate is defined -- we use the delegate's link
3823  if ( method_exists($delegate, $fieldname."__link") ){
3824  $methodname = $fieldname."__link";
3825  $link = $delegate->$methodname($this);
3826  //$link = call_user_func(array(&$delegate, $fieldname."__link"), $this);
3827 
3828 
3829  // Case 2: The link was specified in an ini file.
3830  } else if ( isset($field['link']) ){
3831 
3832  $link = $field['link'];
3833 
3834  // Case 3: The link was not specified
3835  } else {
3836 
3837  $link = null;
3838  }
3839 
3840 
3841  if ( is_array($link) ){
3842  foreach ( array_keys($link) as $key){
3843  $link[$key] = $this->parseString($link[$key]);
3844  }
3845 
3846 
3847  return $link;
3848 
3849  } else if ( $link ){
3850  return $this->parseString($link);
3851  } else {
3852 
3853  return null;
3854  }
3855 
3856  }
3857 
3858  //-----------------------------------------------------------------------------------
3859  // @{
3870  function _getSubfield(&$fieldval, $path){
3871  if ( !is_array($fieldval) ){
3872  return PEAR::raiseError("_getSubfield() expects its first parameter to be an array.");
3873  }
3874  $path = explode(":",$path);
3875  $temp1 =& $fieldval[array_shift($path)];
3876  $temp2 =& $temp1;
3877  while ( sizeof($path) > 0 ){
3878  unset($temp1);
3879  $temp1 =& $temp2[array_shift($path)];
3880  unset($temp2);
3881  $temp2 =& $temp1;
3882  }
3883  return $temp2;
3884  }
3885 
3970  function getTitle($dontGuess=false){
3971  if ( !isset($this->_title) ){
3972  $delegate =& $this->_table->getDelegate();
3973  $title = null;
3974  if ( $delegate !== null and method_exists($delegate, 'getTitle') ){
3975 
3976  $title = $delegate->getTitle($this);
3977  } else {
3978 
3979  $parent =& $this->getParentRecord();
3980  if ( isset($parent) ){
3981  $title = $parent->getTitle(true);
3982  }
3983  }
3984 
3985  if ( $dontGuess ){
3986  if ( isset($title) ) $this->_title = $title;
3987  return $title;
3988  }
3989 
3990  if ( !isset($title) ){
3991  $fields =& $this->_table->fields();
3992  $found_title = false; // flag to specify that a specific title field has been found
3993  // declared by the 'title' flag in the fields.ini file.
3994 
3995  foreach (array_keys($fields) as $field_name){
3996  if ( isset($fields[$field_name]['title']) ){
3997  $title = $this->display($field_name);
3998  $found_title = true;
3999  }
4000  else if ( !isset($title) and $this->_table->isChar($field_name) ){
4001  $title = $this->display($field_name );
4002  }
4003  if ( $found_title) break;
4004  }
4005 
4006  if ( !isset( $title) ){
4007  $title = "Untitled ".$this->_table->getLabel()." Record";
4008  }
4009 
4010  }
4011  $this->_title = $title;
4012  }
4013 
4014  return $this->_title;
4015 
4016 
4017  }
4018 
4019 
4020 
4044  function getDescription(){
4045  if ( ($res = $this->callDelegateFunction('getDescription')) !== null ){
4046  return $res;
4047  } else if ( $descriptionField = $this->_table->getDescriptionField() ){
4048  return $this->htmlValue($descriptionField);
4049  } else {
4050  return '';
4051  }
4052 
4053  }
4054 
4077  function getCreated(){
4078  if ( $res = $this->callDelegateFunction('getCreated') ){
4079  return $res;
4080  } else if ( $createdField = $this->_table->getCreatedField() ){
4081  if ( strcasecmp($this->_table->getType($createdField),'timestamp') === 0 ){
4082  $date = $this->val($createdField);
4083  return strtotime($date['year'].'-'.$date['month'].'-'.$date['day'].' '.$date['hours'].':'.$date['minutes'].':'.$date['seconds']);
4084 
4085  }
4086  return strtotime($this->display($createdField));
4087  } else {
4088  return '';
4089  }
4090  }
4091 
4113  function getCreator(){
4114  if ( ($res = $this->callDelegateFunction('getCreator',-1)) !== -1 ){
4115  return $res;
4116  } else if ( $creatorField = $this->_table->getCreatorField() ){
4117  return $this->display($creatorField);
4118  } else {
4119  return '';
4120  }
4121  }
4122 
4123 
4145  function getLastModified(){
4146  if ( $res = $this->callDelegateFunction('getLastModified') ){
4147  return $res;
4148  } else if ( $lastModifiedField = $this->_table->getLastUpdatedField() ){
4149  if ( strcasecmp($this->_table->getType($lastModifiedField),'timestamp') === 0 ){
4150  $date = $this->val($lastModifiedField);
4151  return strtotime($date['year'].'-'.$date['month'].'-'.$date['day'].' '.$date['hours'].':'.$date['minutes'].':'.$date['seconds']);
4152 
4153  }
4154  $strtime = $this->strval($lastModifiedField);
4155  if ( $strtime){
4156  return strtotime($strtime);
4157  }
4158  }
4159 
4160  if ( !isset($this->pouch['__mtime']) ){
4161  $sql = "select mtime from dataface__record_mtimes where recordhash='".addslashes(md5($this->getId()))."'";
4162  try {
4163  try {
4164  $res = df_q($sql);
4165  } catch ( Exception $ex){
4167  $res = df_q($sql);
4168  }
4169  list($mtime) = mysql_fetch_row($res);
4170  @mysql_free_result($res);
4171  $this->pouch['__mtime'] = intval($mtime);
4172  } catch (Exception $ex){
4173  error_log("Failed SQL query $sql");
4174  $this->pouch['__mtime'] = 0;
4175  }
4176  }
4177  return $this->pouch['__mtime'];
4178  }
4179 
4180 
4181 
4205  function getBody(){
4206  if ( $res = $this->callDelegateFunction('getBody') ){
4207  return $res;
4208  } else if ( $bodyField = $this->_table->getBodyField() ){
4209  return $this->htmlValue($bodyField);
4210  } else {
4211  return '';
4212  }
4213  }
4214 
4271  function getPublicLink($params=null){
4272  if ( $res = $this->callDelegateFunction('getPublicLink') ){
4273  return $res;
4274  } else {
4275  return $this->getURL($params);
4276  }
4277 
4278  }
4279 
4280 
4316  function getBreadCrumbs(){
4317  $delegate =& $this->_table->getDelegate();
4318  if ( $delegate !== null and method_exists($delegate, 'getBreadCrumbs') ){
4319  return $delegate->getBreadCrumbs($this);
4320  }
4321 
4322 
4323  if ( ( $parent = $this->getParent() ) !== null ){
4324  $bc = $parent->getBreadCrumbs();
4325  $bc[$this->getTitle()] = $this->getURL( array('-action'=>'browse'));
4326  return $bc;
4327  }
4328 
4329 
4330  return array(
4331  $this->_table->getLabel() => Dataface_LinkTool::buildLink(array('-action'=>'list', '-table'=>$this->_table->tablename)),
4332  $this->getTitle() => $this->getURL(array('-action'=>'browse'))
4333  );
4334  }
4335 
4407  function getURL($params=array()){
4408  if ( is_string($params) ){
4409  $pairs = explode('&',$params);
4410  $params = array();
4411  foreach ( $pairs as $pair ){
4412  list($key,$value) = array_map('urldecode',explode('=', $pair));
4413  $params[$key] = $value;
4414  }
4415  }
4416  if ( $this->secureDisplay and !$this->checkPermission('link') ){
4417  $del =& $this->_table->getDelegate();
4418  if ( $del and method_exists($del, 'no_access_link')){
4419  return $del->no_access_link($this, $params);
4420  }
4421  }
4422 
4423  $params['-table'] = $this->_table->tablename;
4424  if ( !isset($params['-action']) ) $params['-action'] = 'browse';
4425  foreach (array_keys($this->_table->keys()) as $key){
4426  $params[$key] = '='.$this->strval($key);
4427  }
4428 
4429  $delegate =& $this->_table->getDelegate();
4430  if ( isset($delegate) and method_exists($delegate, 'getURL') ){
4431  $res = $delegate->getURL($this, $params);
4432  if ( $res and is_string($res) ) return $res;
4433  }
4434 
4435  import('Dataface/LinkTool.php');
4436  //$linkTool =& Dataface_LinkTool::getInstance();
4437 
4438  return Dataface_LinkTool::buildLink($params ,false);
4439  }
4440 
4441 
4447  function getId(){
4448  $keys = array_keys($this->_table->keys());
4449  $params=array();
4450  foreach ($keys as $key){
4451  $params[] = urlencode($key).'='.urlencode($this->strval($key));
4452  }
4453  return $this->_table->tablename.'?'.implode('&',$params);
4454  }
4455 
4456 
4457  // @}
4458  // END Record Metadata
4459  //--------------------------------------------------------------------------------------
4460 
4461 
4462  // @{
4478  function getMimetype($fieldname,$index=0,$where=0,$sort=0){
4479  $field =& $this->_table->getField($fieldname);
4480  if ( isset($field['mimetype']) and strlen($field['mimetype']) > 0 ){
4481  return $this->getValue($field['mimetype'], $index,$where,$sort);
4482  }
4483 
4484  if ( $this->_table->isContainer($fieldname) ){
4485  $filename = $this->strval($fieldname,$index,$where,$sort);
4486  if ( strlen($filename) > 0 ){
4487  $path = $field['savepath'].'/'.$filename;
4488  $mimetype='';
4489  //if(!extension_loaded('fileinfo')) {
4490  // @dl('fileinfo.' . PHP_SHLIB_SUFFIX);
4491  //}
4492  if(extension_loaded('fileinfo')) {
4493  $res = finfo_open(FILEINFO_MIME); /* return mime type ala mimetype extension */
4494  $mimetype = finfo_file($res, $path);
4495  } else if (function_exists('mime_content_type')) {
4496 
4497 
4498  $mimetype = mime_content_type($path);
4499 
4500  }
4501 
4502 
4503  return $mimetype;
4504  }
4505  }
4506  return '';
4507 
4508  }
4509 
4523  function isImage($fieldname, $index=0, $where=0, $sort=0){
4524  return preg_match('/^image/', $this->getMimetype($fieldname,$index,$where,$sort));
4525 
4526  }
4527 
4528 
4543  function getLength($fieldname, $index=0, $where=0, $sort=0){
4544  if ( strpos($fieldname, '.') !== false ){
4545 
4546  list($relname, $localfieldname) = explode('.',$fieldname);
4547  $record =& $this->getRelatedRecords($relname, false, $index, null, $where, $sort);
4548  $relatedRecord = new Dataface_RelatedRecord($this, $relname,$record);
4549  $relatedRecord->setValues($this->_relatedMetaValues[$relname][0][0][$index]);
4550  return $relatedRecord->getLength($localfieldname);
4551  //$key = '__'.$localfieldname.'_length';
4552  //if ( isset($record[$key]) ){
4553  // return $record[$key];
4554  //} else {
4555  // return null;
4556  //}
4557  } else {
4558  $key = '__'.$fieldname.'_length';
4559  if ( isset($this->_metaDataValues[$key] ) ){
4560  return $this->_metaDataValues[$key];
4561  } else {
4562  return strlen($this->getValueAsString($fieldname));
4563  }
4564  }
4565 
4566  }
4567 
4568 
4569 
4570  // @}
4571  // END Field Metadata
4572  //--------------------------------------------------------------------------------------
4573 
4574 
4575 
4576 
4577  //-------------------------------------------------------------------------------------
4578  // @{
4595  function save($lang=null, $secure=false){
4596  if ( !isset($lang) ) $lang = $this->lang;
4597  return df_save_record($this, $this->strvals(array_keys($this->_table->keys())), $lang, $secure);
4598  }
4599 
4607  function delete($secure=false){
4608  import('Dataface/IO.php');
4609  $io = new Dataface_IO($this->_table->tablename);
4610  return $io->delete($this, $secure);
4611 
4612  }
4613 
4614 
4615  // @}
4616  // END IO Methods
4617  //---------------------------------------------------------------------------------------
4618 
4619  function toJS($fields=null, $override = array()){
4620  $strvals = $this->strvals($fields);
4621  $out = array();
4622  foreach ( $strvals as $key=>$val){
4623  if ( $this->checkPermission('view', array('field'=>$key)) ){
4624  $out[$key] = $val;
4625 
4626  }
4627  }
4628  $out['__title__'] = $this->getTitle();
4629  //$out[] = "'__title__': '".addslashes($this->getTitle())."'";
4630  $out['__url__'] = $this->getURL();
4631  //$out[] = "'__url__': '".addslashes($this->getURL())."'";
4632  $out['__expandable'] = ($this->checkPermission('expandable')?1:0);
4633  //$out[] = "'__expandable__': ".($this->checkPermission('expandable')?1:0);
4634 
4635  foreach ($override as $k=>$v){
4636  $out[$k] = $v;
4637  }
4638 
4639  return json_encode($out);
4640  //return '{'.implode(',',$out).'}';
4641 
4642  }
4643 
4644  function getStdObject(){
4645  return new Dataface_Record_StdClass($this);
4646  }
4647 
4648 
4649 
4650 
4651 
4652 
4653 }
4654 
4659 
4661  var $_keys;
4663  function Dataface_RecordIterator($tablename, &$records){
4664  $this->_records =& $records;
4665  $this->_keys = array_keys($records);
4666  $this->_tablename = $tablename;
4667  $this->reset();
4668  }
4669 
4670  function &next(){
4671  $out = new Dataface_Record($this->_tablename, $this->_records[current($this->_keys)]);
4672  next($this->_keys);
4673  return $out;
4674  }
4675 
4676  function &current(){
4677  return new Dataface_Record($this->_tablename, $this->_records[current($this->_keys)]);
4678  }
4679 
4680  function reset(){
4681  return reset($this->_keys);
4682  }
4683 
4684  function hasNext(){
4685 
4686  return (current($this->_keys) !== false);
4687  }
4688 
4689 }
4690 
4691 
4699  var $_keys;
4700  var $_where;
4701  var $_sort;
4702  function Dataface_RelationshipIterator(&$record, $relationshipName, $start=null, $limit=null, $where=0, $sort=0){
4703  $this->_record =& $record;
4704  $this->_relationshipName = $relationshipName;
4705  $this->_where = $where;
4706  $this->_sort = $sort;
4707  if ( $start !== 'all' ){
4708 
4709  $this->_records =& $record->getRelatedRecords($relationshipName, true, $start, $limit, $where, $sort);
4710  } else {
4711  $this->_records =& $record->getRelatedRecords($relationshipName, 'all',$where, $sort);
4712  }
4713  if ( is_array($this->_records) ){
4714  $this->_keys = array_keys($this->_records);
4715  } else {
4716  $this->_keys = array();
4717  }
4718  }
4719 
4720  function &next(){
4721  $out =& $this->current();
4722  next($this->_keys);
4723  return $out;
4724  }
4725 
4726  function &current(){
4727  $rec = new Dataface_RelatedRecord($this->_record, $this->_relationshipName, $this->_records[current($this->_keys)]);
4728  $rec->setValues($this->_record->_relatedMetaValues[$this->_relationshipName][$this->_where][$this->_sort][current($this->_keys)]);
4729  return $rec;
4730  }
4731 
4732  function reset(){
4733  return reset($this->_keys);
4734  }
4735 
4736  function hasNext(){
4737  return (current($this->_keys) !== false);
4738  }
4739 }
4740 
4741 
4742 class Dataface_Record_StdClass extends StdClass {
4743  private $r;
4744 
4745  public function __construct(Dataface_Record $r){
4746  $this->r = $r;
4747  }
4748 
4749  public function __get($name){
4750  return $this->r->val($name);
4751  }
4752 
4753  public function __set($name, $value){
4754  $this->r->setValue($name, $value);
4755  }
4756 
4757  public function __unset($name){
4758  $this->r->setValue($name, null);
4759  }
4760 
4761 
4762 }
4763