eZ Publish  [trunk]
ezcontentclassattribute.php
Go to the documentation of this file.
00001 <?php
00002 /**
00003  * File containing the eZContentClassAttribute class.
00004  *
00005  * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved.
00006  * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2
00007  * @version //autogentag//
00008  * @package kernel
00009  */
00010 
00011 /*!
00012   \class eZContentClassAttribute ezcontentclassattribute.php
00013   \ingroup eZKernel
00014   \brief Encapsulates data for a class attribute
00015 
00016 */
00017 
00018 class eZContentClassAttribute extends eZPersistentObject
00019 {
00020     function eZContentClassAttribute( $row )
00021     {
00022         $this->eZPersistentObject( $row );
00023 
00024         $this->Content = null;
00025         $this->DisplayInfo = null;
00026         $this->Module = null;
00027 
00028         $this->NameList = new eZSerializedObjectNameList();
00029         if ( isset( $row['serialized_name_list'] ) )
00030             $this->NameList->initFromSerializedList( $row['serialized_name_list'] );
00031         else
00032             $this->NameList->initDefault();
00033 
00034         $this->DescriptionList = new eZSerializedObjectNameList();
00035         if ( isset( $row['serialized_description_list'] ) )
00036             $this->DescriptionList->initFromSerializedList( $row['serialized_description_list'] );
00037         else
00038             $this->DescriptionList->initDefault();
00039 
00040         $this->DataTextI18nList = new eZSerializedObjectNameList();
00041         if ( isset( $row['serialized_data_text'] ) )
00042             $this->DataTextI18nList->initFromSerializedList( $row['serialized_data_text'] );
00043         else
00044             $this->DataTextI18nList->initDefault();
00045 
00046         // Make sure datatype gets final say if attribute should be translatable
00047         if ( isset( $row['can_translate'] ) && $row['can_translate'] )
00048         {
00049             $datatype = $this->dataType();
00050             if ( $datatype instanceof eZDataType )
00051             {
00052                 if ( !$datatype->isTranslatable() )
00053                     $this->setAttribute('can_translate', 0);
00054             }
00055             else
00056             {
00057                 eZDebug::writeError( 'Could not get instance of datatype: ' . $row['data_type_string'], __METHOD__ );
00058             }
00059         }
00060     }
00061 
00062     static function definition()
00063     {
00064         static $definition = array( 'fields' => array( 'id' => array( 'name' => 'ID',
00065                                                         'datatype' => 'integer',
00066                                                         'default' => 0,
00067                                                         'required' => true ),
00068                                          'serialized_name_list' => array( 'name' => 'SerializedNameList',
00069                                                                           'datatype' => 'string',
00070                                                                           'default' => '',
00071                                                                           'required' => true ),
00072                                          'serialized_description_list' => array( 'name' => 'SerializedDescriptionList',
00073                                                                           'datatype' => 'string',
00074                                                                           'default' => '',
00075                                                                           'required' => true ),
00076                                          'version' => array( 'name' => 'Version',
00077                                                              'datatype' => 'integer',
00078                                                              'default' => 0,
00079                                                              'required' => true ),
00080                                          'contentclass_id' => array( 'name' => 'ContentClassID',
00081                                                                      'datatype' => 'integer',
00082                                                                      'default' => 0,
00083                                                                      'required' => true,
00084                                                                      'foreign_class' => 'eZContentClass',
00085                                                                      'foreign_attribute' => 'id',
00086                                                                      'multiplicity' => '1..*' ),
00087                                          'identifier' => array( 'name' => 'Identifier',
00088                                                                 'datatype' => 'string',
00089                                                                 'default' => '',
00090                                                                 'required' => true ),
00091                                          'placement' => array( 'name' => 'Position',
00092                                                                'datatype' => 'integer',
00093                                                                'default' => 0,
00094                                                                'required' => true ),
00095                                          'is_searchable' => array( 'name' => 'IsSearchable',
00096                                                                    'datatype' => 'integer',
00097                                                                    'default' => 0
00098                                                                    ),
00099                                          'is_required' => array( 'name' => 'IsRequired',
00100                                                                  'datatype' => 'integer',
00101                                                                  'default' => 0,
00102                                                                  'required' => true ),
00103                                          'can_translate' => array( 'name' => 'CanTranslate',
00104                                                                    'datatype' => 'integer',
00105                                                                    'default' => 0
00106                                                                    ),
00107                                          'is_information_collector' => array( 'name' => 'IsInformationCollector',
00108                                                                               'datatype' => 'integer',
00109                                                                               'default' => 0,
00110                                                                               'required' => true ),
00111                                          'data_type_string' => array( 'name' => 'DataTypeString',
00112                                                                       'datatype' => 'string',
00113                                                                       'default' => '',
00114                                                                       'required' => true ),
00115                                          'data_int1' => array( 'name' => 'DataInt1',
00116                                                                'datatype' => 'integer',
00117                                                                'default' => 0,
00118                                                                'required' => true ),
00119                                          'data_int2' => array( 'name' => 'DataInt2',
00120                                                                'datatype' => 'integer',
00121                                                                'default' => 0,
00122                                                                'required' => true ),
00123                                          'data_int3' => array( 'name' => 'DataInt3',
00124                                                                'datatype' => 'integer',
00125                                                                'default' => 0,
00126                                                                'required' => true ),
00127                                          'data_int4' => array( 'name' => 'DataInt4',
00128                                                                'datatype' => 'integer',
00129                                                                'default' => 0,
00130                                                                'required' => true ),
00131                                          'data_float1' => array( 'name' => 'DataFloat1',
00132                                                                  'datatype' => 'float',
00133                                                                  'default' => 0,
00134                                                                  'required' => true ),
00135                                          'data_float2' => array( 'name' => 'DataFloat2',
00136                                                                  'datatype' => 'float',
00137                                                                  'default' => 0,
00138                                                                  'required' => true ),
00139                                          'data_float3' => array( 'name' => 'DataFloat3',
00140                                                                  'datatype' => 'float',
00141                                                                  'default' => 0,
00142                                                                  'required' => true ),
00143                                          'data_float4' => array( 'name' => 'DataFloat4',
00144                                                                  'datatype' => 'float',
00145                                                                  'default' => 0,
00146                                                                  'required' => true ),
00147                                          'data_text1' => array( 'name' => 'DataText1',
00148                                                                 'datatype' => 'text',
00149                                                                 'default' => '',
00150                                                                 'required' => true ),
00151                                          'data_text2' => array( 'name' => 'DataText2',
00152                                                                 'datatype' => 'text',
00153                                                                 'default' => '',
00154                                                                 'required' => true ),
00155                                          'data_text3' => array( 'name' => 'DataText3',
00156                                                                 'datatype' => 'text',
00157                                                                 'default' => '',
00158                                                                 'required' => true ),
00159                                          'data_text4' => array( 'name' => 'DataText4',
00160                                                                 'datatype' => 'text',
00161                                                                 'default' => '',
00162                                                                 'required' => true ),
00163                                          'data_text5' => array( 'name' => 'DataText5',
00164                                                                 'datatype' => 'text',
00165                                                                 'default' => '',
00166                                                                 'required' => true ),
00167                                          'serialized_data_text' => array( 'name' => 'SerializedDataText',
00168                                                                           'datatype' => 'string',
00169                                                                           'default' => '',
00170                                                                           'required' => true ),
00171                                          'category' => array( 'name' => 'Category',
00172                                                                'datatype' => 'text',
00173                                                                'default' => '',
00174                                                                'required' => true )),
00175                       'keys' => array( 'id', 'version' ),
00176                       "function_attributes" => array( "content" => "content",
00177                                                       'temporary_object_attribute' => 'instantiateTemporary',
00178                                                       'data_type' => 'dataType',
00179                                                       'display_info' => 'displayInfo',
00180                                                       'name' => 'name',
00181                                                       'nameList' => 'nameList',
00182                                                       'description' => 'description',
00183                                                       'descriptionList' => 'descriptionList',
00184                                                       'data_text_i18n' => 'dataTextI18n',
00185                                                       'data_text_i18n_list' => 'dataTextI18nList' ),
00186                       'set_functions' => array( 'name' => 'setName',
00187                                                 'description' => 'setDescription',
00188                                                 'data_text_i18n' => 'setDataTextI18n' ),
00189                       'increment_key' => 'id',
00190                       'sort' => array( 'placement' => 'asc' ),
00191                       'class_name' => 'eZContentClassAttribute',
00192                       'name' => 'ezcontentclass_attribute' );
00193         return $definition;
00194     }
00195 
00196     function __clone()
00197     {
00198         $this->ID = null;
00199     }
00200 
00201     /*!
00202      Creates an 'eZContentClassAttribute' object.
00203 
00204      To specify contentclassattribute name use either $optionalValues['serialized_name_list'] or
00205      combination of $optionalValues['name'] and/or $languageLocale.
00206 
00207      In case of conflict(when both 'serialized_name_list' and 'name' with/without $languageLocale
00208      are specified) 'serialized_name_list' has top priority. This means that 'name' and
00209      $languageLocale will be ingnored because 'serialized_name_list' already has all needed info
00210      about names and languages.
00211 
00212      If 'name' is specified then the contentclassattribute will have a name in $languageLocale(if specified) or
00213      in default language.
00214 
00215      If neither of 'serialized_name_list' or 'name' isn't specified then the contentclassattribute will have an empty
00216      name in 'languageLocale'(if specified) or in default language.
00217 
00218      \return 'eZContentClassAttribute' object.
00219     */
00220     static function create( $class_id, $data_type_string, $optionalValues = array(), $languageLocale = false )
00221     {
00222         $nameList = new eZSerializedObjectNameList();
00223         if ( isset( $optionalValues['serialized_name_list'] ) )
00224             $nameList->initFromSerializedList( $optionalValues['serialized_name_list'] );
00225         else if ( isset( $optionalValues['name'] ) )
00226             $nameList->initFromString( $optionalValues['name'], $languageLocale );
00227         else
00228             $nameList->initFromString( '', $languageLocale );
00229 
00230         $descriptionList = new eZSerializedObjectNameList();
00231         if ( isset( $optionalValues['serialized_description_list'] ) )
00232             $descriptionList->initFromSerializedList( $optionalValues['serialized_description_list'] );
00233         else if ( isset( $optionalValues['description'] ) )
00234             $descriptionList->initFromString( $optionalValues['description'], $languageLocale );
00235         else
00236             $descriptionList->initFromString( '', $languageLocale );
00237 
00238         if ( isset( $optionalValues['data_text_i18n'] ) )
00239         {
00240             $dataTextI18nList = new eZSerializedObjectNameList( $optionalValues['data_text_i18n'] );
00241             $optionalValues['serialized_data_text'] = $dataTextI18nList->serializeNames();
00242         }
00243 
00244         $row = array(
00245             'id' => null,
00246             'version' => eZContentClass::VERSION_STATUS_TEMPORARY,
00247             'contentclass_id' => $class_id,
00248             'identifier' => '',
00249             'serialized_name_list' => $nameList->serializeNames(),
00250             'serialized_description_list' => $descriptionList->serializeNames(),
00251             'is_searchable' => 1,
00252             'is_required' => 0,
00253             'can_translate' => 1,
00254             'is_information_collector' => 0,
00255             'data_type_string' => $data_type_string,
00256             'placement' => eZPersistentObject::newObjectOrder( eZContentClassAttribute::definition(),
00257                                                                'placement',
00258                                                                array( 'version' => 1,
00259                                                                       'contentclass_id' => $class_id ) ) );
00260         $row = array_merge( $row, $optionalValues );
00261         $attribute = new eZContentClassAttribute( $row );
00262 
00263         return $attribute;
00264     }
00265 
00266     function instantiate( $contentobjectID, $languageCode = false, $version = 1 )
00267     {
00268         $attribute = eZContentObjectAttribute::create( $this->attribute( 'id' ), $contentobjectID, $version, $languageCode );
00269         $attribute->initialize();
00270         $attribute->store();
00271         $attribute->postInitialize();
00272     }
00273 
00274     function instantiateTemporary( $contentobjectID = false )
00275     {
00276         return eZContentObjectAttribute::create( $this->attribute( 'id' ), $contentobjectID );
00277     }
00278 
00279     function store( $fieldFilters = null )
00280     {
00281         $dataType = $this->dataType();
00282         if ( !$dataType )
00283         {
00284             return false;
00285         }
00286 
00287         self::expireCache( $this->ID, $this->attribute( 'contentclass_id' ) );
00288 
00289         $dataType->preStoreClassAttribute( $this, $this->attribute( 'version' ) );
00290 
00291         $this->setAttribute( 'serialized_name_list', $this->NameList->serializeNames() );
00292         $this->setAttribute( 'serialized_description_list', $this->DescriptionList->serializeNames() );
00293         $this->setAttribute( 'serialized_data_text', $this->DataTextI18nList->serializeNames() );
00294 
00295         $stored = eZPersistentObject::store( $fieldFilters );
00296 
00297         // store the content data for this attribute
00298         $dataType->storeClassAttribute( $this, $this->attribute( 'version' ) );
00299 
00300         return $stored;
00301     }
00302 
00303     /**
00304      * Store the content class in the version status "defined".
00305      *
00306      * @note Transaction unsafe. If you call several transaction unsafe methods you must enclose
00307      *       the calls within a db transaction; thus within db->begin and db->commit.
00308      *
00309      * @return null|false false if the operation failed
00310      */
00311     function storeDefined()
00312     {
00313         return $this->storeVersioned( eZContentClass::VERSION_STATUS_DEFINED );
00314     }
00315 
00316     /**
00317      * Store the content class in the specified version status.
00318      *
00319      * @note Transaction unsafe. If you call several transaction unsafe methods you must enclose
00320      *       the calls within a db transaction; thus within db->begin and db->commit.
00321      *
00322      * @param int $version version status
00323      * @since Version 4.3
00324      * @return null|false false if the operation failed
00325      */
00326     function storeVersioned( $version )
00327     {
00328         $dataType = $this->dataType();
00329         if ( !$dataType )
00330         {
00331             return false;
00332         }
00333 
00334         self::expireCache( $this->ID, $this->attribute( 'contentclass_id' ) );
00335 
00336         $db = eZDB::instance();
00337         $db->begin();
00338         $dataType->preStoreVersionedClassAttribute( $this, $version );
00339 
00340         $this->setAttribute( 'serialized_name_list', $this->NameList->serializeNames() );
00341         $this->setAttribute( 'serialized_description_list', $this->DescriptionList->serializeNames() );
00342         $this->setAttribute( 'serialized_data_text', $this->DataTextI18nList->serializeNames() );
00343 
00344         eZPersistentObject::store();
00345 
00346         // store the content data for this attribute
00347         $dataType->storeVersionedClassAttribute( $this, $version );
00348         $db->commit();
00349     }
00350 
00351     /*!
00352      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
00353      the calls within a db transaction; thus within db->begin and db->commit.
00354      */
00355     function removeThis( $quiet = false )
00356     {
00357         $dataType = $this->dataType();
00358         if ( $dataType->isClassAttributeRemovable( $this ) )
00359         {
00360             self::expireCache( $this->ID, $this->attribute( 'contentclass_id' ) );
00361 
00362             $db = eZDB::instance();
00363             $db->begin();
00364             $dataType->deleteStoredClassAttribute( $this, $this->Version );
00365             eZPersistentObject::remove();
00366             $db->commit();
00367             return true;
00368         }
00369         else
00370         {
00371             if ( !$quiet )
00372             {
00373                 eZDebug::writeError( 'Datatype [' . $dataType->attribute( 'name' ) . '] can not be deleted to avoid system crash' );
00374             }
00375             return false;
00376         }
00377     }
00378 
00379     static function fetch( $id, $asObject = true, $version = eZContentClass::VERSION_STATUS_DEFINED, $field_filters = null )
00380     {
00381         $object = null;
00382         if ( $field_filters === null and $asObject and
00383              isset( $GLOBALS['eZContentClassAttributeCache'][$id][$version] ) )
00384         {
00385             $object = $GLOBALS['eZContentClassAttributeCache'][$id][$version];
00386         }
00387         if ( $object === null )
00388         {
00389             $object = eZPersistentObject::fetchObject( eZContentClassAttribute::definition(),
00390                                                        $field_filters,
00391                                                        array( 'id' => $id,
00392                                                               'version' => $version ),
00393                                                        $asObject );
00394             if ( $field_filters === null and $asObject )
00395             {
00396                 $GLOBALS['eZContentClassAttributeCache'][$id][$version] = $object;
00397             }
00398         }
00399         return $object;
00400     }
00401 
00402     static function fetchList( $asObject = true, $parameters = array() )
00403     {
00404         $parameters = array_merge( array( 'data_type' => false,
00405                                           'version' => false ),
00406                                    $parameters );
00407         $dataType = $parameters['data_type'];
00408         $version = $parameters['version'];
00409         $objects = null;
00410         if ( $asObject &&
00411              $dataType === false &&
00412              $version === false )
00413         {
00414             $objects = $GLOBALS['eZContentClassAttributeCacheListFull'];
00415         }
00416         if ( !isset( $objects ) or
00417              $objects === null )
00418         {
00419             $conditions = null;
00420             if ( $dataType !== false or
00421                  $version !== false )
00422             {
00423                 $conditions = array();
00424                 if ( $dataType !== false )
00425                     $conditions['data_type_string'] = $dataType;
00426                 if ( $version !== false )
00427                     $conditions['version'] = $version;
00428             }
00429             $objects = eZPersistentObject::fetchObjectList( eZContentClassAttribute::definition(),
00430                                                                 null, $conditions, null, null,
00431                                                                 $asObject );
00432             if ( $asObject )
00433             {
00434                 foreach ( $objects as $objectItem )
00435                 {
00436                     $objectID = $objectItem->ID;
00437                     $objectVersion = $objectItem->Version;
00438                     $GLOBALS['eZContentClassAttributeCache'][$objectID][$objectVersion] = $objectItem;
00439                 }
00440                 if (  $dataType === false && $version === false )
00441                 {
00442                     $GLOBALS['eZContentClassAttributeCacheListFull'] = $objects;
00443                 }
00444             }
00445         }
00446         return $objects;
00447     }
00448 
00449     static function fetchListByClassID( $classID, $version = eZContentClass::VERSION_STATUS_DEFINED, $asObject = true )
00450     {
00451         $objects = null;
00452         if ( $asObject )
00453         {
00454             if ( isset( $GLOBALS['eZContentClassAttributeCacheList'][$classID][$version] ) )
00455                 $objects = $GLOBALS['eZContentClassAttributeCacheList'][$classID][$version];
00456         }
00457         if ( !isset( $objects ) or
00458              $objects === null )
00459         {
00460             $cond = array( 'contentclass_id' => $classID,
00461                            'version' => $version );
00462             $objects = eZPersistentObject::fetchObjectList( eZContentClassAttribute::definition(),
00463                                                                 null, $cond, null, null,
00464                                                                 $asObject );
00465             if ( $asObject )
00466             {
00467                 foreach ( $objects as $objectItem )
00468                 {
00469                     $objectID = $objectItem->ID;
00470                     $objectVersion = $objectItem->Version;
00471                     if ( !isset( $GLOBALS['eZContentClassAttributeCache'][$objectID][$objectVersion] ) )
00472                         $GLOBALS['eZContentClassAttributeCache'][$objectID][$objectVersion] = $objectItem;
00473                 }
00474                 $GLOBALS['eZContentClassAttributeCacheList'][$classID][$version] = $objects;
00475             }
00476         }
00477         return $objects;
00478     }
00479 
00480     static function fetchFilteredList( $cond, $asObject = true )
00481     {
00482         $objectList = eZPersistentObject::fetchObjectList( eZContentClassAttribute::definition(),
00483                                                            null, $cond, null, null,
00484                                                            $asObject );
00485         if ( $asObject )
00486         {
00487             foreach ( $objectList as $objectItem )
00488             {
00489                 $objectID = $objectItem->ID;
00490                 $objectVersion = $objectItem->Version;
00491                 if ( !isset( $GLOBALS['eZContentClassAttributeCache'][$objectID][$objectVersion] ) )
00492                     $GLOBALS['eZContentClassAttributeCache'][$objectID][$objectVersion] = $objectItem;
00493             }
00494         }
00495         return $objectList;
00496     }
00497 
00498     /*!
00499      Moves the object down if $down is true, otherwise up.
00500      If object is at either top or bottom it is wrapped around.
00501      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
00502      the calls within a db transaction; thus within db->begin and db->commit.
00503     */
00504     function move( $down, $params = null )
00505     {
00506         if ( is_array( $params ) )
00507         {
00508             $pos = $params['placement'];
00509             $cid = $params['contentclass_id'];
00510             $version = $params['version'];
00511         }
00512         else
00513         {
00514             $pos = $this->Position;
00515             $cid = $this->ContentClassID;
00516             $version = $this->Version;
00517         }
00518         eZPersistentObject::reorderObject( eZContentClassAttribute::definition(),
00519                                            array( 'placement' => $pos ),
00520                                            array( 'contentclass_id' => $cid,
00521                                                   'version' => $version ),
00522                                            $down );
00523     }
00524 
00525     function dataType()
00526     {
00527         return eZDataType::create( $this->DataTypeString );
00528     }
00529 
00530     /*!
00531      \return The content for this attribute.
00532     */
00533     function content()
00534     {
00535         if ( $this->Content === null )
00536         {
00537             $dataType = $this->dataType();
00538             $this->Content = $dataType->classAttributeContent( $this );
00539         }
00540 
00541         return $this->Content;
00542     }
00543 
00544     /*!
00545      Sets the content for the current attribute
00546     */
00547     function setContent( $content )
00548     {
00549         $this->Content = $content;
00550     }
00551 
00552     /*!
00553      \return Information on how to display the class attribute.
00554              See eZDataType::classDisplayInformation() for more information on what is returned.
00555     */
00556     function displayInfo()
00557     {
00558         if ( !$this->DisplayInfo )
00559         {
00560             $dataType = $this->dataType();
00561             if ( is_object( $dataType ) )
00562             {
00563                 $this->DisplayInfo = $dataType->classDisplayInformation( $this, false );
00564             }
00565         }
00566         return $this->DisplayInfo;
00567     }
00568 
00569     /*!
00570      Executes the custom HTTP action
00571     */
00572     function customHTTPAction( $module, $http, $action )
00573     {
00574         $dataType = $this->dataType();
00575         $this->Module = $module;
00576         $dataType->customClassAttributeHTTPAction( $http, $action, $this );
00577         unset( $this->Module );
00578         $this->Module = null;
00579     }
00580 
00581     /*!
00582      \return the module which uses this attribute or \c null if no module set.
00583      \note Currently only customHTTPAction sets this.
00584     */
00585     function currentModule()
00586     {
00587         return $this->Module;
00588     }
00589 
00590     static function cachedInfo()
00591     {
00592         eZExpiryHandler::registerShutdownFunction();
00593 
00594         $info = array();
00595         $db = eZDB::instance();
00596         $dbName = md5( $db->DB );
00597 
00598         $cacheDir = eZSys::cacheDirectory();
00599         $phpCache = new eZPHPCreator( "$cacheDir", "sortkey_$dbName.php", '', array( 'clustering' => 'sortkey' ) );
00600         $handler = eZExpiryHandler::instance();
00601         $expiryTime = 0;
00602 
00603         if ( $handler->hasTimestamp( 'sort-key-cache' ) )
00604         {
00605             $expiryTime = $handler->timestamp( 'sort-key-cache' );
00606         }
00607 
00608         if ( $phpCache->canRestore( $expiryTime ) )
00609         {
00610             $info = $phpCache->restore( array( 'sortkey_type_array' => 'sortKeyTypeArray',
00611                                                'attribute_type_array' => 'attributeTypeArray' ) );
00612         }
00613         else
00614         {
00615             // Fetch all datatypes and id's used
00616             $query = "SELECT id, data_type_string FROM ezcontentclass_attribute";
00617             $attributeArray = $db->arrayQuery( $query );
00618 
00619             $attributeTypeArray = array();
00620             $sortKeyTypeArray = array();
00621             foreach ( $attributeArray as $attribute )
00622             {
00623                 $attributeTypeArray[$attribute['id']] = $attribute['data_type_string'];
00624                 $sortKeyTypeArray[$attribute['data_type_string']] = 0;
00625             }
00626 
00627             // Fetch datatype for every unique datatype
00628             foreach ( array_keys( $sortKeyTypeArray ) as $key )
00629             {
00630                 unset( $dataType );
00631                 $dataType = eZDataType::create( $key );
00632                 if( is_object( $dataType ) )
00633                     $sortKeyTypeArray[$key] = $dataType->sortKeyType();
00634             }
00635             unset( $dataType );
00636 
00637             // Store identifier list to cache file
00638             $phpCache->addVariable( 'sortKeyTypeArray', $sortKeyTypeArray );
00639             $phpCache->addVariable( 'attributeTypeArray', $attributeTypeArray );
00640             $phpCache->store();
00641 
00642             $info['sortkey_type_array'] = $sortKeyTypeArray;
00643             $info['attribute_type_array'] = $attributeTypeArray;
00644         }
00645 
00646         return $info;
00647     }
00648 
00649     /*!
00650      \static
00651     */
00652     static function sortKeyTypeByID( $classAttributeID )
00653     {
00654         $sortKeyType = false;
00655 
00656         $info = eZContentClassAttribute::cachedInfo();
00657         if ( isset( $info['attribute_type_array'][$classAttributeID] ) )
00658         {
00659             $classAttributeType = $info['attribute_type_array'][$classAttributeID];
00660             $sortKeyType = $info['sortkey_type_array'][$classAttributeType];
00661         }
00662 
00663         return $sortKeyType;
00664     }
00665 
00666     /*!
00667      \static
00668     */
00669     static function dataTypeByID( $classAttributeID )
00670     {
00671         $dataTypeString = false;
00672         $info = eZContentClassAttribute::cachedInfo();
00673 
00674         if ( isset( $info['attribute_type_array'][$classAttributeID] ) )
00675             $dataTypeString = $info['attribute_type_array'][$classAttributeID];
00676 
00677         return $dataTypeString;
00678     }
00679 
00680     /*!
00681       This methods relay calls to the diff method inside the datatype.
00682     */
00683     function diff( $old, $new )
00684     {
00685         $datatype = $this->dataType();
00686         $result = $datatype->diff( $old, $new );
00687         return $result;
00688     }
00689 
00690     /**
00691      * Returns name from serialized string, can be used for serialized description and data_text as well.
00692      *
00693      * @param string $serializedNameList
00694      * @param string|false $languageLocale Uses AlwaysAvailable language if false
00695      * @return string
00696      */
00697     static function nameFromSerializedString( $serializedNameList, $languageLocale = false )
00698     {
00699         return eZSerializedObjectNameList::nameFromSerializedString( $serializedNameList, $languageLocale );
00700     }
00701 
00702     /**
00703      * Returns name of attribute based on serialized_name_list
00704      *
00705      * @param string|false $languageLocale Uses AlwaysAvailable language if false
00706      * @return string
00707      */
00708     function name( $languageLocale = false )
00709     {
00710         return $this->NameList->name( $languageLocale );
00711     }
00712 
00713     /**
00714      * Sets name of attribute, store() will take care of writing back to serialized_name_list
00715      *
00716      * @param string $name
00717      * @param string|false $languageLocale Uses AlwaysAvailable language if false
00718      * @return string Return old value
00719      */
00720     function setName( $name, $languageLocale = false )
00721     {
00722         return $this->NameList->setName( $name, $languageLocale );
00723     }
00724 
00725     /**
00726      * Returns name list for all locales for attribute
00727      *
00728      * @return array
00729      */
00730     function nameList()
00731     {
00732         return $this->NameList->nameList();
00733     }
00734 
00735     /**
00736      * Returns description of attribute based on serialized_description_list
00737      *
00738      * @param string|false $languageLocale Uses AlwaysAvailable language if false
00739      * @return string
00740      */
00741     function description( $languageLocale = false )
00742     {
00743         return $this->DescriptionList->name( $languageLocale );
00744     }
00745 
00746     /**
00747      * Sets description of attribute, store() will take care of writing back to serialized_description_list
00748      *
00749      * @param string $description
00750      * @param string|false $languageLocale Uses AlwaysAvailable language if false
00751      * @return string Return old value
00752      */
00753     function setDescription( $description, $languageLocale = false )
00754     {
00755         return $this->DescriptionList->setName( $description, $languageLocale );
00756     }
00757 
00758     /**
00759      * Returns description list for all locales for attribute
00760      *
00761      * @return array
00762      */
00763     function descriptionList()
00764     {
00765         return $this->DescriptionList->nameList();
00766     }
00767 
00768     /**
00769      * Returns data_text_i18n of attribute based on serialized_data_text
00770      *
00771      * @param string|false $languageLocale Uses AlwaysAvailable language if false
00772      * @return string
00773      */
00774     function dataTextI18n( $languageLocale = false )
00775     {
00776         return $this->DataTextI18nList->name( $languageLocale );
00777     }
00778 
00779     /**
00780      * Sets data_text_i18n of attribute, store() will take care of writing back to serialized_data_text
00781      *
00782      * @param string $string
00783      * @param string|false $languageLocale Uses AlwaysAvailable language if false
00784      * @return string Return old value
00785      */
00786     function setDataTextI18n( $string, $languageLocale = false )
00787     {
00788         return $this->DataTextI18nList->setName( $string, $languageLocale );
00789     }
00790 
00791     /**
00792      * Returns data_text_i18n list for all locales for attribute
00793      *
00794      * @return array
00795      */
00796     function dataTextI18nList()
00797     {
00798         return $this->DataTextI18nList->nameList();
00799     }
00800 
00801     /**
00802      * Returns locale code as set with {@link self::setEditLocale()}
00803      *
00804      * @return string|false
00805      */
00806     function editLocale()
00807     {
00808         return $this->EditLocale;
00809     }
00810 
00811     /**
00812      * Sets locale code of attribute for use by datatypes in class/edit storing process.
00813      *
00814      * @param string|false $languageLocale Uses AlwaysAvailable language if false
00815      */
00816     function setEditLocale( $languageLocale = false )
00817     {
00818         $this->EditLocale = $languageLocale;
00819     }
00820 
00821     /**
00822      * Specify AlwaysAvailableLanguage (for name, description or data_text_i18n)
00823      *
00824      * @param string|false $languageLocale
00825      */
00826     function setAlwaysAvailableLanguage( $languageLocale )
00827     {
00828         if ( $languageLocale )
00829         {
00830             $this->NameList->setAlwaysAvailableLanguage( $languageLocale );
00831             $this->DescriptionList->setAlwaysAvailableLanguage( $languageLocale );
00832             $this->DataTextI18nList->setAlwaysAvailableLanguage( $languageLocale );
00833         }
00834         else
00835         {
00836             $this->NameList->setAlwaysAvailableLanguage( false );
00837             $this->DescriptionList->setAlwaysAvailableLanguage( false );
00838             $this->DataTextI18nList->setAlwaysAvailableLanguage( false );
00839         }
00840     }
00841 
00842     /**
00843      * Removes an translation (as in the serilized strings for name, description or data_text_i18n)
00844      *
00845      * @param string $languageLocale
00846      */
00847     function removeTranslation( $languageLocale )
00848     {
00849         $this->NameList->removeName( $languageLocale );
00850         $this->DescriptionList->removeName( $languageLocale );
00851         $this->DataTextI18nList->removeName( $languageLocale );
00852     }
00853 
00854     /**
00855      * Resolves the string class attribute identifier $identifier to its numeric value
00856      * Use {@link eZContentObjectTreeNode::classAttributeIDByIdentifier()} for < 4.1.
00857      * If multiple classes have the same identifier, the first found is returned.
00858      *
00859      * @static
00860      * @since Version 4.1
00861      * @return int|false Returns classattributeid or false
00862      */
00863     public static function classAttributeIDByIdentifier( $identifier )
00864     {
00865         $identifierHash = self::classAttributeIdentifiersHash();
00866 
00867         if ( isset( $identifierHash[$identifier] ) )
00868             return $identifierHash[$identifier];
00869         else
00870             return false;
00871     }
00872 
00873     /**
00874      * Resolves the numeric class attribute identifier $id to its string value
00875      *
00876      * @static
00877      * @since Version 4.1
00878      * @return string|false Returns classattributeidentifier or false
00879      */
00880     public static function classAttributeIdentifierByID( $id )
00881     {
00882         $identifierHash = array_flip( self::classAttributeIdentifiersHash() );
00883 
00884         if ( isset( $identifierHash[$id] ) )
00885         {
00886             $classAndClassAttributeIdentifier = explode( '/', $identifierHash[$id] );
00887             return $classAndClassAttributeIdentifier[1];
00888         }
00889         else
00890             return false;
00891     }
00892 
00893     /**
00894      * Returns the class attribute identifier hash for the current database.
00895      * If it is outdated or non-existent, the method updates/generates the file
00896      *
00897      * @static
00898      * @since Version 4.1
00899      * @access protected
00900      * @return array Returns hash of classattributeidentifier => classattributeid
00901      */
00902     protected static function classAttributeIdentifiersHash()
00903     {
00904         if ( self::$identifierHash === null )
00905         {
00906             $db = eZDB::instance();
00907             $dbName = md5( $db->DB );
00908 
00909             $cacheDir = eZSys::cacheDirectory();
00910             $phpCache = new eZPHPCreator( $cacheDir,
00911                                           'classattributeidentifiers_' . $dbName . '.php',
00912                                           '',
00913                                           array( 'clustering' => 'classattridentifiers' ) );
00914 
00915             eZExpiryHandler::registerShutdownFunction();
00916             $handler = eZExpiryHandler::instance();
00917             $expiryTime = 0;
00918             if ( $handler->hasTimestamp( 'class-identifier-cache' ) )
00919             {
00920                 $expiryTime = $handler->timestamp( 'class-identifier-cache' );
00921             }
00922 
00923             if ( $phpCache->canRestore( $expiryTime ) )
00924             {
00925                 $var = $phpCache->restore( array( 'identifierHash' => 'identifier_hash' ) );
00926                 self::$identifierHash = $var['identifierHash'];
00927             }
00928             else
00929             {
00930                 // Fetch identifier/id pair from db
00931                 $query = "SELECT ezcontentclass_attribute.id as attribute_id, ezcontentclass_attribute.identifier as attribute_identifier, ezcontentclass.identifier as class_identifier
00932                           FROM ezcontentclass_attribute, ezcontentclass
00933                           WHERE ezcontentclass.id=ezcontentclass_attribute.contentclass_id";
00934                 $identifierArray = $db->arrayQuery( $query );
00935 
00936                 self::$identifierHash = array();
00937                 foreach ( $identifierArray as $identifierRow )
00938                 {
00939                     $combinedIdentifier = $identifierRow['class_identifier'] . '/' . $identifierRow['attribute_identifier'];
00940                     self::$identifierHash[$combinedIdentifier] = (int) $identifierRow['attribute_id'];
00941                 }
00942 
00943                 // Store identifier list to cache file
00944                 $phpCache->addVariable( 'identifier_hash', self::$identifierHash );
00945                 $phpCache->store();
00946             }
00947         }
00948         return self::$identifierHash;
00949     }
00950 
00951     /**
00952      * Initialize the attribute in the existing objects.
00953      *
00954      * @param mixed $objects not used, the existing objects are fetched if
00955      *        necessary (depending on the datatype of the attribute).
00956      */
00957     function initializeObjectAttributes( &$objects = null )
00958     {
00959         $classAttributeID = $this->ID;
00960         $classID = $this->ContentClassID;
00961         $dataType = $this->attribute( 'data_type' );
00962         if ( $dataType->supportsBatchInitializeObjectAttribute() )
00963         {
00964             $db = eZDB::instance();
00965 
00966             $data = array( 'contentobject_id'         => 'a.contentobject_id',
00967                            'version'                  => 'a.version',
00968                            'contentclassattribute_id' => $classAttributeID,
00969                            'data_type_string'         => "'" . $db->escapeString( $this->DataTypeString ) . "'",
00970                            'language_code'            => 'a.language_code',
00971                            'language_id'              => 'MAX(a.language_id)' );
00972 
00973             $datatypeData = $dataType->batchInitializeObjectAttributeData( $this );
00974             $data = array_merge( $data, $datatypeData );
00975 
00976             $cols = implode( ', ', array_keys( $data ) );
00977             $values = implode( ', ', $data );
00978 
00979             $sql = "INSERT INTO ezcontentobject_attribute( $cols )
00980             SELECT $values
00981             FROM ezcontentobject_attribute a, ezcontentobject o
00982             WHERE o.id = a.contentobject_id AND
00983                   o.contentclass_id=$classID
00984             GROUP BY contentobject_id,
00985                      version,
00986                      language_code";
00987 
00988             $db->query( $sql );
00989             // update ids to keep them same with one attribute for different versions
00990             if( $db->databaseName() == 'mysql' )
00991             {
00992                 $updateSql = "UPDATE ezcontentobject_attribute,
00993                                  ( SELECT contentobject_id, language_code, version, contentclassattribute_id, MIN( id ) AS minid
00994                                  FROM ezcontentobject_attribute WHERE contentclassattribute_id = $classAttributeID
00995                               GROUP BY contentobject_id, language_code, version, contentclassattribute_id ) t
00996                               SET ezcontentobject_attribute.id = t.minid
00997                               WHERE ezcontentobject_attribute.contentobject_id = t.contentobject_id
00998                               AND ezcontentobject_attribute.language_code = t.language_code
00999                               AND ezcontentobject_attribute.contentclassattribute_id = $classAttributeID";
01000             }
01001             else if( $db->databaseName() == 'postgresql' )
01002             {
01003                 $updateSql = "UPDATE ezcontentobject_attribute
01004                               SET id=t.minid FROM
01005                                 ( SELECT contentobject_id, language_code, version, contentclassattribute_id, MIN( id ) AS minid
01006                                  FROM ezcontentobject_attribute WHERE contentclassattribute_id = $classAttributeID
01007                                  GROUP BY contentobject_id, language_code, version, contentclassattribute_id ) t
01008                               WHERE ezcontentobject_attribute.contentobject_id = t.contentobject_id
01009                               AND ezcontentobject_attribute.language_code = t.language_code
01010                               AND ezcontentobject_attribute.contentclassattribute_id = $classAttributeID";
01011             }
01012             else if( $db->databaseName() == 'oracle' )
01013             {
01014                 $updateSql = "UPDATE ezcontentobject_attribute a SET a.id = (
01015                                  SELECT MIN( id ) FROM ezcontentobject_attribute b
01016                                  WHERE b.contentclassattribute_id = $classAttributeID
01017                                        AND b.contentobject_id = a.contentobject_id
01018                                        AND b.language_code = a.language_code )
01019                                 WHERE a.contentclassattribute_id = $classAttributeID";
01020             }
01021             else
01022             {
01023                 $updateSql = "";
01024             }
01025             $db->query( $updateSql );
01026         }
01027         else
01028         {
01029             $limit = 1000;
01030             $offset = 0;
01031             while ( true )
01032             {
01033                 $contentObjects = eZContentObject::fetchSameClassList( $classID, true, $offset, $limit );
01034                 if ( empty( $contentObjects ) )
01035                 {
01036                     break;
01037                 }
01038 
01039                 foreach ( $contentObjects as $object )
01040                 {
01041                     $contentobjectID = $object->attribute( 'id' );
01042                     $objectVersions = $object->versions();
01043                     // the start version ID, to make sure one attribute in different version has same id.
01044                     $startAttributeID = array();
01045                     foreach ( $objectVersions as $objectVersion )
01046                     {
01047                         $translations = $objectVersion->translations( false );
01048                         $version = $objectVersion->attribute( 'version' );
01049                         foreach ( $translations as $translation )
01050                         {
01051                             $objectAttribute = eZContentObjectAttribute::create( $classAttributeID, $contentobjectID, $version, $translation );
01052                             if( array_key_exists( $translation, $startAttributeID ) )
01053                             {
01054                                 $objectAttribute->setAttribute( 'id', $startAttributeID[$translation] );
01055                             }
01056                             $objectAttribute->setAttribute( 'language_code', $translation );
01057                             $objectAttribute->initialize();
01058                             $objectAttribute->store();
01059                             if( !array_key_exists( $translation, $startAttributeID ) )
01060                             {
01061                                 $startAttributeID[$translation] = $objectAttribute->attribute( 'id' );
01062                             }
01063                             $objectAttribute->postInitialize();
01064                         }
01065                     }
01066                 }
01067 
01068                 $offset += $limit;
01069                 eZContentObject::clearCache();
01070             }
01071         }
01072     }
01073 
01074     /**
01075      * Clears all content class attribute related caches
01076      *
01077      * @param int $contentClassAttributeID Specific attribute ID to clear cache for
01078      * @param int $contentClassID Specific attribute ID to clear cache for
01079      *
01080      * @return void
01081      * @since 4.2
01082      */
01083     public static function expireCache( $contentClassAttributeID = false, $contentClassID = false)
01084     {
01085         unset( $GLOBALS['eZContentClassAttributeCacheListFull'] );
01086         if ( $contentClassID !== false )
01087         {
01088             if ( isset( $GLOBALS['eZContentClassAttributeCacheList'][$contentClassID] ) )
01089             {
01090                 unset( $GLOBALS['eZContentClassAttributeCacheList'][$contentClassID] );
01091             }
01092         }
01093         else
01094         {
01095             unset( $GLOBALS['eZContentClassAttributeCacheList'] );
01096         }
01097         if ( $contentClassAttributeID !== false )
01098         {
01099             if ( isset( $GLOBALS['eZContentClassAttributeCache'][$contentClassAttributeID] ) )
01100             {
01101                 unset( $GLOBALS['eZContentClassAttributeCache'][$contentClassAttributeID] );
01102             }
01103         }
01104         else
01105         {
01106             unset( $GLOBALS['eZContentClassAttributeCache'] );
01107         }
01108 
01109         // expire cache file by timestamp
01110         $handler = eZExpiryHandler::instance();
01111         $handler->setTimestamp( 'class-identifier-cache', time() + 1 );
01112         $handler->store();
01113 
01114         // expire local, in-memory cache
01115         self::$identifierHash = null;
01116     }
01117 
01118     /// \privatesection
01119     /// Contains the content for this attribute
01120     public $Content;
01121     /// Contains information on how to display the current attribute in various viewmodes
01122     public $DisplayInfo;
01123     public $ID;
01124     public $Version;
01125     public $ContentClassID;
01126     public $Identifier;
01127     // serialized array of translated names
01128     public $SerializedNameList;
01129     // unserialized attribute names
01130     public $NameList;
01131     // unserialized attribute description
01132     public $DescriptionList;
01133     public $DataTypeString;
01134     public $Position;
01135     public $IsSearchable;
01136     public $IsRequired;
01137     public $IsInformationCollector;
01138     public $Module;
01139     // Used locale for use by datatypes in class/edit when storing data
01140     protected $EditLocale = false;
01141 
01142     /**
01143      * In-memory cache for class attributes identifiers / id matching
01144      */
01145     private static $identifierHash = null;
01146 }
01147 
01148 ?>