![]() |
Xataface 2.0
Xataface Application Framework
|
00001 <?php 00002 /*------------------------------------------------------------------------------- 00003 * Xataface Web Application Framework 00004 * Copyright (C) 2005-2008 Web Lite Solutions Corp (shannah@sfu.ca) 00005 * 00006 * This program is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU General Public License 00008 * as published by the Free Software Foundation; either version 2 00009 * of the License, or (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00019 *------------------------------------------------------------------------------- 00020 */ 00061 class Dataface_RelatedRecord { 00062 00070 var $secureDisplay = true; 00071 00075 var $vetoSecurity; 00080 var $_record; 00081 00086 var $_relationshipName; 00087 00092 var $_relationship; 00093 00099 var $_values; 00100 00107 var $_metaDataValues; 00108 00117 var $_absoluteColumnNames; 00118 00122 var $_lockedFields=array(); 00127 var $_records; 00131 var $_dirtyFlags=array(); 00132 00136 var $cache=array(); 00137 00138 00146 function Dataface_RelatedRecord($record, $relationshipName, $values=null){ 00147 00148 if ( !is_a($record, 'Dataface_Record') ){ 00149 throw new Exception("Error in Dataface_RelatedRecord constructor. Expected first argument to be of type 'Dataface_Record' but received '".get_class($record)."'.", E_USER_ERROR); 00150 } 00151 $this->_record =& $record; 00152 $this->_relationshipName = $relationshipName; 00153 $this->_relationship =& $record->_table->getRelationship($relationshipName); 00154 if ( is_array($values) ){ 00155 $this->setValues($values); 00156 $this->clearFlags(); 00157 } 00158 } 00159 00160 00165 function _initValues(){ 00166 if ( !isset( $this->_values ) ){ 00167 $fkeys = $this->_relationship->getForeignKeyValues(); 00168 $this->_values = array(); 00169 $this->_absoluteColumnNames = array(); 00170 //$cols = $this->_relationship->_schema['columns']; 00171 $cols = $this->_relationship->fields(true); // we will get all fields - even grafted ones. 00172 foreach ($cols as $col){ 00173 list($table, $field) = explode('.', $col); 00174 $this->_values[$field] = null; 00175 if ( isset($this->_absoluteColumnNames[$field]) and 00176 Dataface_Table::loadTable($this->_relationship->getDomainTable())->hasField($field) ){ 00177 $this->_absoluteColumnNames[$field] = $this->_relationship->getDomainTable().'.'.$field; 00178 } else { 00179 $this->_absoluteColumnNames[$field] = $col; 00180 } 00181 00182 00183 /* 00184 * We want to check for locked fields. Locked fields are fields that *must* have a particular 00185 * value for the relationship to remain valid. 00186 */ 00187 if ( isset( $fkeys[$table][$field]) and is_scalar($fkeys[$table][$field]) and strpos($fkeys[$table][$field],'$') ===0 ){ 00188 $this->_lockedFields[$field] = $fkeys[$table][$field]; 00189 $this->_values[$field] = $this->_record->parseString($fkeys[$table][$field]); 00190 } 00191 } 00192 } 00193 } 00194 00195 //--------------------------------------------------------------------------------- 00196 //{@ 00197 00210 function clearCache(){ 00211 $this->cache = array(); 00212 return $this; 00213 } 00214 00215 00224 function &toRecord($tablename=null){ 00225 if ( isset($this->cache[__FUNCTION__][$tablename]) ){ 00226 return $this->cache[__FUNCTION__][$tablename]; 00227 } 00228 if ( !isset($tablename) ){ 00229 $tablename = $this->_relationship->getDomainTable(); 00230 00231 00232 } 00233 00234 $table =& Dataface_Table::loadTable($tablename); 00235 00236 00237 $values = array(); 00238 00239 00240 $absVals = $this->getAbsoluteValues(); 00241 $fieldnames = $this->_relationship->fields(true); 00242 //foreach ( array_keys($absVals) as $key ){ 00243 foreach ( $fieldnames as $key ){ 00244 list($currTablename, $columnName) = explode('.', $key); 00245 if ( ($currTablename == $tablename or $table->hasField($columnName)) and array_key_exists($key, $absVals)){ 00246 00247 $values[$columnName] = $absVals[$key]; 00248 00249 } else if ( isset($this->_relationship->_schema['aliases'][$columnName]) /*and 00250 /*$table->hasField($this->_relationship->_schema['aliases'][$columnName])*/ ){ 00251 $values[$this->_relationship->_schema['aliases'][$columnName]] = $absVals[$key]; 00252 } 00253 } 00254 00255 foreach ( $this->_values as $key=>$val ){ 00256 if ( !isset($values[$key]) and $table->hasField($key) ) $values[$key] = $this->_values[$key]; 00257 } 00258 00259 $record = new Dataface_Record($tablename, $values); 00260 $record->secureDisplay = $this->secureDisplay; 00261 foreach (array_keys($values) as $key){ 00262 00263 if ( $this->isDirty($key) ) $record->setFlag($key); 00264 00265 } 00266 $this->cache[__FUNCTION__][$tablename] =& $record; 00267 00268 return $record; 00269 } 00270 00271 00272 00281 function toRecords(){ 00282 $tables =& $this->_relationship->getDestinationTables(); 00283 $out = array(); 00284 foreach ( array_keys($tables) as $index ){ 00285 $out[] =& $this->toRecord($tables[$index]->tablename); 00286 } 00287 return $out; 00288 } 00289 00290 00316 function &getParent(){ 00317 return $this->_record; 00318 } 00319 00320 00337 function testCondition($condition){ 00338 extract($this->strvals()); 00339 return eval('return ('.$condition.');'); 00340 } 00341 00342 00353 function getActions($params=array()){ 00354 $params['record'] =& $this; 00355 return $this->_record->_table->tablename->getActions($params); 00356 } 00357 00358 00359 // @} 00360 // END OF Utility Methods 00361 //------------------------------------------------------------------------------------- 00362 00363 00364 //------------------------------------------------------------------------------------- 00365 // @{ 00379 function clearFlags(){ 00380 $this->_dirtyFlags = array(); 00381 return $this; 00382 } 00383 00389 function setFlag($fieldname){ 00390 $this->_dirtyFlags[$fieldname] = true; 00391 return $this; 00392 } 00393 00399 function clearFlag($fieldname){ 00400 unset($this->_dirtyFlags[$fieldname]); 00401 return $this; 00402 } 00403 00409 function isDirty($fieldname){ 00410 return ( isset($this->_dirtyFlags[$fieldname]) ); 00411 } 00412 00413 // @} 00414 // End Transactions 00415 //--------------------------------------------------------------------------------------- 00416 00417 //---------------------------------------------------------------------------------------- 00418 // @{ 00419 00434 function setMetaDataValue($key, $value){ 00435 if ( !isset( $this->_metaDataValues ) ) $this->_metaDataValues = array(); 00436 $this->_metaDataValues[$key] = $value; 00437 return $this; 00438 00439 } 00440 00441 00451 function getLength($fieldname){ 00452 if ( strpos($fieldname, '.') !== false ){ 00453 list($tablename, $fieldname) = explode('.', $fieldname); 00454 return $this->getLength($fieldname); 00455 } 00456 $key = '__'.$fieldname.'_length'; 00457 if ( isset( $this->_metaDataValues[$key] ) ){ 00458 return $this->_metaDataValues[$key]; 00459 } else { 00460 return strlen($this->getValueAsString($fieldname)); 00461 } 00462 } 00463 00472 function setValue($fieldname, $value){ 00473 $this->_initValues(); 00474 00475 00476 if ( strpos($fieldname,'.') === false ){ 00477 if ( strpos($fieldname, '__') === 0 ){ 00478 return $this->setMetaDataValue($fieldname, $value); 00479 } 00480 00481 if ( isset( $this->_lockedFields[$fieldname]) ) return; 00482 $val = $this->_record->_table->parse($this->_relationshipName.".".$fieldname, $value); 00483 if ( $val != $this->_values[$fieldname] ){ 00484 $this->_values[$fieldname] = $val; 00485 $this->clearCache(); 00486 $this->setFlag($fieldname); 00487 } 00488 00489 } else { 00490 00491 list ( $table, $field ) = explode('.', $fieldname); 00492 if ( $table != $this->_relationship->getDomainTable() and Dataface_Table::loadTable($this->_relationship->getDomainTable())->hasField($field) ){ 00493 return PEAR::raiseError("Cannot set duplicate value in relationship. The field $fieldname is part of the domain table and not part of $table in this relationship."); 00494 } 00495 return $this->setValue($field, $value); 00496 } 00497 00498 } 00499 00508 function setValues($values){ 00509 if ( !is_array($values) ){ 00510 throw new Exception( "setValues() expects 1st parameter to be an array but received a '".get_class($values)."' ",E_USER_WARNING); 00511 } 00512 foreach ( $values as $key=>$value){ 00513 00514 $this->setValue($key, $value); 00515 } 00516 00517 } 00518 00519 00520 00521 00522 00531 function getValue($fieldname){ 00532 $this->_initValues(); 00533 if ( strpos($fieldname,'.') === false ){ 00534 00535 if ( !array_key_exists( $fieldname, $this->_values ) ){ 00536 // The key does not exist as a normal field -- so check if it is a calculated field. 00537 $tables = $this->_relationship->getDestinationTables(); 00538 $dt =& Dataface_Table::loadTable($this->_relationship->getDomainTable()); 00539 00540 if ( $dt->hasField($fieldname) ){ 00541 echo "Domain table has $fieldname"; 00542 $tables = array($this->_relationship->getDomainTable()); 00543 } else { 00544 echo "Domain table doesn't have $fieldname"; 00545 } 00546 foreach ( array_keys($tables) as $tkey){ 00547 if ( $tables[$tkey]->hasField($fieldname) ){ 00548 $tempRecord = new Dataface_Record($tables[$tkey]->tablename,$this->getValues()); 00549 return $tempRecord->getValue($fieldname); 00550 } 00551 } 00552 throw new Exception("Attempt to get value for fieldname '$fieldname' that does not exist in related record of relationship '".$this->_relationshipName."'. Acceptable values include {".implode(', ', array_keys($this->_values))."}.\n<br>", E_USER_ERROR); 00553 } 00554 00555 return $this->_values[$fieldname]; 00556 00557 } else { 00558 list ( $table, $field ) = explode('.', $fieldname); 00559 return $this->getValue($field); 00560 } 00561 } 00562 00573 function getValueAsString($fieldname){ 00574 $value = $this->getValue($fieldname); 00575 00576 $parent =& $this->getParent(); 00577 $table =& $parent->_table->getTableTableForField($this->_relationshipName.'.'.$fieldname); 00578 if ( PEAR::isError($table) ){ 00579 throw new Exception($table->toString(), E_USER_ERROR); 00580 } 00581 $delegate =& $table->getDelegate(); 00582 $rel_fieldname = $fieldname; //$table->relativeFieldName($fieldname); 00583 if ( $delegate !== null and method_exists( $delegate, $rel_fieldname.'__toString') ){ 00584 $value = call_user_func( array(&$delegate, $rel_fieldname.'__toString'), $value); 00585 } else 00586 00587 00588 if ( is_array($value) ){ 00589 if ( method_exists( $table, $table->getType($fieldname)."_to_string") ){ 00590 $value = call_user_func( array( &$table, $table->getType($fieldname)."_to_string"), $value ); 00591 } else { 00592 $value = implode(', ', $value); 00593 } 00594 } 00595 00596 00597 $evt = new stdClass; 00598 $evt->table = $table; 00599 $evt->field =& $table->getField($rel_fieldname); 00600 $evt->value = $value; 00601 $evt->type = $table->getType($rel_fieldname); 00602 $table->app->fireEvent('after_getValueAsString', $evt); 00603 $value = $evt->value; 00604 00605 return $value; 00606 00607 } 00608 00616 function htmlValue($fieldname){ 00617 $value = $this->getValue($fieldname); 00618 $parent =& $this->getParent(); 00619 $table =& $parent->_table->getTableTableForField($this->_relationshipName.'.'.$fieldname); 00620 if ( PEAR::isError($table) ){ 00621 throw new Exception($table->toString(), E_USER_ERROR); 00622 } 00623 $record =& $this->toRecord($table->tablename); 00624 $htmlval = $record->htmlValue($fieldname); 00625 return $htmlval; 00626 } 00627 00638 function getValuesAsStrings($fields=''){ 00639 $keys = is_array($fields) ? $fields : array_keys($this->_values); 00640 $values = array(); 00641 foreach ($keys as $key){ 00642 $values[$key] = $this->getValueAsString($key); 00643 } 00644 return $values; 00645 } 00646 00647 00652 function strvals($fields=''){ 00653 return $this->getValuesAsStrings($fields); 00654 } 00655 00656 00661 function strval($fieldname, $index=0){ 00662 return $this->getValueAsString($fieldname, $index); 00663 } 00664 00665 00670 function stringValue($fieldname){ 00671 return $this->getValueAsString($fieldname); 00672 } 00673 00684 function &getValues($columns=null, $excludeNulls=false){ 00685 $this->_initValues(); 00686 return $this->_values; 00687 } 00688 00693 function &values($fields = null){ 00694 return $this->getValues($fields); 00695 } 00696 00701 function &vals($fields = null){ 00702 return $this->getValues($fields); 00703 } 00704 00705 00710 function val($fieldname){ 00711 return $this->getValue($fieldname); 00712 } 00713 00714 00715 00723 function getAbsoluteValues($excludeNulls=false, $includeAll=false){ 00724 $absVals = array(); 00725 foreach ( $this->getValues() as $key=>$value){ 00726 $absVals[ $this->_absoluteColumnNames[$key] ] = $value; 00727 } 00728 return $absVals; 00729 00730 00731 } 00732 00743 function getForeignKeyValues($sql = null){ 00744 if ( !isset($sql) ) $sql_index = 0; 00745 else $sql_index = $sql; 00746 if ( isset($this->cache[__FUNCTION__][$sql_index]) ){ 00747 return $this->cache[__FUNCTION__][$sql_index]; 00748 } 00749 $fkeys = $this->_relationship->getForeignKeyValues(); 00750 $absVals = $this->getAbsoluteValues(true); 00751 00752 $out = $this->_relationship->getForeignKeyValues($absVals, $sql, $this->getParent()); 00753 $this->cache[__FUNCTION__][$sql_index] = $out; 00754 return $out; 00755 00756 } 00757 00758 00759 00760 00785 function display($fieldname){ 00786 if ( isset($this->cache[__FUNCTION__][$fieldname]) ){ 00787 return $this->cache[__FUNCTION__][$fieldname]; 00788 } 00789 $parent =& $this->getParent(); 00790 $table =& $parent->_table->getTableTableForField($this->_relationshipName.'.'.$fieldname); 00791 if (PEAR::isError($table) ){ 00792 throw new Exception("Error loading table while displaying $fieldname because ".$table->getMessage(), E_USER_ERROR); 00793 00794 } 00795 00796 00797 if ( !$table->isBlob($fieldname) ){ 00798 $record =& $this->toRecord($table->tablename); 00799 if ( $this->secureDisplay and $this->checkPermission('view', array('field'=>$fieldname)) ){ 00800 //echo "HERE"; 00801 $oldSecure = $record->secureDisplay; 00802 $record->secureDisplay = false; 00803 $out = $record->display($fieldname); 00804 $record->secureDisplay = $oldSecure; 00805 } else { 00806 $out = $record->display($fieldname); 00807 } 00808 00809 $this->cache[__FUNCTION__][$fieldname] = $out; 00810 return $out; 00811 00812 } else { 00813 $keys = array_keys($table->keys()); 00814 $qstr = ''; 00815 foreach ($keys as $key){ 00816 $qstr .= "&$key"."=".$this->strval($key); 00817 } 00818 $out = DATAFACE_SITE_HREF."?-action=getBlob&-table=".$table->tablename."&-field=$fieldname$qstr"; 00819 $this->cache[__FUNCTION__][$fieldname] = $out; 00820 return $out; 00821 } 00822 00823 00824 } 00825 00837 function preview($fieldname, $index=0, $maxlength=255){ 00838 if ( isset($this->cache[__FUNCTION__][$fieldname][$index][$maxlength]) ){ 00839 return $this->cache[__FUNCTION__][$fieldname][$index][$maxlength]; 00840 } 00841 $strval = strip_tags($this->display($fieldname,$index)); 00842 $out = substr($strval, 0, $maxlength); 00843 if ( strlen($strval)>$maxlength) { 00844 $out .= '...'; 00845 } 00846 $this->cache[__FUNCTION__][$fieldname][$index][$maxlength] = $out; 00847 return $out; 00848 } 00849 00854 function printValue($fieldname){ 00855 return $this->display($fieldname); 00856 } 00857 00862 function printval($fieldname){ 00863 return $this->display($fieldname); 00864 } 00865 00870 function q($fieldname){ 00871 return $this->display($fieldname); 00872 } 00873 00881 function qq($fieldname){ 00882 $parent =& $this->getParent(); 00883 $table =& $parent->_table->getTableTableForField($this->_relationshipName.'.'.$fieldname); 00884 if ( PEAR::isError($table) ){ 00885 throw new Exception($table->toString(), E_USER_ERROR); 00886 } 00887 if ( !$table->isBlob($fieldname) ){ 00888 return htmlspecialchars($this->q($fieldname, $index)); 00889 } else { 00890 return $this->display($fieldname, $index); 00891 } 00892 } 00893 00894 00895 // @} 00896 // End Accessing Field Data 00897 //------------------------------------------------------------------------------------------- 00898 00899 00900 //------------------------------------------------------------------------------------------- 00901 // @{ 00919 function getId(){ 00920 if ( isset($this->cache[__FUNCTION__]) ){ 00921 return $this->cache[__FUNCTION__]; 00922 } 00923 $parentid = $this->_record->getId(); 00924 list($tablename, $querystr) = explode('?',$parentid); 00925 $id = $tablename.'/'.$this->_relationshipName.'?'.$querystr; 00926 $keys = array_keys($this->_relationship->keys()); 00927 $params = array(); 00928 foreach ($keys as $key){ 00929 $params[] = urlencode($this->_relationshipName.'::'.$key).'='.urlencode($this->strval($key)); 00930 } 00931 $out = $id.'&'.implode('&',$params); 00932 $this->cache[__FUNCTION__] = $out; 00933 return $out; 00934 } 00935 00936 00945 function getTitle(){ 00946 $record =& $this->toRecord(); 00947 return $record->getTitle(); 00948 } 00949 00950 00951 00952 // @} 00953 // END Metadata 00954 //-------------------------------------------------------------------------------------------- 00955 00956 // @{ 00976 function validate( $fieldname, $value, &$params){ 00977 if ( strpos($fieldname, '.') !== false ){ 00978 list($relname, $fieldname) = explode('.', $fieldname); 00979 return $this->validate($fieldname, $value, $params); 00980 } 00981 00982 if ( !is_array($params) ){ 00983 $params = array('message'=> &$params); 00984 } 00985 $table =& $this->_relationship->getTable($fieldname); 00986 if (PEAR::isError($table) ){ 00987 error_log($table->toString().implode("\n", $table->getBacktrace())); 00988 throw new Exception("Failed to get table for field $fieldname. See error log for details", E_USER_ERROR); 00989 00990 } else if (!$table ){ 00991 throw new Exception("Could not load table for field $fieldname .", E_USER_ERROR); 00992 } 00993 $field =& $table->getField($fieldname); 00994 00995 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'])){ 00996 // This bit of validation operates on the upload values assuming the file was just uploaded as a form. It assumes 00997 // that $value is of the form 00999 $params['message'] = "$fieldname is a required field."; 01000 return false; 01001 } 01002 01003 $res = $table->validate($fieldname, $value, $params); 01004 if ( $res ){ 01005 $delegate =& $table->getDelegate(); 01006 if ( $delegate !== null and method_exists($delegate, $fieldname."__validate") ){ 01007 /* 01008 * 01009 * The delegate defines a custom validation method for this field. Use it. 01010 * 01011 */ 01012 $methodname = $fieldname."__validate"; 01013 $res = $delegate->$methodname($this,$value,$params); 01014 //$res = call_user_func(array(&$delegate, $fieldname."__validate"), $this, $value, $params); 01015 } 01016 } 01017 return $res; 01018 01019 } 01020 01021 // @} 01022 // END Form Validation 01023 //-------------------------------------------------------------------------------------------- 01024 01025 // @{ 01032 function save($lang=null, $secure=false){ 01033 $recs =& $this->toRecords(); 01034 01035 foreach (array_keys($recs) as $i){ 01036 01037 $res = $recs[$i]->save($lang, $secure); 01038 if ( PEAR::isError($res) ) return $res; 01039 } 01040 } 01041 01042 // @} 01043 // End Saving 01044 //---------------------------------------------------------------------------------------------- 01045 01046 //---------------------------------------------------------------------------------------------- 01047 // @{ 01055 function getPermissions($params=array()){ 01056 01057 // 1. Get the permissions for the particular field 01058 if ( isset($params['field']) ){ 01059 if ( strpos($params['field'],'.') !== false ){ 01060 list($junk,$fieldname) = explode('.', $params['field']); 01061 } else { 01062 $fieldname = $params['field']; 01063 } 01064 $t =& $this->_relationship->getTable($fieldname); 01065 $rec = $this->toRecord($t->tablename); 01066 01067 01068 $perms = $rec->getPermissions(array('field'=>$fieldname, 'nobubble'=>1)); 01069 if ( !$perms ) $perms = array(); 01070 01071 01072 $rfperms = $this->_record->getPermissions(array('relationship'=>$this->_relationshipName, 'field'=>$fieldname, 'nobubble'=>1)); 01073 //echo "RFPerms: ";print_r($rfperms); 01074 if ( $rfperms ){ 01075 foreach ($rfperms as $k=>$v){ 01076 $perms[$k] = $v; 01077 } 01078 } 01079 01080 //print_r($perms); 01081 return $perms; 01082 } else { 01083 $perms = array(); 01084 foreach ( $this->toRecords() as $record){ 01085 $rperms = $record->getPermissions(array()); 01086 if ( $perms ){ 01087 $perms = array_intersect_assoc($perms, $rperms); 01088 01089 } else { 01090 $perms = $rperms; 01091 } 01092 01093 } 01094 return $perms; 01095 01096 } 01097 } 01098 01115 function checkPermission($perm, $params=array()){ 01116 $perms = $this->getPermissions($params); 01117 return @$perms[$perm]?1:0; 01118 01119 } 01120 01121 01122 // @} 01123 // End Permissions 01124 //---------------------------------------------------------------------------------------------- 01125 01126 01127 01128 01129 01130 01131 01132 01133 01134 01135 }