eZ Publish  [trunk]
ezcontentobject.php
Go to the documentation of this file.
00001 <?php
00002 /**
00003  * File containing the eZContentObject 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 eZContentObject ezcontentobject.php
00013   \ingroup eZKernel
00014   \brief Handles eZ Publish content objects
00015 
00016   It encapsulates the data for an object and provides lots of functions
00017   for dealing with versions, translations and attributes.
00018 
00019   \sa eZContentClass
00020 */
00021 
00022 class eZContentObject extends eZPersistentObject
00023 {
00024     const STATUS_DRAFT = 0;
00025     const STATUS_PUBLISHED = 1;
00026     const STATUS_ARCHIVED = 2;
00027 
00028     const PACKAGE_ERROR_NO_CLASS = 1;
00029     const PACKAGE_ERROR_EXISTS = 2;
00030     const PACKAGE_ERROR_NODE_EXISTS = 3;
00031     const PACKAGE_ERROR_MODIFIED = 101;
00032     const PACKAGE_ERROR_HAS_CHILDREN = 102;
00033 
00034     const PACKAGE_REPLACE = 1;
00035     const PACKAGE_SKIP = 2;
00036     const PACKAGE_NEW = 3;
00037     const PACKAGE_UPDATE = 6;
00038 
00039     const PACKAGE_DELETE = 4;
00040     const PACKAGE_KEEP = 5;
00041 
00042     const RELATION_COMMON = 1;
00043     const RELATION_EMBED = 2;
00044     const RELATION_LINK = 4;
00045     const RELATION_ATTRIBUTE = 8;
00046 
00047     function eZContentObject( $row )
00048     {
00049         $this->eZPersistentObject( $row );
00050         $this->ClassIdentifier = false;
00051         if ( isset( $row['contentclass_identifier'] ) )
00052             $this->ClassIdentifier = $row['contentclass_identifier'];
00053         $this->ClassName = false;
00054         if ( isset( $row['contentclass_name'] ) )
00055             $this->ClassName = $row['contentclass_name'];
00056         if ( isset( $row['serialized_name_list'] ) )
00057             $this->ClassName = eZContentClass::nameFromSerializedString( $row['serialized_name_list'] );
00058 
00059         $this->CurrentLanguage = false;
00060         if ( isset( $row['content_translation'] ) )
00061         {
00062             $this->CurrentLanguage = $row['content_translation'];
00063         }
00064         else if ( isset( $row['real_translation'] ) )
00065         {
00066             $this->CurrentLanguage = $row['real_translation'];
00067         }
00068         else if ( isset( $row['language_mask'] ) )
00069         {
00070             $topPriorityLanguage = eZContentLanguage::topPriorityLanguageByMask( $row['language_mask'] );
00071             if ( $topPriorityLanguage )
00072             {
00073                $this->CurrentLanguage = $topPriorityLanguage->attribute( 'locale' );
00074             }
00075         }
00076     }
00077 
00078     static function definition()
00079     {
00080         static $definition = array( "fields" => array( "id" => array( 'name' => 'ID',
00081                                                         'datatype' => 'integer',
00082                                                         'default' => 0,
00083                                                         'required' => true ),
00084                                          "section_id" => array( 'name' => "SectionID",
00085                                                                 'datatype' => 'integer',
00086                                                                 'default' => 0,
00087                                                                 'required' => true,
00088                                                                 'foreign_class' => 'eZSection',
00089                                                                 'foreign_attribute' => 'id',
00090                                                                 'multiplicity' => '1..*' ),
00091                                          "owner_id" => array( 'name' => "OwnerID",
00092                                                               'datatype' => 'integer',
00093                                                               'default' => 0,
00094                                                               'required' => true,
00095                                                               'foreign_class' => 'eZUser',
00096                                                               'foreign_attribute' => 'contentobject_id',
00097                                                               'multiplicity' => '1..*'),
00098                                          "contentclass_id" => array( 'name' => "ClassID",
00099                                                                      'datatype' => 'integer',
00100                                                                      'default' => 0,
00101                                                                      'required' => true,
00102                                                                      'foreign_class' => 'eZContentClass',
00103                                                                      'foreign_attribute' => 'id',
00104                                                                      'multiplicity' => '1..*' ),
00105                                          "name" => array( 'name' => "Name",
00106                                                           'datatype' => 'string',
00107                                                           'default' => '',
00108                                                           'required' => true ),
00109                                          "published" => array( 'name' => "Published",
00110                                                                'datatype' => 'integer',
00111                                                                'default' => 0,
00112                                                                'required' => true ),
00113                                          "modified" => array( 'name' => "Modified",
00114                                                               'datatype' => 'integer',
00115                                                               'default' => 0,
00116                                                               'required' => true ),
00117                                          "current_version" => array( 'name' => "CurrentVersion",
00118                                                                      'datatype' => 'integer',
00119                                                                      'default' => 0,
00120                                                                      'required' => true ),
00121                                          "status" => array( 'name' => "Status",
00122                                                             'datatype' => 'integer',
00123                                                             'default' => 0,
00124                                                             'required' => true ),
00125                                          'remote_id' => array( 'name' => "RemoteID",
00126                                                                'datatype' => 'string',
00127                                                                'default' => '',
00128                                                                'required' => true ),
00129                                          'language_mask' => array( 'name' => 'LanguageMask',
00130                                                                    'datatype' => 'integer',
00131                                                                    'default' => 0,
00132                                                                    'required' => true ),
00133                                          'initial_language_id' => array( 'name' => 'InitialLanguageID',
00134                                                                          'datatype' => 'integer',
00135                                                                          'default' => 0,
00136                                                                          'required' => true,
00137                                                                          'foreign_class' => 'eZContentLanguage',
00138                                                                          'foreign_attribute' => 'id',
00139                                                                          'multiplicity' => '1..*' ) ),
00140                       "keys" => array( "id" ),
00141                       "function_attributes" => array( "current" => "currentVersion",
00142                                                       'versions' => 'versions',
00143                                                       'author_array' => 'authorArray',
00144                                                       "class_name" => "className",
00145                                                       "content_class" => "contentClass",
00146                                                       "contentobject_attributes" => "contentObjectAttributes",
00147                                                       "owner" => "owner",
00148                                                       "related_contentobject_array" => "relatedContentObjectList",
00149                                                       "related_contentobject_count" => "relatedContentObjectCount",
00150                                                       'reverse_related_contentobject_array' => 'reverseRelatedObjectList',
00151                                                       'reverse_related_contentobject_count' => 'reverseRelatedObjectCount',
00152                                                       "linked_contentobject_array" => "linkedContentObjectList",
00153                                                       "linked_contentobject_count" => "linkedContentObjectCount",
00154                                                       'reverse_linked_contentobject_array' => 'reverseLinkedObjectList',
00155                                                       'reverse_linked_contentobject_count' => 'reverseLinkedObjectCount',
00156                                                       "embedded_contentobject_array" => "embeddedContentObjectList",
00157                                                       "embedded_contentobject_count" => "embeddedContentObjectCount",
00158                                                       'reverse_embedded_contentobject_array' => 'reverseEmbeddedObjectList',
00159                                                       'reverse_embedded_contentobject_count' => 'reverseEmbeddedObjectCount',
00160                                                       "can_read" => "canRead",
00161                                                       "can_pdf" => "canPdf",
00162                                                       "can_diff" => "canDiff",
00163                                                       "can_create" => "canCreate",
00164                                                       "can_create_class_list" => "canCreateClassList",
00165                                                       "can_edit" => "canEdit",
00166                                                       "can_translate" => "canTranslate",
00167                                                       "can_remove" => "canRemove",
00168                                                       "can_move" => "canMoveFrom",
00169                                                       "can_move_from" => "canMoveFrom",
00170                                                       'can_view_embed' => 'canViewEmbed',
00171                                                       "data_map" => "dataMap",
00172                                                       "grouped_data_map" => "groupedDataMap",
00173                                                       "main_parent_node_id" => "mainParentNodeID",
00174                                                       "assigned_nodes" => "assignedNodes",
00175                                                       "parent_nodes" => "parentNodeIDArray",
00176                                                       "main_node_id" => "mainNodeID",
00177                                                       "main_node" => "mainNode",
00178                                                       "default_language" => "defaultLanguage",
00179                                                       "content_action_list" => "contentActionList",
00180                                                       "class_identifier" => "contentClassIdentifier",
00181                                                       'class_group_id_list' => 'contentClassGroupIDList',
00182                                                       'name' => 'name',
00183                                                       'match_ingroup_id_list' => 'matchIngroupIDList',
00184                                                       'remote_id' => 'remoteID',
00185                                                       'current_language' => 'currentLanguage',
00186                                                       'current_language_object' => 'currentLanguageObject',
00187                                                       'initial_language' => 'initialLanguage',
00188                                                       'initial_language_code' => 'initialLanguageCode',
00189                                                       'available_languages' => 'availableLanguages',
00190                                                       'language_codes' => 'availableLanguages',
00191                                                       'language_js_array' => 'availableLanguagesJsArray',
00192                                                       'languages' => 'languages',
00193                                                       'all_languages' => 'allLanguages',
00194                                                       'can_edit_languages' => 'canEditLanguages',
00195                                                       'can_create_languages' => 'canCreateLanguages',
00196                                                       'always_available' => 'isAlwaysAvailable',
00197                                                       'allowed_assign_section_list' => 'allowedAssignSectionList',
00198                                                       'allowed_assign_state_id_list' => 'allowedAssignStateIDList',
00199                                                       'allowed_assign_state_list' => 'allowedAssignStateList',
00200                                                       'state_id_array' => 'stateIDArray',
00201                                                       'state_identifier_array' => 'stateIdentifierArray' ),
00202                       "increment_key" => "id",
00203                       "class_name" => "eZContentObject",
00204                       "sort" => array( "id" => "asc" ),
00205                       "name" => "ezcontentobject" );
00206         return $definition;
00207     }
00208 
00209     /*!
00210      Get class groups this object's class belongs to if match for class groups is enabled.
00211 
00212      \return array of class group ids. False if match is disabled.
00213     */
00214     function matchIngroupIDList()
00215     {
00216         $contentINI = eZINI::instance( 'content.ini' );
00217         $inList = false;
00218         if( $contentINI->variable( 'ContentOverrideSettings', 'EnableClassGroupOverride' ) == 'true' )
00219         {
00220             $contentClass = $this->contentClass();
00221             $inList = $contentClass->attribute( 'ingroup_id_list' );
00222         }
00223         return $inList;
00224     }
00225 
00226     /*!
00227      Store the object
00228      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
00229      the calls within a db transaction; thus within db->begin and db->commit.
00230     */
00231     function store( $fieldFilters = null )
00232     {
00233         // Unset the cache
00234         global $eZContentObjectContentObjectCache;
00235         unset( $eZContentObjectContentObjectCache[$this->ID] );
00236         global $eZContentObjectDataMapCache;
00237         unset( $eZContentObjectDataMapCache[$this->ID] );
00238         global $eZContentObjectVersionCache;
00239         unset( $eZContentObjectVersionCache[$this->ID] );
00240 
00241         $db = eZDB::instance();
00242         $db->begin();
00243         $this->storeNodeModified();
00244         eZPersistentObject::store( $fieldFilters );
00245         $db->commit();
00246     }
00247 
00248     /*!
00249      Clear in-memory caches.
00250      \param  $idArray  objects to clear caches for.
00251 
00252      If the parameter is ommitted the caches are cleared for all objects.
00253     */
00254 
00255     static function clearCache( $idArray = array() )
00256     {
00257         if ( is_numeric( $idArray ) )
00258             $idArray = array( $idArray );
00259 
00260         // clear in-memory cache for all objects
00261         if ( count( $idArray ) == 0 )
00262         {
00263             unset( $GLOBALS['eZContentObjectContentObjectCache'] );
00264             unset( $GLOBALS['eZContentObjectDataMapCache'] );
00265             unset( $GLOBALS['eZContentObjectVersionCache'] );
00266 
00267             return;
00268         }
00269 
00270         // clear in-memory cache for specified object(s)
00271         global $eZContentObjectContentObjectCache;
00272         global $eZContentObjectDataMapCache;
00273         global $eZContentObjectVersionCache;
00274         foreach ( $idArray as $objectID )
00275         {
00276             unset( $eZContentObjectContentObjectCache[$objectID] );
00277             unset( $eZContentObjectDataMapCache[$objectID] );
00278             unset( $eZContentObjectVersionCache[$objectID] );
00279         }
00280     }
00281 
00282     /*!
00283      Update all nodes to set modified_subnode value
00284      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
00285      the calls within a db transaction; thus within db->begin and db->commit.
00286     */
00287     function storeNodeModified()
00288     {
00289         if ( is_numeric( $this->ID ) )
00290         {
00291             $nodeArray = $this->assignedNodes();
00292 
00293             $db = eZDB::instance();
00294             $db->begin();
00295             foreach ( array_keys( $nodeArray ) as $key )
00296             {
00297                 $nodeArray[$key]->updateAndStoreModified();
00298             }
00299             $db->commit();
00300         }
00301     }
00302 
00303     function name( $version = false , $lang = false )
00304     {
00305         // if the object id is null, we can't read data from the database
00306         // and return the locally known name
00307         if ( $this->attribute( 'id' ) === null )
00308         {
00309             return $this->Name;
00310         }
00311         if ( !$version )
00312         {
00313             $version = $this->attribute( 'current_version' );
00314         }
00315         if ( !$lang && $this->CurrentLanguage )
00316         {
00317             $lang = $this->CurrentLanguage;
00318         }
00319 
00320         return $this->versionLanguageName( $version, $lang );
00321     }
00322 
00323     function names()
00324     {
00325         $version = $this->attribute( 'current_version' );
00326         $id = $this->attribute( 'id' );
00327 
00328         $db = eZDB::instance();
00329         $rows = $db->arrayQuery( "SELECT name, real_translation FROM ezcontentobject_name WHERE contentobject_id = '$id' AND content_version='$version'" );
00330         $names = array();
00331         foreach ( $rows as $row )
00332         {
00333             $names[$row['real_translation']] = $row['name'];
00334         }
00335 
00336         return $names;
00337     }
00338 
00339     function versionLanguageName( $version, $lang = false )
00340     {
00341         $name = false;
00342         if ( !$version > 0 )
00343         {
00344             eZDebug::writeNotice( "There is no object name for version($version) of the content object ($contentObjectID) in language($lang)", __METHOD__ );
00345             return $name;
00346         }
00347         $db = eZDb::instance();
00348         $contentObjectID = $this->attribute( 'id' );
00349         if ( !$lang )
00350         {
00351             // If $lang not given we will use the initial language of the object
00352             $query = "SELECT initial_language_id FROM ezcontentobject WHERE id='$contentObjectID'";
00353             $rows = $db->arrayQuery( $query );
00354             if ( $rows )
00355             {
00356                 $languageID = $rows[0]['initial_language_id'];
00357                 $language = eZContentLanguage::fetch( $languageID );
00358                 if ( $language )
00359                 {
00360                     $lang = $language->attribute( 'locale' );
00361                 }
00362                 else
00363                 {
00364                     return $name;
00365                 }
00366             }
00367             else
00368             {
00369                 return $name;
00370             }
00371         }
00372         $lang = $db->escapeString( $lang );
00373         $version = (int) $version;
00374 
00375         $languageID = $this->attribute( 'initial_language_id' );
00376         if ( $this->attribute( 'always_available' ) )
00377         {
00378             $languageID = (int) $languageID | 1;
00379         }
00380 
00381         $query= "SELECT name, content_translation
00382                  FROM ezcontentobject_name
00383                  WHERE contentobject_id = '$contentObjectID'
00384                        AND content_version = '$version'
00385                        AND ( content_translation = '$lang' OR language_id = '$languageID' )";
00386         $result = $db->arrayQuery( $query );
00387 
00388         $resCount = count( $result );
00389         if( $resCount < 1 )
00390         {
00391             eZDebug::writeNotice( "There is no object name for version($version) of the content object ($contentObjectID) in language($lang)", __METHOD__ );
00392         }
00393         else if( $resCount > 1 )
00394         {
00395             // we have name in requested language => find and return it
00396             foreach( $result as $row )
00397             {
00398                 if( $row['content_translation'] == $lang )
00399                 {
00400                     $name = $row['name'];
00401                     break;
00402                 }
00403             }
00404         }
00405         else
00406         {
00407             // we don't have name in requested language(or requested language is the same as initial language) => use name in initial language
00408             $name = $result[0]['name'];
00409         }
00410 
00411         return $name;
00412     }
00413 
00414     /*!
00415      Sets the name of the object, in memory only. Use setName() to change it.
00416     */
00417     function setCachedName( $name )
00418     {
00419         $this->Name = $name;
00420     }
00421 
00422     /*!
00423      Sets the name of the object in all translations.
00424      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
00425      the calls within a db transaction; thus within db->begin and db->commit.
00426     */
00427     function setName( $objectName, $versionNum = false, $languageCode = false )
00428     {
00429         $initialLanguageCode = false;
00430         if ( $initialLanguage = $this->initialLanguage() )
00431         {
00432             $initialLanguageCode = $initialLanguage->attribute( 'locale' );
00433         }
00434         $db = eZDB::instance();
00435 
00436         if ( $languageCode == false )
00437         {
00438             $languageCode = $initialLanguageCode;
00439         }
00440         $languageCode = $db->escapeString( $languageCode );
00441         if ( $languageCode == $initialLanguageCode )
00442         {
00443             $this->Name = $objectName;
00444         }
00445 
00446         if ( !$versionNum )
00447         {
00448             $versionNum = $this->attribute( 'current_version' );
00449         }
00450         $objectID =(int) $this->attribute( 'id' );
00451         $versionNum =(int) $versionNum;
00452 
00453         $languageID =(int) eZContentLanguage::idByLocale( $languageCode );
00454 
00455         $objectName = $db->escapeString( $objectName );
00456 
00457         $db->begin();
00458 
00459         // Check if name is already set before setting/changing it.
00460         // This helps to avoid deadlocks in mysql: a pair of DELETE/INSERT might cause deadlock here
00461         // in case of concurrent execution.
00462         $rows = $db->arrayQuery( "SELECT COUNT(*) AS count FROM ezcontentobject_name WHERE contentobject_id = '$objectID'
00463                                  AND content_version = '$versionNum' AND content_translation ='$languageCode'" );
00464         if ( $rows[0]['count'] )
00465         {
00466             $db->query( "UPDATE ezcontentobject_name SET name='$objectName'
00467                          WHERE
00468                          contentobject_id = '$objectID'  AND
00469                          content_version = '$versionNum' AND
00470                          content_translation ='$languageCode'" );
00471         }
00472         else
00473         {
00474             $db->query( "INSERT INTO ezcontentobject_name( contentobject_id,
00475                                                            name,
00476                                                            content_version,
00477                                                            content_translation,
00478                                                            real_translation,
00479                                                            language_id )
00480                                 VALUES( '$objectID',
00481                                         '$objectName',
00482                                         '$versionNum',
00483                                         '$languageCode',
00484                                         '$languageCode',
00485                                         '$languageID' )" );
00486         }
00487 
00488         $db->commit();
00489     }
00490 
00491     /*!
00492      \return a map with all the content object attributes where the keys are the
00493              attribute identifiers.
00494     */
00495     function dataMap()
00496     {
00497         return $this->fetchDataMap();
00498     }
00499 
00500     /**
00501      * Generates a map with all the content object attributes where the keys are
00502      * the attribute identifiers grouped by class attribute category.
00503      *
00504      * @note Result is not cached, so make sure you don't call this over and over.
00505      *
00506      * @return array
00507      */
00508     public function groupedDataMap()
00509     {
00510         return self::createGroupedDataMap( $this->fetchDataMap() );
00511     }
00512 
00513     /**
00514      * Generates a map with all the content object attributes where the keys are
00515      * the attribute identifiers grouped by class attribute category.
00516      *
00517      * @note Result is not cached, so make sure you don't call this over and over.
00518      *
00519      * @param array $contentObjectAttributes Array of eZContentObjectAttribute objects
00520      * @return array
00521      */
00522     public static function createGroupedDataMap( $contentObjectAttributes )
00523     {
00524         $groupedDataMap  = array();
00525         $contentINI      = eZINI::instance( 'content.ini' );
00526         $categorys       = $contentINI->variable( 'ClassAttributeSettings', 'CategoryList' );
00527         $defaultCategory = $contentINI->variable( 'ClassAttributeSettings', 'DefaultCategory' );
00528         foreach( $contentObjectAttributes as $attribute )
00529         {
00530             $classAttribute      = $attribute->contentClassAttribute();
00531             $attributeCategory   = $classAttribute->attribute('category');
00532             $attributeIdentifier = $classAttribute->attribute( 'identifier' );
00533             if ( !isset( $categorys[ $attributeCategory ] ) || !$attributeCategory )
00534                 $attributeCategory = $defaultCategory;
00535 
00536             if ( !isset( $groupedDataMap[ $attributeCategory ] ) )
00537                 $groupedDataMap[ $attributeCategory ] = array();
00538 
00539             $groupedDataMap[ $attributeCategory ][$attributeIdentifier] = $attribute;
00540 
00541         }
00542         return $groupedDataMap;
00543     }
00544 
00545     /*!
00546      \return a map with all the content object attributes where the keys are the
00547              attribute identifiers.
00548      \sa eZContentObjectTreeNode::dataMap
00549     */
00550     function fetchDataMap( $version = false, $language = false )
00551     {
00552         // Global variable to cache datamaps
00553         global $eZContentObjectDataMapCache;
00554 
00555         if ( $version == false )
00556             $version = $this->attribute( 'current_version' );
00557 
00558         if ( $language == false )
00559         {
00560             $language = $this->CurrentLanguage;
00561         }
00562 
00563         if ( !$language || !isset( $eZContentObjectDataMapCache[$this->ID][$version][$language] ) )
00564         {
00565             $data = $this->contentObjectAttributes( true, $version, $language );
00566 
00567             if ( !$language )
00568             {
00569                 $language = $this->CurrentLanguage;
00570             }
00571 
00572             // Store the attributes for later use
00573             $this->ContentObjectAttributeArray[$version][$language] = $data;
00574             $eZContentObjectDataMapCache[$this->ID][$version][$language] = $data;
00575         }
00576         else
00577         {
00578             $data = $eZContentObjectDataMapCache[$this->ID][$version][$language];
00579         }
00580 
00581         if ( !isset( $this->DataMap[$version][$language] ) )
00582         {
00583             $ret = array();
00584             foreach( $data as $key => $item )
00585             {
00586                 $identifier = $item->contentClassAttributeIdentifier();
00587                 $ret[$identifier] = $data[$key];
00588             }
00589             $this->DataMap[$version][$language] = $ret;
00590         }
00591         else
00592         {
00593             $ret = $this->DataMap[$version][$language];
00594         }
00595         return $ret;
00596     }
00597 
00598     function resetDataMap()
00599     {
00600         $this->ContentObjectAttributeArray = array();
00601         $this->ContentObjectAttributes = array();
00602         $this->DataMap = array();
00603         return $this->DataMap;
00604     }
00605 
00606     /*!
00607       Fetch a set of content object attributes by their class identifiers.
00608     */
00609     function fetchAttributesByIdentifier( $identifierArray, $version = false, $languageArray = false, $asObject = true )
00610     {
00611         if ( count( $identifierArray ) === 0 )
00612         {
00613             return null;
00614         }
00615 
00616         $db  = eZDB::instance();
00617 
00618         $identifierQuotedString = array();
00619         foreach ( $identifierArray as $identifier )
00620         {
00621             $identifierQuotedString[] = "'$identifier'";
00622         }
00623 
00624         if ( !$version or !is_numeric( $version ) )
00625         {
00626             $version = $this->CurrentVersion;
00627         }
00628 
00629         if ( is_array( $languageArray ) )
00630         {
00631             $langCodeQuotedString = array();
00632             foreach ( $languageArray as $langCode )
00633             {
00634                 if ( is_string( $langCode ) )
00635                     $langCodeQuotedString[] = "'$langCode'";
00636             }
00637 
00638             if ( !empty( $langCodeQuotedString ) )
00639             {
00640                 $languageText = "AND ";
00641                 $languageText .= $db->generateSQLINStatement( $langCodeQuotedString, 'ezcontentobject_attribute.language_code' );
00642             }
00643         }
00644 
00645         if ( !isset( $languageText ) )
00646         {
00647             $languageText = "AND " . eZContentLanguage::sqlFilter( 'ezcontentobject_attribute', 'ezcontentobject_version' );
00648         }
00649 
00650         $versionText = "AND ezcontentobject_attribute.version = '$version'";
00651 
00652         $query = "SELECT ezcontentobject_attribute.*, ezcontentclass_attribute.identifier as identifier
00653             FROM ezcontentobject_attribute, ezcontentclass_attribute, ezcontentobject_version
00654             WHERE
00655                 ezcontentclass_attribute.version = ". eZContentClass::VERSION_STATUS_DEFINED . " AND
00656                 ezcontentclass_attribute.id = ezcontentobject_attribute.contentclassattribute_id AND
00657                 ezcontentobject_version.contentobject_id = {$this->ID} AND
00658                 ezcontentobject_version.version = {$version} AND
00659                 ezcontentobject_attribute.contentobject_id = {$this->ID}
00660 
00661                 {$languageText}
00662 
00663                 {$versionText}
00664 
00665                 AND
00666                 ";
00667 
00668         $query .= $db->generateSQLINStatement( $identifierQuotedString, 'identifier' );
00669 
00670         $rows = $db->arrayQuery( $query );
00671 
00672         if ( count( $rows ) > 0 )
00673         {
00674             if ( $asObject )
00675             {
00676                 $returnArray = array();
00677                 foreach( $rows as $row )
00678                 {
00679                     $returnArray[$row['id']] = new eZContentObjectAttribute( $row );
00680                 }
00681                 return $returnArray;
00682             }
00683             else
00684             {
00685                 return $rows;
00686             }
00687         }
00688         return null;
00689     }
00690 
00691     /*!
00692      Returns the owner of the object as a content object.
00693     */
00694     function owner()
00695     {
00696         if ( $this->OwnerID != 0 )
00697         {
00698             return eZContentObject::fetch( $this->OwnerID );
00699         }
00700         return null;
00701     }
00702 
00703     /*!
00704      \return the content class group identifiers for the current content object
00705     */
00706     function contentClassGroupIDList()
00707     {
00708         $contentClass = $this->contentClass();
00709         return $contentClass->attribute( 'ingroup_id_list' );
00710     }
00711 
00712     /*!
00713      \return the content class identifer for the current content object
00714 
00715      \note The object will cache the class name information so multiple calls will be fast.
00716     */
00717     function contentClassIdentifier()
00718     {
00719         if ( !is_numeric( $this->ClassID ) )
00720         {
00721             $retValue = null;
00722             return $retValue;
00723         }
00724 
00725         if ( $this->ClassIdentifier !== false )
00726             return $this->ClassIdentifier;
00727 
00728         $this->ClassIdentifier = eZContentClass::classIdentifierByID( $this->ClassID );
00729 
00730         return $this->ClassIdentifier;
00731     }
00732 
00733     /*!
00734      \return the content class for the current content object
00735     */
00736     function contentClass()
00737     {
00738         if ( !is_numeric( $this->ClassID ) )
00739         {
00740             $retValue = null;
00741             return $retValue;
00742         }
00743 
00744         return eZContentClass::fetch( $this->ClassID );
00745     }
00746 
00747     /*!
00748      Get remote id of content object
00749     */
00750     function remoteID()
00751     {
00752         $remoteID = eZPersistentObject::attribute( 'remote_id', true );
00753 
00754         // Ensures that we provide the correct remote_id if we have one in the database
00755         if ( $remoteID === null and $this->attribute( 'id' ) )
00756         {
00757             $db = eZDB::instance();
00758             $resultArray = $db->arrayQuery( "SELECT remote_id FROM ezcontentobject WHERE id = '" . $this->attribute( 'id' ) . "'" );
00759             if ( count( $resultArray ) == 1 )
00760             {
00761                 $remoteID = $resultArray[0]['remote_id'];
00762                 $this->setAttribute( 'remote_id',  $remoteID );
00763             }
00764         }
00765 
00766         if ( !$remoteID )
00767         {
00768             $this->setAttribute( 'remote_id', eZRemoteIdUtility::generate( 'object' ) );
00769             if ( $this->attribute( 'id' ) !== null )
00770                 $this->sync( array( 'remote_id' ) );
00771             $remoteID = eZPersistentObject::attribute( 'remote_id', true );
00772         }
00773 
00774         return $remoteID;
00775     }
00776 
00777     function mainParentNodeID()
00778     {
00779         $list = eZContentObjectTreeNode::getParentNodeIdListByContentObjectID( $this->ID, false, true );
00780         return isset( $list[0] ) ? $list[0] : null;
00781     }
00782 
00783     /*!
00784      Fetches contentobject by remote ID, returns null if none exist
00785     */
00786     static function fetchByRemoteID( $remoteID, $asObject = true )
00787     {
00788         $db = eZDB::instance();
00789         $remoteID =$db->escapeString( $remoteID );
00790         $resultArray = $db->arrayQuery( 'SELECT id FROM ezcontentobject WHERE remote_id=\'' . $remoteID . '\'' );
00791         if ( count( $resultArray ) != 1 )
00792             $object = null;
00793         else
00794             $object = eZContentObject::fetch( $resultArray[0]['id'], $asObject );
00795         return $object;
00796     }
00797 
00798     /**
00799      * Fetches a content object by ID
00800      * @param int $id ID of the content object to fetch
00801      * @param bool $asObject
00802      *        Return the result as an object (true) or an assoc. array (false)
00803      *
00804      * @return eZContentObject
00805      */
00806     static function fetch( $id, $asObject = true )
00807     {
00808         global $eZContentObjectContentObjectCache;
00809 
00810         // If the object given by its id is not cached or should be returned as array
00811         // then we fetch it from the DB (objects are always cached as arrays).
00812         if ( !isset( $eZContentObjectContentObjectCache[$id] ) or $asObject === false )
00813         {
00814             $db = eZDB::instance();
00815 
00816             $resArray = $db->arrayQuery( eZContentObject::createFetchSQLString( $id ) );
00817 
00818             $objectArray = array();
00819             if ( count( $resArray ) == 1 && $resArray !== false )
00820             {
00821                 $objectArray = $resArray[0];
00822             }
00823             else
00824             {
00825                 eZDebug::writeError( "Object not found ($id)", __METHOD__ );
00826                 $retValue = null;
00827                 return $retValue;
00828             }
00829 
00830             if ( $asObject )
00831             {
00832                 $obj = new eZContentObject( $objectArray );
00833                 $eZContentObjectContentObjectCache[$id] = $obj;
00834             }
00835             else
00836             {
00837                 return $objectArray;
00838             }
00839 
00840             return $obj;
00841         }
00842         else
00843         {
00844             return $eZContentObjectContentObjectCache[$id];
00845         }
00846     }
00847 
00848     /*!
00849      \static
00850      Tests for the existance of a content object by using the ID \a $id.
00851      \return \c true if the object exists, \c false otherwise.
00852      \note Uses the static function createFetchSQLString() to generate the SQL
00853     */
00854     static function exists( $id )
00855     {
00856         global $eZContentObjectContentObjectCache;
00857 
00858         // Check the global cache
00859         if ( isset( $eZContentObjectContentObjectCache[$id] ) )
00860             return true;
00861 
00862         // If the object is not cached we need to check the DB
00863         $db = eZDB::instance();
00864 
00865         $resArray = $db->arrayQuery( eZContentObject::createFetchSQLString( $id ) );
00866 
00867         if ( $resArray !== false and count( $resArray ) == 1 )
00868         {
00869             return true;
00870         }
00871 
00872         return false;
00873 
00874     }
00875 
00876     /*!
00877       \static
00878       Creates the SQL for fetching the object with ID \a $id and returns the string.
00879     */
00880     static function createFetchSQLString( $id )
00881     {
00882         $id = (int) $id;
00883 
00884         $fetchSQLString = "SELECT ezcontentobject.*,
00885                                ezcontentclass.serialized_name_list as serialized_name_list,
00886                                ezcontentclass.identifier as contentclass_identifier,
00887                                ezcontentclass.is_container as is_container
00888                            FROM
00889                                ezcontentobject,
00890                                ezcontentclass
00891                            WHERE
00892                                ezcontentobject.id='$id' AND
00893                                ezcontentclass.id = ezcontentobject.contentclass_id AND
00894                                ezcontentclass.version=0";
00895 
00896         return $fetchSQLString;
00897     }
00898 
00899     /**
00900      * Creates the SQL for filtering objects by visibility, used by IgnoreVisibility on some fetches.
00901      * The object is visible if 1 or more assigned nodes are visible.
00902      *
00903      * @static
00904      * @since Version 4.1
00905      * @param bool $IgnoreVisibility ignores visibility if true
00906      * @param string $ezcontentobjectTable name of ezcontentobject table used in sql
00907      * @return string with sql condition for node filtering by visibility
00908      */
00909     static function createFilterByVisibilitySQLString( $IgnoreVisibility = false, $ezcontentobjectTable = 'ezcontentobject' )
00910     {
00911         if ( $IgnoreVisibility )
00912             return '';
00913         return " AND ( SELECT MIN( ezct.is_invisible ) FROM ezcontentobject_tree ezct WHERE ezct.contentobject_id = $ezcontentobjectTable.id ) = 0 ";
00914     }
00915 
00916     /**
00917      * Fetches the contentobject which has a node with ID $nodeID
00918      * $nodeID can also be an array of NodeIDs. In this case, an array of content objects will be returned
00919      * @param int|array $nodeID Single nodeID or array of NodeIDs
00920      * @param bool $asObject If results have to be returned as eZContentObject instances or not
00921      * @return mixed Content object or array of content objects.
00922      *               Content objects can be eZContentObject instances or array result sets
00923      */
00924     static function fetchByNodeID( $nodeID, $asObject = true )
00925     {
00926         global $eZContentObjectContentObjectCache;
00927         $resultAsArray = is_array( $nodeID );
00928         $nodeID = (array)$nodeID;
00929 
00930         $db = eZDB::instance();
00931 
00932         $resArray = $db->arrayQuery(
00933             "SELECT co.*, con.name as name, con.real_translation, cot.node_id
00934              FROM ezcontentobject co
00935              JOIN ezcontentobject_tree cot ON co.id = cot.contentobject_id AND co.current_version = cot.contentobject_version
00936              JOIN ezcontentobject_name con ON co.id = con.contentobject_id AND co.current_version = con.content_version
00937              WHERE " .
00938                 $db->generateSQLINStatement( $nodeID, 'cot.node_id', false, true, 'int' ) . " AND " .
00939                 eZContentLanguage::sqlFilter( 'con', 'co' )
00940         );
00941 
00942         if ( $resArray === false || empty( $resArray ) )
00943         {
00944             eZDebug::writeError( 'A problem occured while fetching objects with following NodeIDs : ' . implode( ', ', $nodeID ), __METHOD__ );
00945             return $resultAsArray ? array() : null;
00946         }
00947 
00948         $objectArray = array();
00949         if ( $asObject )
00950         {
00951             foreach ( $resArray as $res )
00952             {
00953                 $objectArray[$res['node_id']] = $eZContentObjectContentObjectCache[$res['id']] = new self( $res );
00954             }
00955         }
00956         else
00957         {
00958             foreach ( $resArray as $res )
00959             {
00960                 $objectArray[$res['node_id']] = $res;
00961             }
00962         }
00963 
00964         if ( !$resultAsArray )
00965             return $objectArray[$res['node_id']];
00966 
00967         return $objectArray;
00968     }
00969 
00970     /**
00971      * Fetches a content object list based on an array of content object ids
00972      *
00973      * @param array $idArray array of content object ids
00974      * @param bool $asObject
00975      *        Wether to get the result as an array of eZContentObject or an
00976      *        array of associative arrays
00977      *
00978      * @return array(contentObjectID => eZContentObject|array)
00979      *         array of eZContentObject (if $asObject = true) or array of
00980      *         associative arrays (if $asObject = false)
00981      */
00982     static function fetchIDArray( $idArray, $asObject = true )
00983     {
00984         global $eZContentObjectContentObjectCache;
00985 
00986         $db = eZDB::instance();
00987 
00988         $resRowArray = $db->arrayQuery(
00989             "SELECT ezcontentclass.serialized_name_list as class_serialized_name_list, ezcontentobject.*, ezcontentobject_name.name as name,  ezcontentobject_name.real_translation
00990              FROM
00991                 ezcontentclass,
00992                 ezcontentobject,
00993                 ezcontentobject_name
00994              WHERE
00995                 ezcontentclass.id=ezcontentobject.contentclass_id AND " .
00996                 // All elements from $idArray should be casted to (int)
00997                 $db->generateSQLINStatement( $idArray, 'ezcontentobject.id', false, true, 'int' ) . " AND
00998                 ezcontentobject.id = ezcontentobject_name.contentobject_id AND
00999                 ezcontentobject.current_version = ezcontentobject_name.content_version AND " .
01000                 eZContentLanguage::sqlFilter( 'ezcontentobject_name', 'ezcontentobject' )
01001         );
01002 
01003         $objectRetArray = array();
01004         foreach ( $resRowArray as $resRow )
01005         {
01006             $objectID = $resRow['id'];
01007             $resRow['class_name'] = eZContentClass::nameFromSerializedString( $resRow['class_serialized_name_list'] );
01008             if ( $asObject )
01009             {
01010                 $obj = new eZContentObject( $resRow );
01011                 $obj->ClassName = $resRow['class_name'];
01012                 $eZContentObjectContentObjectCache[$objectID] = $obj;
01013                 $objectRetArray[$objectID] = $obj;
01014             }
01015             else
01016             {
01017                 $objectRetArray[$objectID] = $resRow;
01018             }
01019         }
01020         return $objectRetArray;
01021     }
01022 
01023     /*!
01024      \return An array with content objects.
01025      \param $asObject Whether to return objects or not
01026      \param $conditions Optional conditions to limit the fetch, set to \c null to skip it.
01027      \param $offset Where to start fetch from, set to \c false to skip it.
01028      \param $limit Maximum number of objects to fetch, set \c false to skip it.
01029      \sa fetchListCount
01030     */
01031     static function fetchList( $asObject = true, $conditions = null, $offset = false, $limit = false )
01032     {
01033         $limitation = null;
01034         if ( $offset !== false or
01035              $limit !== false )
01036             $limitation = array( 'offset' => $offset,
01037                                  'length' => $limit );
01038         return eZPersistentObject::fetchObjectList( eZContentObject::definition(),
01039                                                     null,
01040                                                     $conditions, null, $limitation,
01041                                                     $asObject );
01042     }
01043 
01044     static function fetchFilteredList( $conditions = null, $offset = false, $limit = false, $asObject = true )
01045     {
01046         $limits = null;
01047         if ( $offset or $limit )
01048             $limits = array( 'offset' => $offset,
01049                              'length' => $limit );
01050         return eZPersistentObject::fetchObjectList( eZContentObject::definition(),
01051                                                     null,
01052                                                     $conditions, null, $limits,
01053                                                     $asObject );
01054     }
01055 
01056     /*!
01057      \return The number of objects in the database. Optionally \a $conditions can be used to limit the list count.
01058      \sa fetchList
01059     */
01060     static function fetchListCount( $conditions = null )
01061     {
01062         $rows =  eZPersistentObject::fetchObjectList( eZContentObject::definition(),
01063                                                       array(),
01064                                                       $conditions,
01065                                                       false/* we don't want any sorting when counting. Sorting leads to error on postgresql 8.x */,
01066                                                       null,
01067                                                       false, false,
01068                                                       array( array( 'operation' => 'count( * )',
01069                                                                     'name' => 'count' ) ) );
01070         return $rows[0]['count'];
01071     }
01072 
01073     static function fetchSameClassList( $contentClassID, $asObject = true, $offset = false, $limit = false )
01074     {
01075         $conditions = array( 'contentclass_id' => $contentClassID );
01076         return eZContentObject::fetchFilteredList( $conditions, $offset, $limit, $asObject );
01077     }
01078 
01079     static function fetchSameClassListCount( $contentClassID )
01080     {
01081         $result = eZPersistentObject::fetchObjectList( eZContentObject::definition(),
01082                                                        array(),
01083                                                        array( "contentclass_id" => $contentClassID ),
01084                                                        false, null,
01085                                                        false, false,
01086                                                        array( array( 'operation' => 'count( * )',
01087                                                                      'name' => 'count' ) ) );
01088         return $result[0]['count'];
01089     }
01090 
01091     /*!
01092       Returns the current version of this document.
01093     */
01094     function currentVersion( $asObject = true )
01095     {
01096         return eZContentObjectVersion::fetchVersion( $this->attribute( "current_version" ), $this->ID, $asObject );
01097     }
01098 
01099     /*!
01100       Returns the given object version. False is returned if the versions does not exist.
01101     */
01102     function version( $version, $asObject = true )
01103     {
01104         if ( $asObject )
01105         {
01106             global $eZContentObjectVersionCache;
01107 
01108             if ( !isset( $eZContentObjectVersionCache ) ) // prevent PHP warning below
01109                 $eZContentObjectVersionCache = array();
01110 
01111             if ( isset( $eZContentObjectVersionCache[$this->ID][$version] ) )
01112             {
01113                 return $eZContentObjectVersionCache[$this->ID][$version];
01114             }
01115             else
01116             {
01117                 $eZContentObjectVersionCache[$this->ID][$version] = eZContentObjectVersion::fetchVersion( $version, $this->ID, $asObject );
01118                 return $eZContentObjectVersionCache[$this->ID][$version];
01119             }
01120         }
01121         else
01122         {
01123             return eZContentObjectVersion::fetchVersion( $version, $this->ID, $asObject );
01124         }
01125     }
01126 
01127     /**
01128      * Returns an array of eZContentObjectVersion for the current object
01129      * according to the conditions in $parameters.
01130      *
01131      * @param boolean $asObject
01132      * @param array $parameters
01133      * @return array
01134      */
01135     function versions( $asObject = true, $parameters = array() )
01136     {
01137         $conditions = array( "contentobject_id" => $this->ID );
01138         if ( isset( $parameters['conditions'] ) )
01139         {
01140             if ( isset( $parameters['conditions']['status'] ) )
01141                 $conditions['status'] = $parameters['conditions']['status'];
01142             if ( isset( $parameters['conditions']['creator_id'] ) )
01143                 $conditions['creator_id'] = $parameters['conditions']['creator_id'];
01144             if ( isset( $parameters['conditions']['language_code'] ) )
01145             {
01146                 $conditions['initial_language_id'] = eZContentLanguage::idByLocale( $parameters['conditions']['language_code'] );
01147             }
01148             if ( isset( $parameters['conditions']['initial_language_id'] ) )
01149             {
01150                 $conditions['initial_language_id'] = $parameters['conditions']['initial_language_id'];
01151             }
01152         }
01153         $sort = isset( $parameters['sort'] ) ? $parameters['sort'] : null;
01154         $limit = isset( $parameters['limit'] ) ? $parameters['limit'] : null;
01155 
01156         return eZPersistentObject::fetchObjectList( eZContentObjectVersion::definition(),
01157                                                     null, $conditions,
01158                                                     $sort, $limit,
01159                                                     $asObject );
01160     }
01161 
01162     /*!
01163      \return \c true if the object has any versions remaining.
01164     */
01165     function hasRemainingVersions()
01166     {
01167         $remainingVersions = $this->versions( false );
01168         if ( !is_array( $remainingVersions ) or
01169              count( $remainingVersions ) == 0 )
01170         {
01171             return false;
01172         }
01173         return true;
01174     }
01175 
01176     function createInitialVersion( $userID, $initialLanguageCode = false )
01177     {
01178         return eZContentObjectVersion::create( $this->attribute( "id" ), $userID, 1, $initialLanguageCode );
01179     }
01180 
01181     function createNewVersionIn( $languageCode, $copyFromLanguageCode = false, $copyFromVersion = false, $versionCheck = true, $status = eZContentObjectVersion::STATUS_DRAFT )
01182     {
01183         return $this->createNewVersion( $copyFromVersion, $versionCheck, $languageCode, $copyFromLanguageCode, $status );
01184     }
01185 
01186     /*!
01187      Creates a new version and returns it as an eZContentObjectVersion object.
01188      If version number is given as argument that version is used to create a copy.
01189      \param $versionCheck If \c true it will check if there are too many version and
01190                           remove some of them to make room for a new.
01191 
01192      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
01193      the calls within a db transaction; thus within db->begin and db->commit.
01194     */
01195     function createNewVersion( $copyFromVersion = false, $versionCheck = true, $languageCode = false, $copyFromLanguageCode = false, $status = eZContentObjectVersion::STATUS_DRAFT )
01196     {
01197         $db = eZDB::instance();
01198         $db->begin();
01199         // Check if we have enough space in version list
01200         if ( $versionCheck )
01201         {
01202             $versionlimit = eZContentClass::versionHistoryLimit( $this->attribute( 'contentclass_id' ) );
01203             $versionCount = $this->getVersionCount();
01204             if ( $versionCount >= $versionlimit )
01205             {
01206                 // Remove oldest archived version
01207                 $params = array( 'conditions'=> array( 'status' => eZContentObjectVersion::STATUS_ARCHIVED ) );
01208                 $versions = $this->versions( true, $params );
01209                 if ( count( $versions ) > 0 )
01210                 {
01211                     $modified = $versions[0]->attribute( 'modified' );
01212                     $removeVersion = $versions[0];
01213                     foreach ( $versions as $version )
01214                     {
01215                         $currentModified = $version->attribute( 'modified' );
01216                         if ( $currentModified < $modified )
01217                         {
01218                             $modified = $currentModified;
01219                             $removeVersion = $version;
01220                         }
01221                     }
01222                     $removeVersion->removeThis();
01223                 }
01224             }
01225         }
01226 
01227         // get the next available version number
01228         $nextVersionNumber = $this->nextVersion();
01229 
01230         if ( $copyFromVersion == false )
01231         {
01232             $version = $this->currentVersion();
01233         }
01234         else
01235         {
01236             $version = $this->version( $copyFromVersion );
01237         }
01238 
01239         if ( !$languageCode )
01240         {
01241             $initialLanguage = $version->initialLanguage();
01242             if ( !$initialLanguage )
01243             {
01244                 $initialLanguage = $this->initialLanguage();
01245             }
01246 
01247             if ( $initialLanguage )
01248             {
01249                 $languageCode = $initialLanguage->attribute( 'locale' );
01250             }
01251         }
01252 
01253         $copiedVersion = $this->copyVersion( $this, $version, $nextVersionNumber, false, $status, $languageCode, $copyFromLanguageCode );
01254 
01255         // We need to make sure the copied version contains node-assignment for the existing nodes.
01256         // This is required for BC since scripts might traverse the node-assignments and mark
01257         // some of them for removal.
01258         $parentMap = array();
01259         $copiedNodeAssignmentList = $copiedVersion->attribute( 'node_assignments' );
01260         foreach ( $copiedNodeAssignmentList as $copiedNodeAssignment )
01261         {
01262             $parentMap[$copiedNodeAssignment->attribute( 'parent_node' )] = $copiedNodeAssignment;
01263         }
01264         $nodes = $this->assignedNodes();
01265         foreach ( $nodes as $node )
01266         {
01267             $remoteID = 0;
01268             // Remove assignments which conflicts with existing nodes, but keep remote_id
01269             if ( isset( $parentMap[$node->attribute( 'parent_node_id' )] ) )
01270             {
01271                 $copiedNodeAssignment = $parentMap[$node->attribute( 'parent_node_id' )];
01272                 $remoteID = $copiedNodeAssignment->attribute( 'remote_id' );
01273                 $copiedNodeAssignment->purge();
01274             }
01275             $newNodeAssignment = $copiedVersion->assignToNode( $node->attribute( 'parent_node_id' ), $node->attribute( 'is_main' ), 0,
01276                                                                $node->attribute( 'sort_field' ), $node->attribute( 'sort_order' ),
01277                                                                $remoteID );
01278             // Reset execution bit
01279             $newNodeAssignment->setAttribute( 'op_code', $newNodeAssignment->attribute( 'op_code' ) & ~1 );
01280             $newNodeAssignment->store();
01281         }
01282 
01283         $db->commit();
01284         return $copiedVersion;
01285     }
01286 
01287     /*!
01288      Creates a new version and returns it as an eZContentObjectVersion object.
01289      If version number is given as argument that version is used to create a copy.
01290      \param $languageCode If \c false all languages will be copied, otherwise
01291                           only specified by the locale code string or an array
01292                           of the locale code strings.
01293      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
01294      the calls within a db transaction; thus within db->begin and db->commit.
01295     */
01296     function copyVersion( &$newObject, &$version, $newVersionNumber, $contentObjectID = false, $status = eZContentObjectVersion::STATUS_DRAFT, $languageCode = false, $copyFromLanguageCode = false )
01297     {
01298         $user = eZUser::currentUser();
01299         $userID = $user->attribute( 'contentobject_id' );
01300 
01301         $nodeAssignmentList = $version->attribute( 'node_assignments' );
01302 
01303         $db = eZDB::instance();
01304         $db->begin();
01305 
01306         // This is part of the new 3.8 code.
01307         foreach ( array_keys( $nodeAssignmentList ) as $key )
01308         {
01309             $nodeAssignment = $nodeAssignmentList[$key];
01310             // Only copy assignments which has a remote_id since it will be used in template code.
01311             if ( $nodeAssignment->attribute( 'remote_id' ) == 0 )
01312             {
01313                 continue;
01314             }
01315             $clonedAssignment = $nodeAssignment->cloneNodeAssignment( $newVersionNumber, $contentObjectID );
01316             $clonedAssignment->setAttribute( 'op_code', eZNodeAssignment::OP_CODE_SET ); // Make sure op_code is marked to 'set' the data.
01317             $clonedAssignment->store();
01318         }
01319 
01320         $currentVersionNumber = $version->attribute( "version" );
01321         $contentObjectTranslations = $version->translations();
01322 
01323         $clonedVersion = $version->cloneVersion( $newVersionNumber, $userID, $contentObjectID, $status );
01324 
01325         if ( $contentObjectID != false )
01326         {
01327             if ( $clonedVersion->attribute( 'status' ) == eZContentObjectVersion::STATUS_PUBLISHED )
01328                 $clonedVersion->setAttribute( 'status', eZContentObjectVersion::STATUS_DRAFT );
01329         }
01330 
01331         $clonedVersion->store();
01332 
01333         // We copy related objects before the attributes, this means that the related objects
01334         // are available once the datatype code is run.
01335         $this->copyContentObjectRelations( $currentVersionNumber, $newVersionNumber, $contentObjectID );
01336 
01337         $languageCodeToCopy = false;
01338         if ( $languageCode && in_array( $languageCode, $this->availableLanguages() ) )
01339         {
01340             $languageCodeToCopy = $languageCode;
01341         }
01342         if ( $copyFromLanguageCode && in_array( $copyFromLanguageCode, $this->availableLanguages() ) )
01343         {
01344             $languageCodeToCopy = $copyFromLanguageCode;
01345         }
01346 
01347         $haveCopied = false;
01348         if ( !$languageCode || $languageCodeToCopy )
01349         {
01350             foreach ( $contentObjectTranslations as $contentObjectTranslation )
01351             {
01352                 if ( $languageCode != false && $contentObjectTranslation->attribute( 'language_code' ) != $languageCodeToCopy )
01353                 {
01354                     continue;
01355                 }
01356 
01357                 $contentObjectAttributes = $contentObjectTranslation->objectAttributes();
01358 
01359                 foreach ( $contentObjectAttributes as $attribute )
01360                 {
01361                     $clonedAttribute = $attribute->cloneContentObjectAttribute( $newVersionNumber, $currentVersionNumber, $contentObjectID, $languageCode );
01362                     $clonedAttribute->sync();
01363                     eZDebugSetting::writeDebug( 'kernel-content-object-copy', $clonedAttribute, 'copyVersion:cloned attribute' );
01364                 }
01365 
01366                 $haveCopied = true;
01367             }
01368         }
01369 
01370         if ( !$haveCopied && $languageCode )
01371         {
01372             $class = $this->contentClass();
01373             $classAttributes = $class->fetchAttributes();
01374             foreach ( $classAttributes as $classAttribute )
01375             {
01376                 if ( $classAttribute->attribute( 'can_translate' ) == 1 )
01377                 {
01378                     $classAttribute->instantiate( $contentObjectID? $contentObjectID: $this->attribute( 'id' ), $languageCode, $newVersionNumber );
01379                 }
01380                 else
01381                 {
01382                     // If attribute is NOT Translatable we should check isAlwaysAvailable(),
01383                     // For example,
01384                     // if initial_language_id is 4 and the attribute is always available
01385                     // language_id will be 5 in ezcontentobject_version/ezcontentobject_attribute,
01386                     // this means it uses language ID 4 but also has the bit 0 set to 1 (a reservered bit),
01387                     // You can read about this in the document in doc/features/3.8/.
01388                     $initialLangID = !$this->isAlwaysAvailable() ? $this->attribute( 'initial_language_id' ) : $this->attribute( 'initial_language_id' ) | 1;
01389                     $contentAttribute = eZContentObjectAttribute::fetchByClassAttributeID( $classAttribute->attribute( 'id' ),
01390                                                                                            $this->attribute( 'id' ),
01391                                                                                            $this->attribute( 'current_version' ),
01392                                                                                            $initialLangID );
01393                     if ( $contentAttribute )
01394                     {
01395                         $newAttribute = $contentAttribute->cloneContentObjectAttribute( $newVersionNumber, $currentVersionNumber, $contentObjectID, $languageCode );
01396                         $newAttribute->sync();
01397                     }
01398                     else
01399                     {
01400                         $classAttribute->instantiate( $contentObjectID? $contentObjectID: $this->attribute( 'id' ), $languageCode, $newVersionNumber );
01401                     }
01402                 }
01403             }
01404         }
01405 
01406         if ( $languageCode )
01407         {
01408             $clonedVersion->setAttribute( 'initial_language_id', eZContentLanguage::idByLocale( $languageCode ) );
01409             $clonedVersion->updateLanguageMask();
01410         }
01411 
01412         $db->commit();
01413 
01414         return $clonedVersion;
01415     }
01416 
01417     /*!
01418      Creates a new content object instance and stores it.
01419     */
01420     static function create( $name, $contentclassID, $userID, $sectionID = 1, $version = 1, $languageCode = false )
01421     {
01422         if ( $languageCode == false )
01423         {
01424             $languageCode = eZContentObject::defaultLanguage();
01425         }
01426 
01427         $languageID = eZContentLanguage::idByLocale( $languageCode );
01428 
01429         $row = array(
01430             "name" => $name,
01431             "current_version" => $version,
01432             'initial_language_id' => $languageID,
01433             'language_mask' => $languageID,
01434             "contentclass_id" => $contentclassID,
01435             "permission_id" => 1,
01436             "parent_id" => 0,
01437             "main_node_id" => 0,
01438             "owner_id" => $userID,
01439             "section_id" => $sectionID,
01440             'remote_id' => eZRemoteIdUtility::generate( 'object' ) );
01441 
01442         return new eZContentObject( $row );
01443     }
01444 
01445     function __clone()
01446     {
01447         $this->setAttribute( 'id', null );
01448         $this->setAttribute( 'published', time() );
01449         $this->setAttribute( 'modified', time() );
01450         $this->resetDataMap();
01451     }
01452 
01453     /*!
01454      Makes a copy of the object which is stored and then returns it.
01455      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
01456      the calls within a db transaction; thus within db->begin and db->commit.
01457     */
01458     function copy( $allVersions = true )
01459     {
01460         eZDebugSetting::writeDebug( 'kernel-content-object-copy', 'Copy start, all versions=' . ( $allVersions ? 'true' : 'false' ), 'copy' );
01461         $user = eZUser::currentUser();
01462         $userID = $user->attribute( 'contentobject_id' );
01463 
01464         $contentObject = clone $this;
01465         $contentObject->setAttribute( 'current_version', 1 );
01466         $contentObject->setAttribute( 'owner_id', $userID );
01467 
01468         $contentObject->setAttribute( 'remote_id', eZRemoteIdUtility::generate( 'object' ) );
01469 
01470         $db = eZDB::instance();
01471         $db->begin();
01472         $contentObject->store();
01473 
01474         $originalObjectID = $this->attribute( 'id' );
01475         $contentObjectID = $contentObject->attribute( 'id' );
01476 
01477         $db->query( "INSERT INTO ezcobj_state_link (contentobject_state_id, contentobject_id)
01478                      SELECT contentobject_state_id, $contentObjectID FROM ezcobj_state_link WHERE contentobject_id = $originalObjectID" );
01479 
01480         $contentObject->setName( $this->attribute('name') );
01481         eZDebugSetting::writeDebug( 'kernel-content-object-copy', $contentObject, 'contentObject' );
01482 
01483 
01484         $versionList = array();
01485         if ( $allVersions )
01486         {
01487             $versions = $this->versions();
01488             foreach( $versions as $version )
01489             {
01490                 $versionList[$version->attribute( 'version' )] = $version;
01491             }
01492         }
01493         else
01494         {
01495             $versionList[1] = $this->currentVersion();
01496         }
01497 
01498         foreach ( $versionList as $versionNumber => $currentContentObjectVersion )
01499         {
01500             $currentVersionNumber = $currentContentObjectVersion->attribute( 'version' );
01501             $contentObject->setName( $currentContentObjectVersion->name(), $versionNumber );
01502             foreach( $contentObject->translationStringList() as $languageCode )
01503             {
01504                 $contentObject->setName( $currentContentObjectVersion->name( false, $languageCode ), $versionNumber, $languageCode );
01505             }
01506 
01507             $contentObjectVersion = $this->copyVersion( $contentObject, $currentContentObjectVersion,
01508                                                         $versionNumber, $contentObject->attribute( 'id' ),
01509                                                         false );
01510 
01511             if ( $currentVersionNumber == $this->attribute( 'current_version' ) )
01512             {
01513                 $parentMap = array();
01514                 $copiedNodeAssignmentList = $contentObjectVersion->attribute( 'node_assignments' );
01515                 foreach ( $copiedNodeAssignmentList as $copiedNodeAssignment )
01516                 {
01517                     $parentMap[$copiedNodeAssignment->attribute( 'parent_node' )] = $copiedNodeAssignment;
01518                 }
01519                 // Create node-assignment from all current published nodes
01520                 $nodes = $this->assignedNodes();
01521                 foreach( $nodes as $node )
01522                 {
01523                     $remoteID = 0;
01524                     // Remove assignments which conflicts with existing nodes, but keep remote_id
01525                     if ( isset( $parentMap[$node->attribute( 'parent_node_id' )] ) )
01526                     {
01527                         $copiedNodeAssignment = $parentMap[$node->attribute( 'parent_node_id' )];
01528                         unset( $parentMap[$node->attribute( 'parent_node_id' )] );
01529                         $remoteID = $copiedNodeAssignment->attribute( 'remote_id' );
01530                         $copiedNodeAssignment->purge();
01531                     }
01532                     $newNodeAssignment = $contentObjectVersion->assignToNode( $node->attribute( 'parent_node_id' ), $node->attribute( 'is_main' ), 0,
01533                                                                               $node->attribute( 'sort_field' ), $node->attribute( 'sort_order' ),
01534                                                                               $remoteID );
01535                 }
01536             }
01537 
01538             eZDebugSetting::writeDebug( 'kernel-content-object-copy', $contentObjectVersion, 'Copied version' );
01539         }
01540 
01541         // Set version number
01542         if ( $allVersions )
01543             $contentObject->setAttribute( 'current_version', $this->attribute( 'current_version' ) );
01544 
01545         $contentObject->setAttribute( 'status', eZContentObject::STATUS_DRAFT );
01546 
01547         $contentObject->store();
01548 
01549         $db->commit();
01550 
01551         eZDebugSetting::writeDebug( 'kernel-content-object-copy', 'Copy done', 'copy' );
01552         return $contentObject;
01553     }
01554 
01555     /*!
01556       Reverts the object to the given version. All versions newer then the given version will
01557       be deleted.
01558       \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
01559      the calls within a db transaction; thus within db->begin and db->commit.
01560     */
01561     function revertTo( $version )
01562     {
01563         $db = eZDB::instance();
01564         $db->begin();
01565 
01566         // Delete stored attribute from other tables
01567         $contentobjectAttributes = $this->allContentObjectAttributes( $this->ID );
01568         foreach (  $contentobjectAttributes as $contentobjectAttribute )
01569         {
01570             $contentobjectAttributeVersion = $contentobjectAttribute->attribute("version");
01571             if( $contentobjectAttributeVersion > $version )
01572             {
01573                 $classAttribute = $contentobjectAttribute->contentClassAttribute();
01574                 $dataType = $classAttribute->dataType();
01575                 $dataType->deleteStoredObjectAttribute( $contentobjectAttribute, $contentobjectAttributeVersion );
01576             }
01577         }
01578         $version =(int) $version;
01579         $db->query( "DELETE FROM ezcontentobject_attribute
01580                           WHERE contentobject_id='$this->ID' AND version>'$version'" );
01581 
01582         $db->query( "DELETE FROM ezcontentobject_version
01583                           WHERE contentobject_id='$this->ID' AND version>'$version'" );
01584 
01585         $db->query( "DELETE FROM eznode_assignment
01586                           WHERE contentobject_id='$this->ID' AND contentobject_version > '$version'" );
01587 
01588         $this->CurrentVersion = $version;
01589         $this->store();
01590         $db->commit();
01591     }
01592 
01593     /*!
01594      Copies the given version of the object and creates a new current version.
01595      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
01596      the calls within a db transaction; thus within db->begin and db->commit.
01597     */
01598     function copyRevertTo( $version, $language = false )
01599     {
01600         $versionObject = $this->createNewVersionIn( $language, false, $version );
01601 
01602         return $versionObject->attribute( 'version' );
01603     }
01604 
01605     static function fixReverseRelations( $objectID, $mode = false )
01606     {
01607         $db = eZDB::instance();
01608         $objectID = (int) $objectID;
01609 
01610         // Finds all the attributes that store relations to the given object.
01611         $result = $db->arrayQuery( "SELECT attr.*
01612                                     FROM ezcontentobject_link link,
01613                                          ezcontentobject_attribute attr
01614                                     WHERE link.from_contentobject_id=attr.contentobject_id AND
01615                                           link.from_contentobject_version=attr.version AND
01616                                           link.contentclassattribute_id=attr.contentclassattribute_id AND
01617                                           link.to_contentobject_id=$objectID" );
01618         if ( count( $result ) > 0 )
01619         {
01620             foreach( $result as $row )
01621             {
01622                 $attr = new eZContentObjectAttribute( $row );
01623                 $dataType = $attr->dataType();
01624                 $dataType->fixRelatedObjectItem( $attr, $objectID, $mode );
01625                 eZContentCacheManager::clearObjectViewCache( $attr->attribute( 'contentobject_id' ), true );
01626             }
01627         }
01628     }
01629 
01630     function removeReverseRelations( $objectID )
01631     {
01632         $db = eZDB::instance();
01633         $objectID = (int) $objectID;
01634         // Get list of objects referring to this one.
01635         $relatingObjects = $this->reverseRelatedObjectList( false, 0, false, array( 'AllRelations' => true ) );
01636 
01637         // Finds all the attributes that store relations to the given object.
01638 
01639         $result = $db->arrayQuery( "SELECT attr.*
01640                                     FROM ezcontentobject_link link,
01641                                          ezcontentobject_attribute attr
01642                                     WHERE link.from_contentobject_id=attr.contentobject_id AND
01643                                           link.from_contentobject_version=attr.version AND
01644                                           link.contentclassattribute_id=attr.contentclassattribute_id AND
01645                                           link.to_contentobject_id=$objectID" );
01646 
01647         // Remove references from XML.
01648         if ( count( $result ) > 0 )
01649         {
01650             foreach( $result as $row )
01651             {
01652                 $attr = new eZContentObjectAttribute( $row );
01653                 $dataType = $attr->dataType();
01654                 $dataType->removeRelatedObjectItem( $attr, $objectID );
01655                 eZContentCacheManager::clearObjectViewCache( $attr->attribute( 'contentobject_id' ), true );
01656                 $attr->storeData();
01657             }
01658         }
01659 
01660         // Remove references in ezcontentobject_link.
01661         foreach ( $relatingObjects as $fromObject )
01662         {
01663             $fromObject->removeContentObjectRelation( $this->attribute( 'id' ), false, false );
01664         }
01665     }
01666 
01667     /*!
01668       If nodeID is not given, this function will remove object from database. All versions and translations of this object will be lost.
01669       Otherwise, it will check node assignment and only delete the object from this node if it was assigned to other nodes as well.
01670       \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
01671      the calls within a db transaction; thus within db->begin and db->commit.
01672     */
01673     function purge()
01674     {
01675         $delID = $this->ID;
01676         // Who deletes which content should be logged.
01677         eZAudit::writeAudit( 'content-delete', array( 'Object ID' => $delID, 'Content Name' => $this->attribute( 'name' ),
01678                                                       'Comment' => 'Purged the current object: eZContentObject::purge()' ) );
01679 
01680         $db = eZDB::instance();
01681 
01682         $db->begin();
01683 
01684         $contentobjectAttributes = $this->allContentObjectAttributes( $delID );
01685 
01686         foreach ( $contentobjectAttributes as $contentobjectAttribute )
01687         {
01688             $dataType = $contentobjectAttribute->dataType();
01689             if ( !$dataType )
01690                 continue;
01691             $dataType->deleteStoredObjectAttribute( $contentobjectAttribute );
01692         }
01693 
01694         eZInformationCollection::removeContentObject( $delID );
01695 
01696         eZContentObjectTrashNode::purgeForObject( $delID );
01697 
01698         $db->query( "DELETE FROM ezcontentobject_tree
01699              WHERE contentobject_id='$delID'" );
01700 
01701         $db->query( "DELETE FROM ezcontentobject_attribute
01702              WHERE contentobject_id='$delID'" );
01703 
01704         $db->query( "DELETE FROM ezcontentobject_version
01705              WHERE contentobject_id='$delID'" );
01706 
01707         $db->query( "DELETE FROM ezcontentobject_name
01708              WHERE contentobject_id='$delID'" );
01709 
01710         $db->query( "DELETE FROM ezcobj_state_link WHERE contentobject_id=$delID" );
01711 
01712         $db->query( "DELETE FROM ezcontentobject
01713              WHERE id='$delID'" );
01714 
01715         $db->query( "DELETE FROM eznode_assignment
01716              WHERE contentobject_id = '$delID'" );
01717 
01718         $db->query( "DELETE FROM ezuser_role
01719              WHERE contentobject_id = '$delID'" );
01720 
01721         $db->query( "DELETE FROM ezuser_discountrule
01722              WHERE contentobject_id = '$delID'" );
01723 
01724         eZContentObject::fixReverseRelations( $delID, 'remove' );
01725 
01726         eZSearch::removeObject( $this );
01727 
01728         // Check if deleted object is in basket/wishlist
01729         $sql = 'SELECT DISTINCT ezproductcollection_item.productcollection_id
01730                 FROM   ezbasket, ezwishlist, ezproductcollection_item
01731                 WHERE  ( ezproductcollection_item.productcollection_id=ezbasket.productcollection_id OR
01732                          ezproductcollection_item.productcollection_id=ezwishlist.productcollection_id ) AND
01733                        ezproductcollection_item.contentobject_id=' . $delID;
01734         $rows = $db->arrayQuery( $sql );
01735         if ( count( $rows ) > 0 )
01736         {
01737             $countElements = 50;
01738             $deletedArray = array();
01739             // Create array of productCollectionID will be removed from ezwishlist and ezproductcollection_item
01740             foreach ( $rows as $row )
01741             {
01742                 $deletedArray[] = $row['productcollection_id'];
01743             }
01744             // Split $deletedArray into several arrays with $countElements values
01745             $splitted = array_chunk( $deletedArray, $countElements );
01746             // Remove eZProductCollectionItem and eZWishList
01747             foreach ( $splitted as $value )
01748             {
01749                 eZPersistentObject::removeObject( eZProductCollectionItem::definition(), array( 'productcollection_id' => array( $value, '' ) ) );
01750                 eZPersistentObject::removeObject( eZWishList::definition(), array( 'productcollection_id' => array( $value, '' ) ) );
01751             }
01752         }
01753         $db->query( 'UPDATE ezproductcollection_item
01754                      SET contentobject_id = 0
01755                      WHERE  contentobject_id = ' . $delID );
01756 
01757         // Cleanup relations in two steps to avoid locking table for to long
01758         $db->query( "DELETE FROM ezcontentobject_link
01759                      WHERE from_contentobject_id = '$delID'" );
01760 
01761         $db->query( "DELETE FROM ezcontentobject_link
01762                      WHERE to_contentobject_id = '$delID'" );
01763 
01764         // Cleanup properties: LastVisit, Creator, Owner
01765         $db->query( "DELETE FROM ezuservisit
01766              WHERE user_id = '$delID'" );
01767 
01768         $db->query( "UPDATE ezcontentobject_version
01769              SET creator_id = 0
01770              WHERE creator_id = '$delID'" );
01771 
01772         $db->query( "UPDATE ezcontentobject
01773              SET owner_id = 0
01774              WHERE owner_id = '$delID'" );
01775 
01776         if ( isset( $GLOBALS["eZWorkflowTypeObjects"] ) and is_array( $GLOBALS["eZWorkflowTypeObjects"] ) )
01777         {
01778             $registeredTypes =& $GLOBALS["eZWorkflowTypeObjects"];
01779         }
01780         else
01781         {
01782             $registeredTypes = eZWorkflowType::fetchRegisteredTypes();
01783         }
01784 
01785         // Cleanup ezworkflow_event etc...
01786         foreach ( array_keys( $registeredTypes ) as $registeredTypeKey )
01787         {
01788             $registeredType = $registeredTypes[$registeredTypeKey];
01789             $registeredType->cleanupAfterRemoving( array( 'DeleteContentObject' => $delID ) );
01790         }
01791 
01792         $db->commit();
01793     }
01794 
01795     /*!
01796      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
01797      the calls within a db transaction; thus within db->begin and db->commit.
01798      */
01799     function removeThis( $nodeID = null )
01800     {
01801         $delID = $this->ID;
01802 
01803         // Who deletes which content should be logged.
01804         eZAudit::writeAudit( 'content-delete', array( 'Object ID' => $delID, 'Content Name' => $this->attribute( 'name' ),
01805                                                       'Comment' => 'Setted archived status for the current object: eZContentObject::remove()' ) );
01806 
01807         $nodes = $this->attribute( 'assigned_nodes' );
01808 
01809         if ( $nodeID === null or count( $nodes ) <= 1 )
01810         {
01811             $db = eZDB::instance();
01812             $db->begin();
01813             $mainNodeKey = false;
01814             foreach ( $nodes as $key => $node )
01815             {
01816                 if ( $node->attribute( 'main_node_id' ) == $node->attribute( 'node_id' ) )
01817                 {
01818                     $mainNodeKey = $key;
01819                 }
01820                 else
01821                 {
01822                     $node->removeThis();
01823                 }
01824             }
01825 
01826             if ( $mainNodeKey !== false )
01827             {
01828                 $nodes[$mainNodeKey]->removeNodeFromTree( true );
01829             }
01830 
01831 
01832             $this->setAttribute( 'status', eZContentObject::STATUS_ARCHIVED );
01833             eZSearch::removeObject( $this );
01834             $this->store();
01835             eZContentObject::fixReverseRelations( $delID, 'trash' );
01836             // Delete stored attribute from other tables
01837             $db->commit();
01838 
01839         }
01840         else if ( $nodeID !== null )
01841         {
01842             $node = eZContentObjectTreeNode::fetch( $nodeID , false );
01843             if ( is_object( $node ) )
01844             {
01845                 if ( $node->attribute( 'main_node_id' ) == $nodeID )
01846                 {
01847                     $db = eZDB::instance();
01848                     $db->begin();
01849                     foreach ( $additionalNodes as $additionalNode )
01850                     {
01851                         if ( $additionalNode->attribute( 'node_id' ) != $node->attribute( 'main_node_id' ) )
01852                         {
01853                             $additionalNode->remove();
01854                         }
01855                     }
01856 
01857                     $node->removeNodeFromTree( true );
01858                     $this->setAttribute( 'status', eZContentObject::STATUS_ARCHIVED );
01859                     eZSearch::removeObject( $this );
01860                     $this->store();
01861                     eZContentObject::fixReverseRelations( $delID, 'trash' );
01862                     $db->commit();
01863                 }
01864                 else
01865                 {
01866                     eZContentObjectTreeNode::removeNode( $nodeID );
01867                 }
01868             }
01869         }
01870         else
01871         {
01872             eZContentObjectTreeNode::removeNode( $nodeID );
01873         }
01874     }
01875 
01876     /*!
01877      Removes old internal drafts by the specified user associated with this content object.
01878      Only internal drafts older than 1 day will be considered.
01879      \param $userID The ID of the user to cleanup for, if \c false it will use the current user.
01880      */
01881     function cleanupInternalDrafts( $userID = false, $timeDuration = 86400 ) // default time duration for internal drafts 60*60*24 seconds (1 day)
01882     {
01883         if ( !is_numeric( $timeDuration ) ||
01884              $timeDuration < 0 )
01885         {
01886             eZDebug::writeError( "The time duration must be a positive numeric value (timeDuration = $timeDuration)", __METHOD__ );
01887             return;
01888         }
01889 
01890         if ( $userID === false )
01891         {
01892             $userID = eZUser::currentUserID();
01893         }
01894         // Fetch all draft/temporary versions by specified user
01895         $parameters = array( 'conditions' => array( 'status' => eZContentObjectVersion::STATUS_INTERNAL_DRAFT,
01896                                                     'creator_id' => $userID ) );
01897         // Remove temporary drafts which are old.
01898         $expiryTime = time() - $timeDuration; // only remove drafts older than time duration (default is 1 day)
01899         foreach ( $this->versions( true, $parameters ) as $possibleVersion )
01900         {
01901             if ( $possibleVersion->attribute( 'modified' ) < $expiryTime )
01902             {
01903                 $possibleVersion->removeThis();
01904             }
01905         }
01906     }
01907 
01908     /*!
01909      \static
01910      Removes all old internal drafts by the specified user.
01911      Only internal drafts older than 1 day will be considered.
01912      \param $userID The ID of the user to cleanup for, if \c false it will use the current user.
01913      */
01914     static function cleanupAllInternalDrafts( $userID = false, $timeDuration = 86400 ) // default time duration for internal drafts 60*60*24 seconds (1 day)
01915     {
01916         if ( !is_numeric( $timeDuration ) ||
01917              $timeDuration < 0 )
01918         {
01919             eZDebug::writeError( "The time duration must be a positive numeric value (timeDuration = $timeDuration)", __METHOD__ );
01920             return;
01921         }
01922 
01923 
01924         if ( $userID === false )
01925         {
01926             $userID = eZUser::currentUserID();
01927         }
01928         // Remove all internal drafts
01929         $untouchedDrafts = eZContentObjectVersion::fetchForUser( $userID, eZContentObjectVersion::STATUS_INTERNAL_DRAFT );
01930 
01931         $expiryTime = time() - $timeDuration; // only remove drafts older than time duration (default is 1 day)
01932         foreach ( $untouchedDrafts as $untouchedDraft )
01933         {
01934             if ( $untouchedDraft->attribute( 'modified' ) < $expiryTime )
01935             {
01936                 $untouchedDraft->removeThis();
01937             }
01938         }
01939     }
01940 
01941     /*
01942      Fetch all attributes of all versions belongs to a contentObject.
01943     */
01944     function allContentObjectAttributes( $contentObjectID, $asObject = true )
01945     {
01946         return eZPersistentObject::fetchObjectList( eZContentObjectAttribute::definition(),
01947                                                     null,
01948                                                     array("contentobject_id" => $contentObjectID ),
01949                                                     null,
01950                                                     null,
01951                                                     $asObject );
01952     }
01953 
01954     /*!
01955       Fetches the attributes for the current published version of the object.
01956       TODO: fix using of $asObject parameter,
01957             fix condition for getting attribute from cache,
01958             probably need to move method to eZContentObjectVersion class
01959     */
01960     function contentObjectAttributes( $asObject = true, $version = false, $language = false, $contentObjectAttributeID = false, $distinctItemsOnly = false )
01961     {
01962         $db = eZDB::instance();
01963         if ( $version == false )
01964         {
01965             $version = $this->CurrentVersion;
01966         }
01967         else
01968         {
01969             $version = (int) $version;
01970         }
01971 
01972         if ( $language === false )
01973         {
01974             $language = $this->CurrentLanguage;
01975         }
01976 
01977         if ( is_string( $language ) )
01978             $language = $db->escapeString( $language );
01979 
01980         if ( $contentObjectAttributeID !== false )
01981             $contentObjectAttributeID =(int) $contentObjectAttributeID;
01982 
01983         if ( !$language || !isset( $this->ContentObjectAttributes[$version][$language] ) )
01984         {
01985             $versionText = "AND                    ezcontentobject_attribute.version = '$version'";
01986             if ( $language )
01987             {
01988                 $languageText = "AND                    ezcontentobject_attribute.language_code = '$language'";
01989             }
01990             else
01991             {
01992                 $languageText = "AND                    ".eZContentLanguage::sqlFilter( 'ezcontentobject_attribute', 'ezcontentobject_version' );
01993             }
01994             $attributeIDText = false;
01995             if ( $contentObjectAttributeID )
01996                 $attributeIDText = "AND                    ezcontentobject_attribute.id = '$contentObjectAttributeID'";
01997             $distinctText = false;
01998             if ( $distinctItemsOnly )
01999                 $distinctText = "GROUP BY ezcontentobject_attribute.id";
02000             $query = "SELECT ezcontentobject_attribute.*, ezcontentclass_attribute.identifier as identifier FROM
02001                     ezcontentobject_attribute, ezcontentclass_attribute, ezcontentobject_version
02002                   WHERE
02003                     ezcontentclass_attribute.version = '0' AND
02004                     ezcontentclass_attribute.id = ezcontentobject_attribute.contentclassattribute_id AND
02005                     ezcontentobject_version.contentobject_id = '$this->ID' AND
02006                     ezcontentobject_version.version = '$version' AND
02007                     ezcontentobject_attribute.contentobject_id = '$this->ID' $versionText $languageText $attributeIDText
02008                   $distinctText
02009                   ORDER BY
02010                     ezcontentclass_attribute.placement ASC,
02011                     ezcontentobject_attribute.language_code ASC";
02012 
02013             $attributeArray = $db->arrayQuery( $query );
02014 
02015             if ( !$language && $attributeArray )
02016             {
02017                 $language = $attributeArray[0]['language_code'];
02018                 $this->CurrentLanguage = $language;
02019             }
02020 
02021             $returnAttributeArray = array();
02022             foreach ( $attributeArray as $attribute )
02023             {
02024                 $attr = new eZContentObjectAttribute( $attribute );
02025                 $attr->setContentClassAttributeIdentifier( $attribute['identifier'] );
02026                 $returnAttributeArray[] = $attr;
02027             }
02028 
02029             if ( $language !== null and $version !== null )
02030             {
02031                 $this->ContentObjectAttributes[$version][$language] = $returnAttributeArray;
02032             }
02033         }
02034         else
02035         {
02036             $returnAttributeArray = $this->ContentObjectAttributes[$version][$language];
02037         }
02038 
02039         return $returnAttributeArray;
02040     }
02041 
02042     /*!
02043      Initializes the cached copy of the content object attributes for the given version and language
02044     */
02045     function setContentObjectAttributes( &$attributes, $version, $language )
02046     {
02047         $this->ContentObjectAttributes[$version][$language] = $attributes;
02048     }
02049 
02050     /*!
02051       \static
02052       Fetches the attributes for an array of objects. The objList parameter
02053       contains an array of objects ( instanceOf eZContentObject or a object that is or
02054       extends eZContentObjectTreeNode ) to fetch attributes from.
02055     */
02056     static function fillNodeListAttributes( $objList, $asObject = true )
02057     {
02058         $db = eZDB::instance();
02059 
02060         if ( count( $objList ) > 0 )
02061         {
02062             $objectArray = array();
02063             $tmpLanguageObjectList = array();
02064             $whereSQL = '';
02065             $count = count( $objList );
02066             $i = 0;
02067             foreach ( $objList as $obj )
02068             {
02069                 if ( $obj instanceOf eZContentObject )
02070                     $object = $obj;
02071                 else
02072                     $object = $obj->attribute( 'object' );
02073 
02074                 $language = $object->currentLanguage();
02075                 $tmpLanguageObjectList[$object->attribute( 'id' )] = $language;
02076                 $objectArray = array( 'id' => $object->attribute( 'id' ),
02077                                       'language' => $language,
02078                                       'version' => $object->attribute( 'current_version' ) );
02079 
02080                 $whereSQL .= "( ezcontentobject_attribute.version = '" . $object->attribute( 'current_version' ) . "' AND
02081                     ezcontentobject_attribute.contentobject_id = '" . $object->attribute( 'id' ) . "' AND
02082                     ezcontentobject_attribute.language_code = '" . $language . "' ) ";
02083 
02084                 $i++;
02085                 if ( $i < $count )
02086                     $whereSQL .= ' OR ';
02087             }
02088 
02089             $query = "SELECT ezcontentobject_attribute.*, ezcontentclass_attribute.identifier as identifier FROM
02090                     ezcontentobject_attribute, ezcontentclass_attribute
02091                   WHERE
02092                     ezcontentclass_attribute.version = '0' AND
02093                     ezcontentclass_attribute.id = ezcontentobject_attribute.contentclassattribute_id AND
02094                     ( $whereSQL )
02095                   ORDER BY
02096                     ezcontentobject_attribute.contentobject_id, ezcontentclass_attribute.placement ASC";
02097 
02098             $attributeArray = $db->arrayQuery( $query );
02099 
02100             $tmpAttributeObjectList = array();
02101             $returnAttributeArray = array();
02102             foreach ( $attributeArray as $attribute )
02103             {
02104                 $attr = new eZContentObjectAttribute( $attribute );
02105                 $attr->setContentClassAttributeIdentifier( $attribute['identifier'] );
02106 
02107                 $tmpAttributeObjectList[$attr->attribute( 'contentobject_id' )][] = $attr;
02108             }
02109 
02110             foreach ( $objList as $obj )
02111             {
02112                 if ( $obj instanceOf eZContentObject )
02113                 {
02114                     $obj->setContentObjectAttributes( $tmpAttributeObjectList[$obj->attribute( 'id' )],
02115                                          $obj->attribute( 'current_version' ),
02116                                          $tmpLanguageObjectList[$obj->attribute( 'id' )] );
02117                 }
02118                 else
02119                 {
02120                     $object = $obj->attribute( 'object' );
02121                     $object->setContentObjectAttributes( $tmpAttributeObjectList[$object->attribute( 'id' )],
02122                                                          $object->attribute( 'current_version' ),
02123                                                          $tmpLanguageObjectList[$object->attribute( 'id' )] );
02124                     $obj->setContentObject( $object );
02125                 }
02126             }
02127         }
02128     }
02129 
02130     function resetInputRelationList()
02131     {
02132         $this->InputRelationList = array( eZContentObject::RELATION_EMBED => array(),
02133                                           eZContentObject::RELATION_LINK =>  array() );
02134     }
02135 
02136     function appendInputRelationList( $addingIDList, $relationType )
02137     {
02138         if ( !is_array( $addingIDList ) )
02139         {
02140             $addingIDList = array( ( int ) $addingIDList );
02141         }
02142         elseif ( !count( $addingIDList ) )
02143         {
02144             return;
02145         }
02146         $relationType = ( int ) $relationType;
02147         if ( !$this->InputRelationList )
02148         {
02149             $this->resetInputRelationList();
02150         }
02151 
02152         foreach ( array_keys( $this->InputRelationList ) as $inputRelationType )
02153         {
02154             if ( $inputRelationType & $relationType )
02155             {
02156                 $this->InputRelationList[$inputRelationType] = array_merge( $this->InputRelationList[$inputRelationType], $addingIDList );
02157             }
02158         }
02159     }
02160 
02161     function commitInputRelations( $editVersion )
02162     {
02163         foreach ( $this->InputRelationList as $relationType => $relatedObjectIDArray )
02164         {
02165             $oldRelatedObjectArray = $this->relatedObjects( $editVersion, false, 0, false, array( 'AllRelations' => $relationType ) );
02166 
02167             foreach ( $oldRelatedObjectArray as $oldRelatedObject )
02168             {
02169                 $oldRelatedObjectID = $oldRelatedObject->ID;
02170                 if ( !in_array( $oldRelatedObjectID, $relatedObjectIDArray ) )
02171                 {
02172                     $this->removeContentObjectRelation( $oldRelatedObjectID, $editVersion, 0, $relationType );
02173                 }
02174                 $relatedObjectIDArray = array_diff( $relatedObjectIDArray, array( $oldRelatedObjectID ) );
02175             }
02176 
02177             foreach ( $relatedObjectIDArray as $relatedObjectID )
02178             {
02179                 $this->addContentObjectRelation( $relatedObjectID, $editVersion, 0, $relationType );
02180             }
02181         }
02182         return true;
02183     }
02184 
02185     function validateInput( $contentObjectAttributes, $attributeDataBaseName,
02186                             $inputParameters = false, $parameters = array() )
02187     {
02188         $result = array( 'unvalidated-attributes' => array(),
02189                          'validated-attributes' => array(),
02190                          'status-map' => array(),
02191                          'require-fixup' => false,
02192                          'input-validated' => true );
02193         $parameters = array_merge( array( 'prefix-name' => false ),
02194                                    $parameters );
02195         if ( $inputParameters )
02196         {
02197             $result['unvalidated-attributes'] =& $inputParameters['unvalidated-attributes'];
02198             $result['validated-attributes'] =& $inputParameters['validated-attributes'];
02199         }
02200         $unvalidatedAttributes =& $result['unvalidated-attributes'];
02201         $validatedAttributes =& $result['validated-attributes'];
02202         $statusMap =& $result['status-map'];
02203         if ( !$inputParameters )
02204             $inputParameters = array( 'unvalidated-attributes' => &$unvalidatedAttributes,
02205                                       'validated-attributes' => &$validatedAttributes );
02206         $requireFixup =& $result['require-fixup'];
02207         $inputValidated =& $result['input-validated'];
02208         $http = eZHTTPTool::instance();
02209 
02210         $this->resetInputRelationList();
02211 
02212         $editVersion = null;
02213         $defaultLanguage = $this->initialLanguageCode();
02214         foreach( $contentObjectAttributes as $contentObjectAttribute )
02215         {
02216             $contentClassAttribute = $contentObjectAttribute->contentClassAttribute();
02217             $editVersion = $contentObjectAttribute->attribute('version');
02218 
02219             // Check if this is a translation
02220             $currentLanguage = $contentObjectAttribute->attribute( 'language_code' );
02221 
02222             $isTranslation = false;
02223             if ( $currentLanguage != $defaultLanguage )
02224                 $isTranslation = true;
02225 
02226             // If current attribute is a translation
02227             // Check if this attribute can be translated
02228             // If not do not validate, since the input will be copyed from the original
02229             $doNotValidate = false;
02230             if ( $isTranslation )
02231             {
02232                 if ( !$contentClassAttribute->attribute( 'can_translate' ) )
02233                     $doNotValidate = true;
02234             }
02235 
02236             if ( $doNotValidate == true )
02237             {
02238                 $status = eZInputValidator::STATE_ACCEPTED;
02239             }
02240             else
02241             {
02242                 $status = $contentObjectAttribute->validateInput( $http, $attributeDataBaseName,
02243                                                                   $inputParameters, $parameters );
02244             }
02245             $statusMap[$contentObjectAttribute->attribute( 'id' )] = array( 'value' => $status,
02246                                                                             'attribute' => $contentObjectAttribute );
02247 
02248             if ( $status == eZInputValidator::STATE_INTERMEDIATE )
02249                 $requireFixup = true;
02250             else if ( $status == eZInputValidator::STATE_INVALID )
02251             {
02252                 $inputValidated = false;
02253                 $dataType = $contentObjectAttribute->dataType();
02254                 $attributeName = $dataType->attribute( 'information' );
02255                 $attributeName = $attributeName['name'];
02256                 $description = $contentObjectAttribute->attribute( 'validation_error' );
02257                 $validationNameArray[] = $contentClassAttribute->attribute( 'name' );
02258                 $validationName = implode( '->', $validationNameArray );
02259                 $hasValidationError = $contentObjectAttribute->attribute( 'has_validation_error' );
02260                 if ( $hasValidationError )
02261                 {
02262                     if ( !$description )
02263                         $description = false;
02264                     $validationNameArray = array();
02265                     if ( $parameters['prefix-name'] )
02266                         $validationNameArray = $parameters['prefix-name'];
02267                 }
02268                 else
02269                 {
02270                     if ( !$description )
02271                         $description = 'unknown error';
02272                 }
02273                 $unvalidatedAttributes[] = array( 'id' => $contentObjectAttribute->attribute( 'id' ),
02274                                                   'identifier' => $contentClassAttribute->attribute( 'identifier' ),
02275                                                   'name' => $validationName,
02276                                                   'description' => $description );
02277             }
02278             else if ( $status == eZInputValidator::STATE_ACCEPTED )
02279             {
02280                 $dataType = $contentObjectAttribute->dataType();
02281                 $attributeName = $dataType->attribute( 'information' );
02282                 $attributeName = $attributeName['name'];
02283                 if ( $contentObjectAttribute->attribute( 'validation_log' ) != null )
02284                 {
02285                     $description = $contentObjectAttribute->attribute( 'validation_log' );
02286                     if ( !$description )
02287                         $description = false;
02288                     $validationName = $contentClassAttribute->attribute( 'name' );
02289                     if ( $parameters['prefix-name'] )
02290                         $validationName = $parameters['prefix-name'] . '->' . $validationName;
02291                     $validatedAttributes[] = array(  'id' => $contentObjectAttribute->attribute( 'id' ),
02292                                                      'identifier' => $contentClassAttribute->attribute( 'identifier' ),
02293                                                      'name' => $validationName,
02294                                                      'description' => $description );
02295                 }
02296             }
02297         }
02298 
02299         if ( $editVersion !== null )
02300         {
02301             $this->commitInputRelations( $editVersion );
02302         }
02303         $this->resetInputRelationList();
02304 
02305         return $result;
02306     }
02307 
02308     function fixupInput( $contentObjectAttributes, $attributeDataBaseName )
02309     {
02310         $http = eZHTTPTool::instance();
02311         foreach ( $contentObjectAttributes as $contentObjectAttribute )
02312         {
02313             $contentObjectAttribute->fixupInput( $http, $attributeDataBaseName );
02314         }
02315     }
02316 
02317     function fetchInput( $contentObjectAttributes, $attributeDataBaseName,
02318                          $customActionAttributeArray, $customActionParameters )
02319     {
02320         // Global variable to cache datamaps
02321         global $eZContentObjectDataMapCache;
02322 
02323         $result = array( 'attribute-input-map' => array() );
02324         $attributeInputMap =& $result['attribute-input-map'];
02325         $http = eZHTTPTool::instance();
02326 
02327         $defaultLanguage = $this->initialLanguageCode();
02328 
02329         $this->fetchDataMap();
02330         foreach ( $contentObjectAttributes as $contentObjectAttribute )
02331         {
02332             $contentClassAttribute = $contentObjectAttribute->contentClassAttribute();
02333 
02334             // Check if this is a translation
02335             $currentLanguage = $contentObjectAttribute->attribute( 'language_code' );
02336 
02337             $isTranslation = false;
02338             if ( $currentLanguage != $defaultLanguage )
02339                 $isTranslation = true;
02340 
02341             // If current attribute is an un-translateable translation, input should not be fetched
02342             $fetchInput = true;
02343             if ( $isTranslation == true )
02344             {
02345                 if ( !$contentClassAttribute->attribute( 'can_translate' ) )
02346                 {
02347                     $fetchInput = false;
02348                 }
02349             }
02350 
02351             // Do not handle input for non-translateable attributes.
02352             // Input will be copyed from the std. translation on storage
02353             if ( $fetchInput )
02354             {
02355                 if ( $contentObjectAttribute->fetchInput( $http, $attributeDataBaseName ) )
02356                 {
02357                     $attributeInputMap[$contentObjectAttribute->attribute('id')] = true;
02358 
02359                     // we fill the internal data map cache for the current version here with the attributes of the new version
02360                     // this will make the data map cache inconsistent, but this is required to make it possible to use $object.data_map
02361                     // in content/edit templates
02362                     $attributeIdentifier = $contentObjectAttribute->attribute( 'contentclass_attribute_identifier' );
02363                     $eZContentObjectDataMapCache[$this->ID][$this->CurrentVersion][$currentLanguage][$attributeIdentifier] = $contentObjectAttribute;
02364                     $this->DataMap[$this->CurrentVersion][$currentLanguage][$attributeIdentifier] = $contentObjectAttribute;
02365                 }
02366 
02367                 // Custom Action Code
02368                 $this->handleCustomHTTPActions( $contentObjectAttribute, $attributeDataBaseName,
02369                                                 $customActionAttributeArray, $customActionParameters );
02370             }
02371 
02372         }
02373         return $result;
02374     }
02375 
02376     function handleCustomHTTPActions( $contentObjectAttribute, $attributeDataBaseName,
02377                                       $customActionAttributeArray, $customActionParameters )
02378     {
02379         $http = eZHTTPTool::instance();
02380         $customActionParameters['base_name'] = $attributeDataBaseName;
02381         if ( isset( $customActionAttributeArray[$contentObjectAttribute->attribute( 'id' )] ) )
02382         {
02383             $customActionAttributeID = $customActionAttributeArray[$contentObjectAttribute->attribute( 'id' )]['id'];
02384             $customAction = $customActionAttributeArray[$contentObjectAttribute->attribute( 'id' )]['value'];
02385             $contentObjectAttribute->customHTTPAction( $http, $customAction, $customActionParameters );
02386         }
02387 
02388         $contentObjectAttribute->handleCustomHTTPActions( $http, $attributeDataBaseName,
02389                                                           $customActionAttributeArray, $customActionParameters );
02390     }
02391 
02392     function handleAllCustomHTTPActions( $attributeDataBaseName,
02393                                          $customActionAttributeArray, $customActionParameters,
02394                                          $objectVersion = false )
02395     {
02396         $http = eZHTTPTool::instance();
02397         $contentObjectAttributes = $this->contentObjectAttributes( true, $objectVersion );
02398         $oldAttributeDataBaseName = $customActionParameters['base_name'];
02399         $customActionParameters['base_name'] = $attributeDataBaseName;
02400         foreach( $contentObjectAttributes as $contentObjectAttribute )
02401         {
02402             if ( isset( $customActionAttributeArray[$contentObjectAttribute->attribute( 'id' )] ) )
02403             {
02404                 $customActionAttributeID = $customActionAttributeArray[$contentObjectAttribute->attribute( 'id' )]['id'];
02405                 $customAction = $customActionAttributeArray[$contentObjectAttribute->attribute( 'id' )]['value'];
02406                 $contentObjectAttribute->customHTTPAction( $http, $customAction, $customActionParameters );
02407             }
02408 
02409             $contentObjectAttribute->handleCustomHTTPActions( $http, $attributeDataBaseName,
02410                                                               $customActionAttributeArray, $customActionParameters );
02411         }
02412         $customActionParameters['base_name'] = $oldAttributeDataBaseName;
02413     }
02414 
02415     static function recursionProtectionStart()
02416     {
02417         $GLOBALS["ez_content_object_recursion_protect"] = array();
02418     }
02419 
02420     static function recursionProtect( $id )
02421     {
02422         if ( isset( $GLOBALS["ez_content_object_recursion_protect"][$id] ) )
02423         {
02424             return false;
02425         }
02426         else
02427         {
02428              $GLOBALS["ez_content_object_recursion_protect"][$id] = true;
02429              return true;
02430         }
02431     }
02432 
02433     static function recursionProtectionEnd()
02434     {
02435         unset( $GLOBALS["ez_content_object_recursion_protect"] );
02436     }
02437 
02438     /*!
02439      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
02440      the calls within a db transaction; thus within db->begin and db->commit.
02441      */
02442     function storeInput( $contentObjectAttributes,
02443                          $attributeInputMap )
02444     {
02445         $db = eZDB::instance();
02446         $db->begin();
02447         foreach ( $contentObjectAttributes as $contentObjectAttribute )
02448         {
02449             if ( isset( $attributeInputMap[$contentObjectAttribute->attribute('id')] ) )
02450             {
02451                 $contentObjectAttribute->store();
02452             }
02453         }
02454         $db->commit();
02455         unset( $this->ContentObjectAttributes );
02456     }
02457 
02458     /*!
02459      Returns the next available version number for this object.
02460     */
02461     function nextVersion()
02462     {
02463         $db = eZDB::instance();
02464         $versions = $db->arrayQuery( "SELECT ( MAX( version ) + 1 ) AS next_id FROM ezcontentobject_version
02465                        WHERE contentobject_id='$this->ID'" );
02466         return $versions[0]["next_id"];
02467 
02468     }
02469 
02470     /*!
02471      Returns the previous available version number for this object, if existing, false otherwise ( if the object has only one version )
02472     */
02473     function previousVersion()
02474     {
02475         $db = eZDB::instance();
02476         $versions = $db->arrayQuery( "SELECT version FROM ezcontentobject_version
02477                                       WHERE contentobject_id='$this->ID'
02478                                       ORDER BY version DESC", array( 'limit' => 2 ) );
02479         if ( count( $versions ) > 1 and isset( $versions[1]['version'] ) )
02480         {
02481             return $versions[1]['version'];
02482         }
02483         else
02484         {
02485             return false;
02486         }
02487     }
02488 
02489     /*!
02490      Returns number of exist versions.
02491     */
02492     function getVersionCount()
02493     {
02494         $db = eZDB::instance();
02495         $versionCount = $db->arrayQuery( "SELECT ( COUNT( version ) ) AS version_count FROM ezcontentobject_version
02496                        WHERE contentobject_id='$this->ID'" );
02497         return $versionCount[0]["version_count"];
02498 
02499     }
02500 
02501     function currentLanguage()
02502     {
02503         return $this->CurrentLanguage;
02504     }
02505 
02506     function currentLanguageObject()
02507     {
02508         if ( $this->CurrentLanguage )
02509         {
02510             $language = eZContentLanguage::fetchByLocale( $this->CurrentLanguage );
02511         }
02512         else
02513         {
02514             $language = false;
02515         }
02516 
02517         return $language;
02518     }
02519 
02520     function setCurrentLanguage( $lang )
02521     {
02522         $this->CurrentLanguage = $lang;
02523         $this->Name = null;
02524     }
02525 
02526     function initialLanguage()
02527     {
02528         return isset( $this->InitialLanguageID ) ? eZContentLanguage::fetch( $this->InitialLanguageID ) : false;
02529     }
02530 
02531     function initialLanguageCode()
02532     {
02533         $initialLanguage = $this->initialLanguage();
02534         // If current contentobject is "Top Level Nodes" than it doesn't have "initial Language" and "locale".
02535         return ( $initialLanguage !== false ) ?  $initialLanguage->attribute( 'locale' ) : false;
02536     }
02537 
02538     /*!
02539      Adds a new location (node) to the current object.
02540      \param $parenNodeID The id of the node to use as parent.
02541      \param $asObject    If true it will return the new child-node as an object, if not it returns the ID.
02542      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
02543       the calls within a db transaction; thus within db->begin and db->commit.
02544       */
02545     function addLocation( $parentNodeID, $asObject = false )
02546     {
02547         $node = eZContentObjectTreeNode::addChildTo( $this->ID, $parentNodeID, true, $this->CurrentVersion );
02548 
02549         $data = array( 'contentobject_id' => $this->ID,
02550                        'contentobject_version' => $this->attribute( 'current_version' ),
02551                        'parent_node' => $parentNodeID,
02552                        'is_main' => 0 );
02553         $nodeAssignment = eZNodeAssignment::create( $data );
02554         $nodeAssignment->setAttribute( 'op_code', eZNodeAssignment::OP_CODE_CREATE_NOP );
02555         $nodeAssignment->store();
02556 
02557         if ( $asObject )
02558         {
02559             return $node;
02560         }
02561         else
02562         {
02563             return $node->attribute( 'node_id' );
02564         }
02565     }
02566 
02567     /*!
02568      Adds a link to the given content object id.
02569      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
02570      the calls within a db transaction; thus within db->begin and db->commit.
02571     */
02572     function addContentObjectRelation( $toObjectID,
02573                                        $fromObjectVersion = false,
02574                                        $attributeID = 0,
02575                                        $relationType = eZContentObject::RELATION_COMMON )
02576     {
02577         if ( $attributeID !== 0 )
02578         {
02579             $relationType = eZContentObject::RELATION_ATTRIBUTE;
02580         }
02581 
02582         $relationType =(int) $relationType;
02583         if ( ( $relationType & eZContentObject::RELATION_ATTRIBUTE ) != 0 &&
02584              $relationType != eZContentObject::RELATION_ATTRIBUTE )
02585         {
02586             eZDebug::writeWarning( "Object relation type conflict", __METHOD__ );
02587         }
02588 
02589         $db = eZDB::instance();
02590 
02591         if ( !$fromObjectVersion )
02592             $fromObjectVersion = $this->CurrentVersion;
02593 
02594         $fromObjectID = $this->ID;
02595 
02596         if ( !is_numeric( $toObjectID ) )
02597         {
02598             eZDebug::writeError( "Related object ID (toObjectID): '$toObjectID', is not a numeric value.",
02599                                  "eZContentObject::addContentObjectRelation" );
02600             return false;
02601         }
02602         $fromObjectID =(int) $fromObjectID;
02603         $attributeID =(int) $attributeID;
02604         $fromObjectVersion =(int) $fromObjectVersion;
02605         $relationBaseType = ( $relationType & eZContentObject::RELATION_ATTRIBUTE ) ?
02606                                 eZContentObject::RELATION_ATTRIBUTE :
02607                                 eZContentObject::RELATION_COMMON | eZContentObject::RELATION_EMBED | eZContentObject::RELATION_LINK;
02608         $relationTypeMatch = $db->bitAnd( 'relation_type', $relationBaseType );
02609         $query = "SELECT count(*) AS count
02610                   FROM   ezcontentobject_link
02611                   WHERE  from_contentobject_id=$fromObjectID AND
02612                          from_contentobject_version=$fromObjectVersion AND
02613                          to_contentobject_id=$toObjectID AND
02614                          $relationTypeMatch != 0 AND
02615                          contentclassattribute_id=$attributeID AND
02616                          op_code='0'";
02617         $count = $db->arrayQuery( $query );
02618         // if current relation does not exist
02619         if ( !isset( $count[0]['count'] ) ||  $count[0]['count'] == '0'  )
02620         {
02621             $db->begin();
02622             $db->query( "INSERT INTO ezcontentobject_link ( from_contentobject_id, from_contentobject_version, to_contentobject_id, contentclassattribute_id, relation_type )
02623                          VALUES ( $fromObjectID, $fromObjectVersion, $toObjectID, $attributeID, $relationType )" );
02624             // if an object relation is being added and it is in draft, add the row with op_code 1
02625             if ( $attributeID == 0 && $fromObjectVersion != $this->CurrentVersion )
02626             {
02627                 $db->query( "INSERT INTO ezcontentobject_link ( from_contentobject_id, from_contentobject_version, to_contentobject_id, contentclassattribute_id, op_code, relation_type )
02628                              VALUES ( $fromObjectID, $fromObjectVersion, $toObjectID, $attributeID, '1', $relationType )" );
02629             }
02630             $db->commit();
02631         }
02632         elseif ( isset( $count[0]['count'] ) &&
02633                  $count[0]['count'] != '0' &&
02634                  $attributeID == 0 &&
02635                  (eZContentObject::RELATION_ATTRIBUTE & $relationType) == 0 )
02636         {
02637             $db->begin();
02638             $newRelationType = $db->bitOr( 'relation_type', $relationType );
02639             $db->query( "UPDATE ezcontentobject_link
02640                          SET    relation_type = $newRelationType
02641                          WHERE  from_contentobject_id=$fromObjectID AND
02642                                 from_contentobject_version=$fromObjectVersion AND
02643                                 to_contentobject_id=$toObjectID AND
02644                                 contentclassattribute_id=$attributeID AND
02645                                 op_code='0'" );
02646             // if an object relation is being added and it is in draft, add the row with op_code 1
02647             if ( $attributeID == 0 && $fromObjectVersion != $this->CurrentVersion )
02648             {
02649                 $db->query( "INSERT INTO ezcontentobject_link ( from_contentobject_id, from_contentobject_version, to_contentobject_id, contentclassattribute_id, op_code, relation_type )
02650                              VALUES ( $fromObjectID, $fromObjectVersion, $toObjectID, $attributeID, '1', $relationType )" );
02651             }
02652             $db->commit();
02653         }
02654     }
02655 
02656     /*!
02657      Removes a link to the given content object id.
02658      \param $toObjectID If \c false it will delete relations to all the objects.
02659      \param $attributeID ID of class attribute.
02660                          IF it is > 0 we remove relations created by a specific objectrelation[list] attribute.
02661                          If it is set to 0 we remove relations created without using of objectrelation[list] attribute.
02662                          If it is set to false, we remove all relations, no matter how were they created:
02663                          using objectrelation[list] attribute or using "Add related objects" functionality in obect editing mode.
02664      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
02665      the calls within a db transaction; thus within db->begin and db->commit.
02666     */
02667     function removeContentObjectRelation( $toObjectID = false, $fromObjectVersion = false, $attributeID = 0, $relationType = eZContentObject::RELATION_COMMON )
02668     {
02669         $db = eZDB::instance();
02670 
02671         if ( !$fromObjectVersion )
02672             $fromObjectVersion = $this->CurrentVersion;
02673         $fromObjectVersion = (int) $fromObjectVersion;
02674         $fromObjectID = $this->ID;
02675 
02676         if ( $toObjectID !== false )
02677         {
02678             $toObjectID =(int) $toObjectID;
02679             $toObjectCondition = "AND to_contentobject_id=$toObjectID";
02680         }
02681         else
02682             $toObjectCondition = '';
02683 
02684         if ( $attributeID !== false )
02685         {
02686             $attributeID =(int) $attributeID;
02687             $classAttributeCondition = "AND contentclassattribute_id=$attributeID";
02688         }
02689         else
02690             $classAttributeCondition = '';
02691 
02692         $lastRelationType = 0;
02693         $db->begin();
02694         // if an object relation is being removed from the draft, add the row with op_code -1
02695         if ( !$attributeID && $fromObjectVersion != $this->CurrentVersion )
02696         {
02697             $rows = $db->arrayQuery( "SELECT * FROM ezcontentobject_link
02698                                       WHERE from_contentobject_id=$fromObjectID
02699                                         AND from_contentobject_version=$fromObjectVersion
02700                                         AND contentclassattribute_id='0'
02701                                         $toObjectCondition
02702                                         AND op_code='0'" );
02703             foreach ( $rows as $row )
02704             {
02705                 $db->query( "INSERT INTO ezcontentobject_link ( from_contentobject_id, from_contentobject_version, to_contentobject_id, contentclassattribute_id, op_code, relation_type )
02706                              VALUES ( $fromObjectID, $fromObjectVersion, " . $row['to_contentobject_id'] . ", '0', '-1', $relationType )" );
02707                 $lastRelationType = (int) $row['relation_type'];
02708             }
02709         }
02710 
02711         if ( 0 !== ( eZContentObject::RELATION_ATTRIBUTE & $relationType ) ||
02712              0 != $attributeID ||
02713              $relationType == $lastRelationType )
02714         {
02715             $db->query( "DELETE FROM ezcontentobject_link
02716                          WHERE       from_contentobject_id=$fromObjectID AND
02717                                      from_contentobject_version=$fromObjectVersion $classAttributeCondition $toObjectCondition AND
02718                                      op_code='0'" );
02719         }
02720         else
02721         {
02722             if ( $db->databaseName() == 'oracle' )
02723             {
02724                 $notRelationType = - ( $relationType + 1 );
02725                 $db->query( "UPDATE ezcontentobject_link
02726                              SET    relation_type = " . $db->bitAnd( 'relation_type', $notRelationType ) . "
02727                              WHERE  from_contentobject_id=$fromObjectID AND
02728                                     from_contentobject_version=$fromObjectVersion $classAttributeCondition $toObjectCondition AND
02729                                     op_code='0'" );
02730             }
02731             else
02732             {
02733                 $db->query( "UPDATE ezcontentobject_link
02734                              SET    relation_type = ( relation_type & ".(~$relationType)." )
02735                              WHERE  from_contentobject_id=$fromObjectID AND
02736                                     from_contentobject_version=$fromObjectVersion $classAttributeCondition $toObjectCondition AND
02737                                     op_code='0'" );
02738             }
02739         }
02740 
02741         $db->commit();
02742 
02743     }
02744 
02745     function copyContentObjectRelations( $currentVersion, $newVersion, $newObjectID = false )
02746     {
02747         $objectID = $this->ID;
02748         if ( !$newObjectID )
02749         {
02750             $newObjectID = $objectID;
02751         }
02752 
02753         $db = eZDB::instance();
02754         $db->begin();
02755 
02756         $relations = $db->arrayQuery( "SELECT to_contentobject_id, op_code, relation_type FROM ezcontentobject_link
02757                                        WHERE contentclassattribute_id='0'
02758                                          AND from_contentobject_id='$objectID'
02759                                          AND from_contentobject_version='$currentVersion'" );
02760         foreach ( $relations as $relation )
02761         {
02762             $toContentObjectID = $relation['to_contentobject_id'];
02763             $opCode = $relation['op_code'];
02764             $relationType = $relation['relation_type'];
02765             $db->query( "INSERT INTO ezcontentobject_link( contentclassattribute_id,
02766                                                            from_contentobject_id,
02767                                                            from_contentobject_version,
02768                                                            to_contentobject_id,
02769                                                            op_code,
02770                                                            relation_type )
02771                          VALUES ( '0', '$newObjectID', '$newVersion', '$toContentObjectID', '$opCode', '$relationType' )" );
02772         }
02773 
02774         $db->commit();
02775     }
02776 
02777     static function isObjectRelationTyped()
02778     {
02779         $siteIni = eZINI::instance( 'site.ini' );
02780         if ( $siteIni->hasVariable( 'BackwardCompatibilitySettings', 'ObjectRelationTyped' ) )
02781         {
02782             if ( 'enabled' == $siteIni->variable( 'BackwardCompatibilitySettings', 'ObjectRelationTyped' ) )
02783             {
02784                 return true;
02785             }
02786         }
02787         return false;
02788     }
02789 
02790     static function relationTypeMask( $allRelations = false )
02791     {
02792         $relationTypeMask = eZContentObject::RELATION_COMMON |
02793                             eZContentObject::RELATION_EMBED;
02794 
02795         if ( eZContentObject::isObjectRelationTyped() )
02796         {
02797             $relationTypeMask |= eZContentObject::RELATION_LINK;
02798         }
02799 
02800         if ( $allRelations )
02801         {
02802             $relationTypeMask |= eZContentObject::RELATION_ATTRIBUTE;
02803         }
02804 
02805         return $relationTypeMask;
02806     }
02807 
02808     /*!
02809      Returns the related or reverse related objects:
02810      \param $attributeID :  ( makes sense only when $params['AllRelations'] not set or eZContentObject::RELATION_ATTRIBUTE )
02811                             >0              - return relations made with attribute ID ( "related object(s)" datatype )
02812                             0 or false  ( $params['AllRelations'] is eZContentObject::RELATION_ATTRIBUTE )
02813                                             - return relations made with any attributes
02814                             false       ( $params['AllRelations'] not set )
02815                                             - return ALL relations (deprecated, use "$params['AllRelations'] = true" instead)
02816      \param $groupByAttribute : false - return all relations as an array of content objects
02817                                 true  - return all relations groupped by attribute ID
02818                                 This parameter makes sense only when $attributeID == false or $params['AllRelations'] = true
02819      \param $params : other parameters from template fetch function :
02820                 $params['AllRelations']     - relation type filter :
02821                             true    - return ALL relations, including attribute-level
02822                             false   - return object-level relations
02823                             >0      - bit mask of EZ_CONTENT_OBJECT_RELATION_* values
02824                 $params['SortBy']           - related objects sorting mode.
02825                             Supported modes: class_identifier, class_name, modified, name, published, section
02826                 $params['IgnoreVisibility'] - ignores 'hidden' state of related objects if true
02827                 $params['RelatedClassIdentifiers'] - limit returned relations to objects of the specified class identifiers
02828      \param $reverseRelatedObjects : if "true" returns reverse related contentObjects
02829                                      if "false" returns related contentObjects
02830     */
02831     function relatedObjects( $fromObjectVersion = false,
02832                              $objectID = false,
02833                              $attributeID = 0,
02834                              $groupByAttribute = false,
02835                              $params = false,
02836                              $reverseRelatedObjects = false )
02837     {
02838         if ( $fromObjectVersion == false )
02839             $fromObjectVersion = isset( $this->CurrentVersion ) ? $this->CurrentVersion : false;
02840         $fromObjectVersion =(int) $fromObjectVersion;
02841         if( !$objectID )
02842             $objectID = $this->ID;
02843         $objectID =(int) $objectID;
02844 
02845         $limit            = ( isset( $params['Limit']  ) && is_numeric( $params['Limit']  ) ) ? $params['Limit']              : false;
02846         $offset           = ( isset( $params['Offset'] ) && is_numeric( $params['Offset'] ) ) ? $params['Offset']             : false;
02847         $asObject         = ( isset( $params['AsObject']          ) )                         ? $params['AsObject']           : true;
02848         $loadDataMap      = ( isset( $params['LoadDataMap'] ) )                               ? $params['LoadDataMap']        : false;
02849 
02850 
02851         $db = eZDB::instance();
02852         $sortingString = '';
02853         $sortingInfo = array( 'attributeFromSQL' => '',
02854                               'attributeWhereSQL' => '',
02855                               'attributeTargetSQL' => '' );
02856         $relatedClassIdentifiersSQL = '';
02857         $showInvisibleNodesCond = '';
02858         // process params (only SortBy and IgnoreVisibility currently supported):
02859         // Supported sort_by modes:
02860         //   class_identifier, class_name, modified, name, published, section
02861         if ( is_array( $params ) )
02862         {
02863             if ( isset( $params['SortBy'] ) )
02864             {
02865                 $validSortBy = array( 'class_identifier', 'class_name', 'modified', 'name', 'published', 'section' );
02866                 $sortByParam = array();
02867                 if ( is_array( $params['SortBy'] ) )
02868                 {
02869                     // only one SortBy, as a simple array
02870                     if ( !is_array( $params['SortBy'][0] ) )
02871                     {
02872                         if ( !in_array( $params['SortBy'][0], $validSortBy ) )
02873                             eZDebug::writeWarning( "Unsupported sort_by parameter {$params['SortBy'][0]}; check the online documentation for the list of supported sort types", __METHOD__ );
02874                         else
02875                             $sortByParam[] = $params['SortBy'];
02876                     }
02877                     // multiple SortBy, check each of them one by one, and keep valid ones
02878                     else
02879                     {
02880                         $invalidSortBy = array();
02881                         foreach( $params['SortBy'] as $sortByTuple )
02882                         {
02883                             if ( !in_array( $sortByTuple[0], $validSortBy ) )
02884                                 $invalidSortBy[] = $sortByTuple[0];
02885                             else
02886                                 $sortByParam[] = $sortByTuple;
02887                         }
02888                         if ( count( $invalidSortBy ) > 0 )
02889                         {
02890                             eZDebug::writeWarning( "Unsupported sort_by parameter(s) " . implode( ', ', $invalidSortBy ) . "; check the online documentation for the list of supported sort types", __METHOD__ );
02891                         }
02892                     }
02893                 }
02894                 if ( count( $sortByParam ) > 0 )
02895                 {
02896                     $sortingInfo = eZContentObjectTreeNode::createSortingSQLStrings( $sortByParam );
02897                     $sortingString = ' ORDER BY ' . $sortingInfo['sortingFields'];
02898                 }
02899             }
02900             if ( isset( $params['IgnoreVisibility'] ) )
02901             {
02902                 $showInvisibleNodesCond = self::createFilterByVisibilitySQLString( $params['IgnoreVisibility'] );
02903             }
02904 
02905             // related class identifier filter
02906             $relatedClassIdentifiersSQL = '';
02907             if ( isset( $params['RelatedClassIdentifiers'] ) && is_array( $params['RelatedClassIdentifiers'] ) )
02908             {
02909                 $relatedClassIdentifiers = array();
02910                 foreach( $params['RelatedClassIdentifiers'] as $classIdentifier )
02911                 {
02912                     $relatedClassIdentifiers[] = "'" . $db->escapeString( $classIdentifier ) . "'";
02913                 }
02914                 $relatedClassIdentifiersSQL = $db->generateSQLINStatement( $relatedClassIdentifiers, 'ezcontentclass.identifier', false, true, 'string' ). " AND";
02915                 unset( $classIdentifier, $relatedClassIdentifiers );
02916             }
02917         }
02918 
02919         $relationTypeMasking = '';
02920         $relationTypeMask = isset( $params['AllRelations'] ) ? $params['AllRelations'] : ( $attributeID === false );
02921         if ( $attributeID && ( $relationTypeMask === false || $relationTypeMask === eZContentObject::RELATION_ATTRIBUTE ) )
02922         {
02923             $attributeID =(int) $attributeID;
02924             $relationTypeMasking .= " AND contentclassattribute_id=$attributeID ";
02925             $relationTypeMask = eZContentObject::RELATION_ATTRIBUTE;
02926         }
02927         elseif ( is_bool( $relationTypeMask ) )
02928         {
02929             $relationTypeMask = eZContentObject::relationTypeMask( $relationTypeMask );
02930         }
02931 
02932         if ( $db->databaseName() == 'oracle' )
02933         {
02934             $relationTypeMasking .= " AND bitand( relation_type, $relationTypeMask ) <> 0 ";
02935         }
02936         else
02937         {
02938             $relationTypeMasking .= " AND ( relation_type & $relationTypeMask ) <> 0 ";
02939         }
02940 
02941         // Create SQL
02942         $versionNameTables = ', ezcontentobject_name ';
02943         $versionNameTargets = ', ezcontentobject_name.name as name,  ezcontentobject_name.real_translation ';
02944 
02945         $versionNameJoins = " AND ezcontentobject.id = ezcontentobject_name.contentobject_id AND
02946                                  ezcontentobject.current_version = ezcontentobject_name.content_version AND ";
02947         $versionNameJoins .= eZContentLanguage::sqlFilter( 'ezcontentobject_name', 'ezcontentobject' );
02948 
02949         $fromOrToContentObjectID = $reverseRelatedObjects == false ? " AND ezcontentobject.id=ezcontentobject_link.to_contentobject_id AND
02950                                                                       ezcontentobject_link.from_contentobject_id='$objectID' AND
02951                                                                       ezcontentobject_link.from_contentobject_version='$fromObjectVersion' "
02952                                                                    : " AND ezcontentobject.id=ezcontentobject_link.from_contentobject_id AND
02953                                                                       ezcontentobject_link.to_contentobject_id=$objectID AND
02954                                                                       ezcontentobject_link.from_contentobject_version=ezcontentobject.current_version ";
02955             $query = "SELECT ";
02956 
02957             if ( $groupByAttribute )
02958             {
02959                 $query .= "ezcontentobject_link.contentclassattribute_id, ";
02960             }
02961             $query .= "
02962                         ezcontentclass.serialized_name_list AS class_serialized_name_list,
02963                         ezcontentclass.identifier as contentclass_identifier,
02964                         ezcontentclass.is_container as is_container,
02965                         ezcontentobject.* $versionNameTargets
02966                         $sortingInfo[attributeTargetSQL]
02967                      FROM
02968                         ezcontentclass,
02969                         ezcontentobject,
02970                         ezcontentobject_link
02971                         $versionNameTables
02972                         $sortingInfo[attributeFromSQL]
02973                      WHERE
02974                         ezcontentclass.id=ezcontentobject.contentclass_id AND
02975                         ezcontentclass.version=0 AND
02976                         ezcontentobject.status=" . eZContentObject::STATUS_PUBLISHED . " AND
02977                         $sortingInfo[attributeWhereSQL]
02978                         $relatedClassIdentifiersSQL
02979                         ezcontentobject_link.op_code='0'
02980                         $relationTypeMasking
02981                         $fromOrToContentObjectID
02982                         $showInvisibleNodesCond
02983                         $versionNameJoins
02984                         $sortingString";
02985         if ( !$offset && !$limit )
02986         {
02987             $relatedObjects = $db->arrayQuery( $query );
02988         }
02989         else
02990         {
02991             $relatedObjects = $db->arrayQuery( $query, array( 'offset' => $offset,
02992                                                              'limit'  => $limit ) );
02993         }
02994 
02995         $ret = array();
02996         $tmp = array();
02997         foreach ( $relatedObjects as $object )
02998         {
02999             if ( $asObject )
03000             {
03001                 $obj = new eZContentObject( $object );
03002                 $obj->ClassName = eZContentClass::nameFromSerializedString( $object['class_serialized_name_list'] );
03003             }
03004             else
03005             {
03006                 $obj = $object;
03007             }
03008 
03009             $tmp[] = $obj;
03010 
03011             if ( !$groupByAttribute )
03012             {
03013                 $ret[] = $obj;
03014             }
03015             else
03016             {
03017                 $classAttrID = $object['contentclassattribute_id'];
03018 
03019                 if ( !isset( $ret[$classAttrID] ) )
03020                     $ret[$classAttrID] = array();
03021 
03022                 $ret[$classAttrID][] = $obj;
03023             }
03024         }
03025         if ( $loadDataMap && $asObject )
03026             eZContentObject::fillNodeListAttributes( $tmp );
03027         return $ret;
03028     }
03029 
03030     /*!
03031      Returns the related objects.
03032     \param $attributeID :  ( makes sense only when $params['AllRelations'] not set or eZContentObject::RELATION_ATTRIBUTE )
03033                            >0           - return relations made with attribute ID ( "related object(s)" datatype )
03034                            0 or false  ( $params['AllRelations'] is eZContentObject::RELATION_ATTRIBUTE )
03035                                            - return relations made with any attributes
03036                            false        ( $params['AllRelations'] not set )
03037                                            - return ALL relations (deprecated, use "$params['AllRelations'] = true" instead)
03038     \param $groupByAttribute : false - return all relations as an array of content objects
03039                                true  - return all relations groupped by attribute ID
03040                                This parameter makes sense only when $attributeID == false or $params['AllRelations'] = true
03041     \param $params : other parameters from template fetch function :
03042                $params['AllRelations'] - relation type filter :
03043                            true - return ALL relations, including attribute-level
03044                            false    - return object-level relations
03045                            >0       - bit mask of EZ_CONTENT_OBJECT_RELATION_* values
03046                 $params['SortBy']           - related objects sorting mode.
03047                             Supported modes: class_identifier, class_name, modified, name, published, section
03048                 $params['IgnoreVisibility'] - ignores 'hidden' state of related objects if true
03049                 $params['RelatedClassIdentifiers'] - limit returned relations to objects of the specified class identifiers
03050     */
03051     function relatedContentObjectList( $fromObjectVersion = false,
03052                                        $fromObjectID = false,
03053                                        $attributeID = 0,
03054                                        $groupByAttribute = false,
03055                                        $params = false )
03056     {
03057         eZDebugSetting::writeDebug( 'kernel-content-object-related-objects', $fromObjectID, "objectID" );
03058         return $this->relatedObjects( $fromObjectVersion, $fromObjectID, $attributeID, $groupByAttribute, $params );
03059     }
03060 
03061     /*!
03062      Returns the xml-linked objects.
03063     */
03064     function linkedContentObjectList( $fromObjectVersion = false, $fromObjectID = false )
03065     {
03066         return $this->relatedObjects( $fromObjectVersion,
03067                                       $fromObjectID,
03068                                       0,
03069                                       false,
03070                                       array( 'AllRelations' => eZContentObject::RELATION_LINK ) );
03071     }
03072 
03073     /*!
03074      Returns the xml-embedded objects.
03075     */
03076     function embeddedContentObjectList( $fromObjectVersion = false, $fromObjectID = false )
03077     {
03078         return $this->relatedObjects( $fromObjectVersion,
03079                                       $fromObjectID,
03080                                       0,
03081                                       false,
03082                                       array( 'AllRelations' => eZContentObject::RELATION_EMBED ) );
03083     }
03084 
03085     /*!
03086      Returns the reverse xml-linked objects.
03087     */
03088     function reverseLinkedObjectList( $fromObjectVersion = false, $fromObjectID = false )
03089     {
03090         return $this->relatedObjects( $fromObjectVersion,
03091                                       $fromObjectID,
03092                                       0,
03093                                       false,
03094                                       array( 'AllRelations' => eZContentObject::RELATION_LINK ),
03095                                       true );
03096     }
03097 
03098     /*!
03099      Returns the reverse xml-embedded objects.
03100     */
03101     function reverseEmbeddedObjectList( $fromObjectVersion = false, $fromObjectID = false )
03102     {
03103         return $this->relatedObjects( $fromObjectVersion,
03104                                       $fromObjectID,
03105                                       0,
03106                                       false,
03107                                       array( 'AllRelations' => eZContentObject::RELATION_EMBED ),
03108                                       true );
03109     }
03110 
03111     // left for compatibility
03112     function relatedContentObjectArray( $fromObjectVersion = false,
03113                                         $fromObjectID = false,
03114                                         $attributeID = 0,
03115                                         $params = false )
03116     {
03117         return eZContentObject::relatedContentObjectList( $fromObjectVersion,
03118                                                           $fromObjectID,
03119                                                           $attributeID,
03120                                                           false,
03121                                                           $params );
03122     }
03123 
03124     /*!
03125      \return the number of related objects
03126     \param $attributeID :  ( makes sense only when $params['AllRelations'] not set or eZContentObject::RELATION_ATTRIBUTE )
03127                            >0           - return relations made with attribute ID ( "related object(s)" datatype )
03128                            0 or false  ( $params['AllRelations'] is eZContentObject::RELATION_ATTRIBUTE )
03129                                            - return relations made with any attributes
03130                            false        ( $params['AllRelations'] not set )
03131                                            - return ALL relations (deprecated, use "$params['AllRelations'] = true" instead)
03132     \param $params : other parameters from template fetch function :
03133                $params['AllRelations'] - relation type filter :
03134                            true - return ALL relations, including attribute-level
03135                            false    - return object-level relations
03136                            >0       - bit mask of EZ_CONTENT_OBJECT_RELATION_* values
03137                 $params['SortBy']           - related objects sorting mode.
03138                             Supported modes: class_identifier, class_name, modified, name, published, section
03139                 $params['IgnoreVisibility'] - ignores 'hidden' state of related objects if true
03140     */
03141     function relatedContentObjectCount( $fromObjectVersion = false,
03142                                         $attributeID = 0,
03143                                         $params = false )
03144     {
03145         eZDebugSetting::writeDebug( 'kernel-content-object-related-objects', $this->ID, "relatedContentObjectCount::objectID" );
03146         return $this->relatedObjectCount( $fromObjectVersion,
03147                                           $attributeID,
03148                                           false,
03149                                           $params );
03150     }
03151 
03152     /*!
03153      Returns the objects to which this object are related .
03154     \param $attributeID :  ( makes sense only when $params['AllRelations'] not set or eZContentObject::RELATION_ATTRIBUTE )
03155                            >0           - return relations made with attribute ID ( "related object(s)" datatype )
03156                            0 or false  ( $params['AllRelations'] is eZContentObject::RELATION_ATTRIBUTE )
03157                                            - return relations made with any attributes
03158                            false        ( $params['AllRelations'] not set )
03159                                            - return ALL relations (deprecated, use "$params['AllRelations'] = true" instead)
03160     \param $groupByAttribute : false - return all relations as an array of content objects
03161                                true  - return all relations groupped by attribute ID
03162                                This parameter makes sense only when $attributeID == false or $params['AllRelations'] = true
03163     \param $params : other parameters from template fetch function :
03164                 $params['AllRelations'] - relation type filter :
03165                            true - return ALL relations, including attribute-level
03166                            false    - return object-level relations
03167                            >0       - bit mask of EZ_CONTENT_OBJECT_RELATION_* values
03168                 $params['SortBy']           - related objects sorting mode.
03169                             Supported modes: class_identifier, class_name, modified, name, published, section
03170                 $params['IgnoreVisibility'] - ignores 'hidden' state of related objects if true
03171     */
03172     function reverseRelatedObjectList( $version = false,
03173                                        $attributeID = 0,
03174                                        $groupByAttribute = false,
03175                                        $params = false )
03176     {
03177         return $this->relatedObjects( $version, $this->ID, $attributeID, $groupByAttribute, $params, true );
03178     }
03179 
03180     /*!
03181      Returns the xml-linked objects count.
03182     */
03183     function linkedContentObjectCount( $fromObjectVersion = false )
03184     {
03185         return $this->relatedObjectCount( $fromObjectVersion,
03186                                           0,
03187                                           false,
03188                                           array( 'AllRelations' => eZContentObject::RELATION_LINK ) );
03189     }
03190 
03191     /*!
03192      Returns the xml-embedded objects count.
03193     */
03194     function embeddedContentObjectCount( $fromObjectVersion = false )
03195     {
03196         return $this->relatedObjectCount( $fromObjectVersion,
03197                                           0,
03198                                           false,
03199                                           array( 'AllRelations' => eZContentObject::RELATION_EMBED ) );
03200     }
03201 
03202     /*!
03203      Returns the reverse xml-linked objects count.
03204     */
03205     function reverseLinkedObjectCount( $fromObjectVersion = false )
03206     {
03207         return $this->relatedObjectCount( $fromObjectVersion,
03208                                           0,
03209                                           true,
03210                                           array( 'AllRelations' => eZContentObject::RELATION_LINK ) );
03211     }
03212 
03213     /*!
03214      Returns the reverse xml-embedded objects count.
03215     */
03216     function reverseEmbeddedObjectCount( $fromObjectVersion = false )
03217     {
03218         return $this->relatedObjectCount( $fromObjectVersion,
03219                                           0,
03220                                           true,
03221                                           array( 'AllRelations' => eZContentObject::RELATION_EMBED ) );
03222     }
03223 
03224     /**
03225      * Fetch the number of (reverse) related objects
03226      *
03227      * @param int $version
03228      * @param int $attributeID
03229      *        This parameter only makes sense if $params[AllRelations] is unset,
03230      *        set to false, or matches eZContentObject::RELATION_ATTRIBUTE
03231      *        Possible values:
03232      *        - 0 or false:
03233      *          Count relations made with any attribute
03234      *        - >0
03235      *          Count relations made with attribute $attributeID
03236      * @param int|false $reverseRelatedObjects
03237      *        Wether to count related objects (false) or reverse related
03238      *        objects (false)
03239      * @param array|false $params
03240      *        Various params, as an associative array.
03241      *        Possible values:
03242      *        - AllRelations (bool|int)
03243      *          true: count ALL relations, object and attribute level
03244      *          false: only count object level relations
03245      *          other: bit mask of eZContentObject::RELATION_* constants
03246      *        - IgnoreVisibility (bool)
03247      *          If true, 'hidden' status will be ignored
03248      *
03249      * @return int The number of (reverse) related objects for the object
03250      */
03251     function relatedObjectCount( $version = false, $attributeID = 0, $reverseRelatedObjects = false, $params = false )
03252     {
03253         $objectID = $this->ID;
03254         if ( $version == false )
03255             $version = isset( $this->CurrentVersion ) ? $this->CurrentVersion : false;
03256         $version = (int) $version;
03257 
03258         $db = eZDB::instance();
03259         $showInvisibleNodesCond = '';
03260 
03261         // process params (only IgnoreVisibility currently supported):
03262         if ( is_array( $params ) )
03263         {
03264             if ( isset( $params['IgnoreVisibility'] ) )
03265             {
03266                 $showInvisibleNodesCond = self::createFilterByVisibilitySQLString( $params['IgnoreVisibility'], 'inner_object' );
03267             }
03268         }
03269 
03270         $relationTypeMasking = '';
03271         $relationTypeMask = isset( $params['AllRelations'] ) ? $params['AllRelations'] : ( $attributeID === false );
03272         if ( $attributeID && ( $relationTypeMask === false || $relationTypeMask === eZContentObject::RELATION_ATTRIBUTE ) )
03273         {
03274             $attributeID =(int) $attributeID;
03275             $relationTypeMasking .= " AND inner_link.contentclassattribute_id = $attributeID ";
03276             $relationTypeMask = eZContentObject::RELATION_ATTRIBUTE;
03277         }
03278         elseif ( is_bool( $relationTypeMask ) )
03279         {
03280             $relationTypeMask = eZContentObject::relationTypeMask( $relationTypeMask );
03281         }
03282 
03283         if ( $db->databaseName() == 'oracle' )
03284         {
03285             $relationTypeMasking .= " AND bitand( inner_link.relation_type, $relationTypeMask ) <> 0 ";
03286         }
03287         else
03288         {
03289             $relationTypeMasking .= " AND ( inner_link.relation_type & $relationTypeMask ) <> 0 ";
03290         }
03291 
03292         if ( $reverseRelatedObjects )
03293         {
03294             $outerObjectIDSQL = 'outer_object.id = outer_link.from_contentobject_id';
03295             if ( is_array( $objectID ) )
03296             {
03297                 if ( count( $objectID ) > 0 )
03298                 {
03299                     $objectIDSQL = ' AND ' . $db->generateSQLINStatement( $objectID, 'inner_link.to_contentobject_id', false, false, 'int' ) . ' AND
03300                                      inner_link.from_contentobject_version = inner_object.current_version';
03301                 }
03302                 else
03303                 {
03304                     $objectIDSQL = '';
03305                 }
03306             }
03307             else
03308             {
03309                 $objectID = (int) $objectID;
03310                 $objectIDSQL = " AND inner_link.to_contentobject_id = $objectID
03311                                  AND inner_link.from_contentobject_version = inner_object.current_version";
03312             }
03313         }
03314         else
03315         {
03316             $outerObjectIDSQL = 'outer_object.id = outer_link.to_contentobject_id';
03317             $objectIDSQL = " AND inner_link.from_contentobject_id = $objectID
03318                              AND inner_link.from_contentobject_version = $version";
03319         }
03320 
03321         $query = "SELECT
03322                     COUNT( outer_object.id ) AS count
03323                   FROM
03324                     ezcontentobject outer_object, ezcontentobject inner_object, ezcontentobject_link outer_link
03325                   INNER JOIN
03326                     ezcontentobject_link inner_link ON outer_link.id = inner_link.id
03327                   WHERE
03328                     $outerObjectIDSQL
03329                     AND outer_object.status = " . eZContentObject::STATUS_PUBLISHED . "
03330                     AND inner_object.id = inner_link.from_contentobject_id
03331                     AND inner_object.status = " . eZContentObject::STATUS_PUBLISHED . "
03332                     AND inner_link.op_code = 0
03333                     $objectIDSQL
03334                     $relationTypeMasking
03335                     $showInvisibleNodesCond";
03336 
03337         $rows = $db->arrayQuery( $query );
03338         return $rows[0]['count'];
03339     }
03340 
03341     /*!
03342      Returns the number of objects to which this object is related.
03343     \param $attributeID :  ( makes sense only when $params['AllRelations'] not set or eZContentObject::RELATION_ATTRIBUTE )
03344                            >0           - return relations made with attribute ID ( "related object(s)" datatype )
03345                            0 or false  ( $params['AllRelations'] is eZContentObject::RELATION_ATTRIBUTE )
03346                                            - return relations made with any attributes
03347                            false        ( $params['AllRelations'] not set )
03348                                            - return ALL relations (deprecated, use "$params['AllRelations'] = true" instead)
03349     \param $params : other parameters from template fetch function :
03350                $params['AllRelations'] - relation type filter :
03351                            true - return ALL relations, including attribute-level
03352                            false    - return object-level relations
03353                            >0       - bit mask of EZ_CONTENT_OBJECT_RELATION_* values
03354     */
03355     function reverseRelatedObjectCount( $version = false, $attributeID = 0, $params = false )
03356     {
03357         return $this->relatedObjectCount( $version, $attributeID, true, $params );
03358     }
03359 
03360     /*!
03361      Returns the related objects.
03362      \note This function is a duplicate of reverseRelatedObjectList(), use that function instead.
03363     */
03364     function contentObjectListRelatingThis( $version = false )
03365     {
03366         return $this->reverseRelatedObjectList( $version );
03367     }
03368 
03369     function publishContentObjectRelations( $version )
03370     {
03371         $objectID = $this->ID;
03372         $currentVersion = $this->CurrentVersion;
03373         $version =(int) $version;
03374         $db = eZDB::instance();
03375         $db->begin();
03376 
03377         $toContentObjectIDs = array();
03378         $relationTypesArray = array();
03379         $publishedRelations = $db->arrayQuery( "SELECT to_contentobject_id, relation_type FROM ezcontentobject_link
03380                                                 WHERE contentclassattribute_id='0'
03381                                                   AND from_contentobject_id='$objectID'
03382                                                   AND from_contentobject_version='$currentVersion'
03383                                                   AND op_code='0'" );
03384 
03385         foreach ( $publishedRelations as $relation )
03386         {
03387             $toContentObjectIDs[] = $relation['to_contentobject_id'];
03388             $relationTypesArray[$relation['to_contentobject_id']] = (int) $relation['relation_type'];
03389         }
03390         $toContentObjectIDs = array_unique( $toContentObjectIDs );
03391 
03392         $addedOrRemovedRelations = $db->arrayQuery( "SELECT to_contentobject_id, op_code, relation_type FROM ezcontentobject_link
03393                                                      WHERE contentclassattribute_id='0'
03394                                                        AND from_contentobject_id='$objectID'
03395                                                        AND from_contentobject_version='$version'
03396                                                        AND op_code!='0'
03397                                                      ORDER BY id ASC" );
03398 
03399         foreach ( $addedOrRemovedRelations as $relation )
03400         {
03401             $relationType = (int) $relation['relation_type'];
03402             if ( !isset( $relationTypesArray[$relation['to_contentobject_id']] ) )
03403             {
03404                 $relationTypesArray[$relation['to_contentobject_id']] = 0;
03405             }
03406             if ( $relation['op_code'] == 1 )
03407             {
03408                 if ( !in_array( $relation['to_contentobject_id'], $toContentObjectIDs ) )
03409                 {
03410                     $toContentObjectIDs[] = $relation['to_contentobject_id'];
03411                 }
03412                 $relationTypesArray[$relation['to_contentobject_id']] |= $relationType;
03413              }
03414             else
03415             {
03416                 $relationTypesArray[$relation['to_contentobject_id']] &= ~$relationType;
03417                 if ( 0 === $relationTypesArray[$relation['to_contentobject_id']] )
03418                 {
03419                     $toContentObjectIDs = array_diff( $toContentObjectIDs, array( $relation['to_contentobject_id'] ) );
03420                 }
03421             }
03422         }
03423 
03424         $db->query( "DELETE FROM ezcontentobject_link
03425                      WHERE contentclassattribute_id='0'
03426                        AND from_contentobject_id='$objectID'
03427                        AND from_contentobject_version='$version'" );
03428 
03429         foreach( $toContentObjectIDs as $toContentObjectID )
03430         {
03431             $db->query( "INSERT INTO ezcontentobject_link( contentclassattribute_id,
03432                                                            from_contentobject_id,
03433                                                            from_contentobject_version,
03434                                                            to_contentobject_id,
03435                                                            op_code,
03436                                                            relation_type )
03437                          VALUES ( '0', '$objectID', '$version', '$toContentObjectID', '0', '{$relationTypesArray[$toContentObjectID]}' )" );
03438         }
03439 
03440         $db->commit();
03441     }
03442 
03443     /*!
03444      Get parent node IDs
03445     */
03446     function parentNodeIDArray()
03447     {
03448         return $this->parentNodes( true, false );
03449     }
03450 
03451     /*!
03452      \param $version No longer in use, published nodes are used instead.
03453      \param $asObject If true it fetches PHP objects, otherwise it fetches IDs.
03454      \return the parnet nodes for the current object.
03455     */
03456     function parentNodes( $version = false, $asObject = true )
03457     {
03458         // We no longer use node-assignment table to find the parents but uses
03459         // the 'published' tree structure.
03460         $retNodes = array();
03461 
03462         $parentNodeIDs = eZContentObjectTreeNode::getParentNodeIdListByContentObjectID( $this->ID );
03463         if ( !$parentNodeIDs )
03464         {
03465           return $retNodes;
03466         }
03467         if ( $asObject )
03468         {
03469             $retNodes = eZContentObjectTreeNode::fetch( $parentNodeIDs );
03470             if ( !is_array( $retNodes ) )
03471             {
03472                 $retNodes = array( $retNodes );
03473             }
03474         }
03475         else
03476         {
03477             $retNodes = $parentNodeIDs;
03478         }
03479 
03480         return $retNodes;
03481     }
03482 
03483     /*!
03484      Creates a new node assignment that will place the object as child of node \a $nodeID.
03485      \return The eZNodeAssignment object it created
03486      \param $parentNodeID The node ID of the parent node
03487      \param $isMain \c true if the created node is the main node of the object
03488      \param $remoteID A string denoting the unique remote ID of the assignment or \c false for no remote id.
03489      \param $sortField
03490      \param $sortOrder
03491      \note The returned assignment will already be stored in the database
03492      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
03493      the calls within a db transaction; thus within db->begin and db->commit.
03494     */
03495     function createNodeAssignment( $parentNodeID, $isMain, $remoteID = false, $sortField = eZContentObjectTreeNode::SORT_FIELD_PUBLISHED, $sortOrder = eZContentObjectTreeNode::SORT_ORDER_DESC )
03496     {
03497         $nodeAssignment = eZNodeAssignment::create( array( 'contentobject_id' => $this->attribute( 'id' ),
03498                                                            'contentobject_version' => $this->attribute( 'current_version' ),
03499                                                            'parent_node' => $parentNodeID,
03500                                                            'is_main' => ( $isMain ? 1 : 0 ),
03501                                                            'sort_field' => $sortField,
03502                                                            'sort_order' => $sortOrder ) );
03503         if ( $remoteID !== false )
03504         {
03505             $nodeAssignment->setAttribute( 'remote_id', $remoteID );
03506         }
03507         $nodeAssignment->store();
03508         return $nodeAssignment;
03509     }
03510 
03511     /*
03512      * Creates object with nodeAssignment from given parent Node, class ID and language code.
03513      */
03514     static function createWithNodeAssignment( $parentNode, $contentClassID, $languageCode, $remoteID = false )
03515     {
03516         $class = eZContentClass::fetch( $contentClassID );
03517         $parentObject = $parentNode->attribute( 'object' );
03518 
03519         // Check if the user has access to create a folder here
03520         if ( $class instanceof eZContentClass and
03521              $parentObject->checkAccess( 'create', $contentClassID, false, false, $languageCode ) == '1' )
03522         {
03523             // Set section of the newly created object to the section's value of it's parent object
03524             $sectionID = $parentObject->attribute( 'section_id' );
03525 
03526             $db = eZDB::instance();
03527             $db->begin();
03528             $contentObject = $class->instantiateIn( $languageCode, false, $sectionID, false, eZContentObjectVersion::STATUS_INTERNAL_DRAFT );
03529             $nodeAssignment = $contentObject->createNodeAssignment( $parentNode->attribute( 'node_id' ),
03530                                                                     true, $remoteID,
03531                                                                     $class->attribute( 'sort_field' ),
03532                                                                     $class->attribute( 'sort_order' ) );
03533             $db->commit();
03534             return $contentObject;
03535         }
03536         return null;
03537     }
03538 
03539 
03540     /*!
03541      Returns the node assignments for the current object.
03542     */
03543     function assignedNodes( $asObject = true )
03544     {
03545         $contentobjectID = $this->attribute( 'id' );
03546         if ( $contentobjectID == null )
03547         {
03548             $retValue = array();
03549             return $retValue;
03550         }
03551         $query = "SELECT ezcontentobject.*,
03552              ezcontentobject_tree.*,
03553              ezcontentclass.serialized_name_list as class_serialized_name_list,
03554              ezcontentclass.identifier as class_identifier,
03555              ezcontentclass.is_container as is_container
03556           FROM   ezcontentobject_tree,
03557              ezcontentobject,
03558              ezcontentclass
03559           WHERE  contentobject_id=$contentobjectID AND
03560              ezcontentobject_tree.contentobject_id=ezcontentobject.id  AND
03561              ezcontentclass.version=0 AND
03562              ezcontentclass.id = ezcontentobject.contentclass_id
03563           ORDER BY path_string";
03564         $db = eZDB::instance();
03565         $nodesListArray = $db->arrayQuery( $query );
03566         if ( $asObject == true )
03567         {
03568             $nodes = eZContentObjectTreeNode::makeObjectsArray( $nodesListArray );
03569             return $nodes;
03570         }
03571         else
03572             return $nodesListArray;
03573     }
03574 
03575     /*!
03576      Returns the main node id for the current object.
03577     */
03578     function mainNodeID()
03579     {
03580         if ( !is_numeric( $this->MainNodeID ) )
03581         {
03582             $mainNodeID = eZContentObjectTreeNode::findMainNode( $this->attribute( 'id' ) );
03583             $this->MainNodeID = $mainNodeID;
03584         }
03585         return $this->MainNodeID;
03586     }
03587 
03588     function mainNode()
03589     {
03590         return eZContentObjectTreeNode::findMainNode( $this->attribute( 'id' ), true );
03591     }
03592 
03593     /*!
03594      Sets the permissions for this object.
03595     */
03596     function setPermissions( $permissionArray )
03597     {
03598         $this->Permissions =& $permissionArray;
03599     }
03600 
03601     /*!
03602      Returns the permission for the current object.
03603     */
03604     function permissions( )
03605     {
03606         return $this->Permissions;
03607     }
03608 
03609     function canEditLanguages()
03610     {
03611         $availableLanguages = $this->availableLanguages();
03612         $languages = array();
03613 
03614         foreach ( eZContentLanguage::prioritizedLanguages() as $language )
03615         {
03616             $languageCode = $language->attribute( 'locale' );
03617             if ( in_array( $languageCode, $availableLanguages ) &&
03618                  $this->canEdit( false, false, false, $languageCode ) )
03619             {
03620                 $languages[] = $language;
03621             }
03622         }
03623 
03624         return $languages;
03625     }
03626 
03627     function canCreateLanguages()
03628     {
03629         $availableLanguages = $this->availableLanguages();
03630         $languages = array();
03631         foreach ( eZContentLanguage::prioritizedLanguages() as $language )
03632         {
03633             $languageCode = $language->attribute( 'locale' );
03634             if ( !in_array( $languageCode, $availableLanguages ) &&
03635                  $this->checkAccess( 'edit', false, false, false, $languageCode ) )
03636             {
03637                 $languages[] = $language;
03638             }
03639         }
03640 
03641         return $languages;
03642     }
03643 
03644     function checkGroupLimitationAccess( $limitationValueList, $userID, $contentObjectID = false )
03645     {
03646         $access = 'denied';
03647 
03648         if ( is_array( $limitationValueList ) && is_numeric( $userID ) )
03649         {
03650             if ( $contentObjectID !== false )
03651             {
03652                 $contentObject = eZContentObject::fetch( $contentObjectID );
03653             }
03654             else
03655             {
03656                 $contentObject = $this;
03657             }
03658 
03659             if ( is_object( $contentObject ) )
03660             {
03661                 // limitation value == 1, means "self group"
03662                 if ( in_array( 1, $limitationValueList ) )
03663                 {
03664                     // no need to check groups if user ownes this object
03665                     $ownerID = $contentObject->attribute( 'owner_id' );
03666                     if ( $ownerID == $userID || $contentObject->attribute( 'id' ) == $userID )
03667                     {
03668                         $access = 'allowed';
03669                     }
03670                     else
03671                     {
03672                         // get parent node ids for 'user' and 'owner'
03673                         $groupList = eZContentObjectTreeNode::getParentNodeIdListByContentObjectID( array( $userID, $ownerID ), true );
03674 
03675                         // find group(s) which is common for 'user' and 'owner'
03676                         $commonGroup = array_intersect( $groupList[$userID], $groupList[$ownerID] );
03677 
03678                         if ( count( $commonGroup ) > 0 )
03679                         {
03680                             // ok, we have at least 1 common group
03681                             $access = 'allowed';
03682                         }
03683                     }
03684                 }
03685             }
03686         }
03687 
03688         return $access;
03689     }
03690 
03691     /*!
03692      Check access for the current object
03693 
03694      \param function name ( edit, read, remove, etc. )
03695      \param original class ID ( used to check access for object creation ), default false
03696      \param parent class id ( used to check access for object creation ), default false
03697      \param return access list instead of access result (optional, default false )
03698 
03699      \return 1 if has access, 0 if not.
03700              If returnAccessList is set to true, access list is returned
03701     */
03702     function checkAccess( $functionName, $originalClassID = false, $parentClassID = false, $returnAccessList = false, $language = false )
03703     {
03704         $classID = $originalClassID;
03705         $user = eZUser::currentUser();
03706         $userID = $user->attribute( 'contentobject_id' );
03707         $origFunctionName = $functionName;
03708 
03709         // Fetch the ID of the language if we get a string with a language code
03710         // e.g. 'eng-GB'
03711         $originalLanguage = $language;
03712         if ( is_string( $language ) && strlen( $language ) > 0 )
03713         {
03714             $language = eZContentLanguage::idByLocale( $language );
03715         }
03716         else
03717         {
03718             $language = false;
03719         }
03720 
03721         // This will be filled in with the available languages of the object
03722         // if a Language check is performed.
03723         $languageList = false;
03724 
03725         // The 'move' function simply reuses 'edit' for generic access
03726         // but adds another top-level check below
03727         // The original function is still available in $origFunctionName
03728         if ( $functionName == 'move' )
03729             $functionName = 'edit';
03730 
03731         $accessResult = $user->hasAccessTo( 'content' , $functionName );
03732         $accessWord = $accessResult['accessWord'];
03733 
03734         /*
03735         // Uncomment this part if 'create' permissions should become implied 'edit'.
03736         // Merges in 'create' policies with 'edit'
03737         if ( $functionName == 'edit' &&
03738              !in_array( $accessWord, array( 'yes', 'no' ) ) )
03739         {
03740             // Add in create policies.
03741             $accessExtraResult = $user->hasAccessTo( 'content', 'create' );
03742             if ( $accessExtraResult['accessWord'] != 'no' )
03743             {
03744                 $accessWord = $accessExtraResult['accessWord'];
03745                 if ( isset( $accessExtraResult['policies'] ) )
03746                 {
03747                     $accessResult['policies'] = array_merge( $accessResult['policies'],
03748                                                              $accessExtraResult['policies'] );
03749                 }
03750                 if ( isset( $accessExtraResult['accessList'] ) )
03751                 {
03752                     $accessResult['accessList'] = array_merge( $accessResult['accessList'],
03753                                                                $accessExtraResult['accessList'] );
03754                 }
03755             }
03756         }
03757         */
03758 
03759         if ( $origFunctionName == 'remove' or
03760              $origFunctionName == 'move' )
03761         {
03762             $mainNode = $this->attribute( 'main_node' );
03763             // We do not allow these actions on objects placed at top-level
03764             // - remove
03765             // - move
03766             if ( $mainNode and $mainNode->attribute( 'parent_node_id' ) <= 1 )
03767             {
03768                 return 0;
03769             }
03770         }
03771 
03772         if ( $classID === false )
03773         {
03774             $classID = $this->attribute( 'contentclass_id' );
03775         }
03776         if ( $accessWord == 'yes' )
03777         {
03778             return 1;
03779         }
03780         else if ( $accessWord == 'no' )
03781         {
03782             if ( $functionName == 'edit' )
03783             {
03784                 // Check if we have 'create' access under the main parent
03785                 if ( $this->attribute( 'current_version' ) == 1 && !$this->attribute( 'status' ) )
03786                 {
03787                     $mainNode = eZNodeAssignment::fetchForObject( $this->attribute( 'id' ), $this->attribute( 'current_version' ) );
03788                     $parentObj = $mainNode[0]->attribute( 'parent_contentobject' );
03789                     $result = $parentObj->checkAccess( 'create', $this->attribute( 'contentclass_id' ),
03790                                                        $parentObj->attribute( 'contentclass_id' ), false, $originalLanguage );
03791                     return $result;
03792                 }
03793                 else
03794                 {
03795                     return 0;
03796                 }
03797             }
03798 
03799             if ( $returnAccessList === false )
03800             {
03801                 return 0;
03802             }
03803             else
03804             {
03805                 return $accessResult['accessList'];
03806             }
03807         }
03808         else
03809         {
03810             $policies  =& $accessResult['policies'];
03811             $access = 'denied';
03812             foreach ( array_keys( $policies ) as $pkey  )
03813             {
03814                 $limitationArray =& $policies[ $pkey ];
03815                 if ( $access == 'allowed' )
03816                 {
03817                     break;
03818                 }
03819 
03820                 $limitationList = array();
03821                 if ( isset( $limitationArray['Subtree' ] ) )
03822                 {
03823                     $checkedSubtree = false;
03824                 }
03825                 else
03826                 {
03827                     $checkedSubtree = true;
03828                     $accessSubtree = false;
03829                 }
03830                 if ( isset( $limitationArray['Node'] ) )
03831                 {
03832                     $checkedNode = false;
03833                 }
03834                 else
03835                 {
03836                     $checkedNode = true;
03837                     $accessNode = false;
03838                 }
03839                 foreach ( array_keys( $limitationArray ) as $key  )
03840                 {
03841                     $access = 'denied';
03842                     switch( $key )
03843                     {
03844                         case 'Class':
03845                         {
03846                             if ( $functionName == 'create' and
03847                                  !$originalClassID )
03848                             {
03849                                 $access = 'allowed';
03850                             }
03851                             else if ( $functionName == 'create' and
03852                                       in_array( $classID, $limitationArray[$key] ) )
03853                             {
03854                                 $access = 'allowed';
03855                             }
03856                             else if ( $functionName != 'create' and
03857                                       in_array( $this->attribute( 'contentclass_id' ), $limitationArray[$key] )  )
03858                             {
03859                                 $access = 'allowed';
03860                             }
03861                             else
03862                             {
03863                                 $access = 'denied';
03864                                 $limitationList = array( 'Limitation' => $key,
03865                                                          'Required' => $limitationArray[$key] );
03866                             }
03867                         } break;
03868 
03869                         case 'ParentClass':
03870                         {
03871 
03872                             if (  in_array( $this->attribute( 'contentclass_id' ), $limitationArray[$key]  ) )
03873                             {
03874                                 $access = 'allowed';
03875                             }
03876                             else
03877                             {
03878                                 $access = 'denied';
03879                                 $limitationList = array( 'Limitation' => $key,
03880                                                          'Required' => $limitationArray[$key] );
03881                             }
03882                         } break;
03883 
03884                         case 'ParentDepth':
03885                         {
03886                             $assignedNodes = $this->attribute( 'assigned_nodes' );
03887                             if ( count( $assignedNodes ) > 0 )
03888                             {
03889                                 foreach ( $assignedNodes as  $assignedNode )
03890                                 {
03891                                     $depth = $assignedNode->attribute( 'depth' );
03892                                     if ( in_array( $depth, $limitationArray[$key] ) )
03893                                     {
03894                                         $access = 'allowed';
03895                                         break;
03896                                     }
03897                                 }
03898                             }
03899 
03900                             if ( $access != 'allowed' )
03901                             {
03902                                 $access = 'denied';
03903                                 $limitationList = array( 'Limitation' => $key,
03904                                                          'Required' => $limitationArray[$key] );
03905                             }
03906                         } break;
03907 
03908                         case 'Section':
03909                         case 'User_Section':
03910                         {
03911                             if ( in_array( $this->attribute( 'section_id' ), $limitationArray[$key]  ) )
03912                             {
03913                                 $access = 'allowed';
03914                             }
03915                             else
03916                             {
03917                                 $access = 'denied';
03918                                 $limitationList = array( 'Limitation' => $key,
03919                                                          'Required' => $limitationArray[$key] );
03920                             }
03921                         } break;
03922 
03923                         case 'Language':
03924                         {
03925                             $languageMask = 0;
03926                             // If we don't have a language list yet we need to fetch it
03927                             // and optionally filter out based on $language.
03928 
03929                             if ( $functionName == 'create' )
03930                             {
03931                                 // If the function is 'create' we do not use the language_mask for matching.
03932                                 if ( $language !== false )
03933                                 {
03934                                     $languageMask = $language;
03935                                 }
03936                                 else
03937                                 {
03938                                     // If the create is used and no language specified then
03939                                     // we need to match against all possible languages (which
03940                                     // is all bits set, ie. -1).
03941                                     $languageMask = -1;
03942                                 }
03943                             }
03944                             else
03945                             {
03946                                 if ( $language !== false )
03947                                 {
03948                                     if ( $languageList === false )
03949                                     {
03950                                         $languageMask = (int)$this->attribute( 'language_mask' );
03951                                         // We are restricting language check to just one language
03952                                         $languageMask &= (int)$language;
03953                                         // If the resulting mask is 0 it means that the user is trying to
03954                                         // edit a language which does not exist, ie. translating.
03955                                         // The mask will then become the language trying to edit.
03956                                         if ( $languageMask == 0 )
03957                                         {
03958                                             $languageMask = $language;
03959                                         }
03960                                     }
03961                                 }
03962                                 else
03963                                 {
03964                                     $languageMask = -1;
03965                                 }
03966                             }
03967                             // Fetch limit mask for limitation list
03968                             $limitMask = eZContentLanguage::maskByLocale( $limitationArray[$key] );
03969                             if ( ( $languageMask & $limitMask ) != 0 )
03970                             {
03971                                 $access = 'allowed';
03972                             }
03973                             else
03974                             {
03975                                 $access = 'denied';
03976                                 $limitationList = array( 'Limitation' => $key,
03977                                                          'Required' => $limitationArray[$key] );
03978                             }
03979                         } break;
03980 
03981                         case 'Owner':
03982                         case 'ParentOwner':
03983                         {
03984                             // if limitation value == 2, anonymous limited to current session.
03985                             if ( in_array( 2, $limitationArray[$key] ) &&
03986                                  $user->isAnonymous() )
03987                             {
03988                                 $createdObjectIDList = eZPreferences::value( 'ObjectCreationIDList' );
03989                                 if ( $createdObjectIDList &&
03990                                      in_array( $this->ID, unserialize( $createdObjectIDList ) ) )
03991                                 {
03992                                     $access = 'allowed';
03993                                 }
03994                             }
03995                             else if ( $this->attribute( 'owner_id' ) == $userID || $this->ID == $userID )
03996                             {
03997                                 $access = 'allowed';
03998                             }
03999                             if ( $access != 'allowed' )
04000                             {
04001                                 $access = 'denied';
04002                                 $limitationList = array ( 'Limitation' => $key, 'Required' => $limitationArray[$key] );
04003                             }
04004                         } break;
04005 
04006                         case 'Group':
04007                         case 'ParentGroup':
04008                         {
04009                             $access = $this->checkGroupLimitationAccess( $limitationArray[$key], $userID );
04010 
04011                             if ( $access != 'allowed' )
04012                             {
04013                                 $access = 'denied';
04014                                 $limitationList = array ( 'Limitation' => $key,
04015                                                           'Required' => $limitationArray[$key] );
04016                             }
04017                         } break;
04018 
04019                         case 'State':
04020                         {
04021                             if ( count( array_intersect( $limitationArray[$key], $this->attribute( 'state_id_array' ) ) ) == 0 )
04022                             {
04023                                 $access = 'denied';
04024                                 $limitationList = array ( 'Limitation' => $key,
04025                                                           'Required' => $limitationArray[$key] );
04026                             }
04027                             else
04028                             {
04029                                 $access = 'allowed';
04030                             }
04031                         } break;
04032 
04033                         case 'Node':
04034                         {
04035                             $accessNode = false;
04036                             $mainNodeID = $this->attribute( 'main_node_id' );
04037                             foreach ( $limitationArray[$key] as $nodeID )
04038                             {
04039                                 $node = eZContentObjectTreeNode::fetch( $nodeID, false, false );
04040                                 $limitationNodeID = $node['main_node_id'];
04041                                 if ( $mainNodeID == $limitationNodeID )
04042                                 {
04043                                     $access = 'allowed';
04044                                     $accessNode = true;
04045                                     break;
04046                                 }
04047                             }
04048                             if ( $access != 'allowed' && $checkedSubtree && !$accessSubtree )
04049                             {
04050                                 $access = 'denied';
04051                                 // ??? TODO: if there is a limitation on Subtree, return two limitations?
04052                                 $limitationList = array( 'Limitation' => $key,
04053                                                          'Required' => $limitationArray[$key] );
04054                             }
04055                             else
04056                             {
04057                                 $access = 'allowed';
04058                             }
04059                             $checkedNode = true;
04060                         } break;
04061 
04062                         case 'Subtree':
04063                         {
04064                             $accessSubtree = false;
04065                             $assignedNodes = $this->attribute( 'assigned_nodes' );
04066                             if ( count( $assignedNodes ) != 0 )
04067                             {
04068                                 foreach (  $assignedNodes as  $assignedNode )
04069                                 {
04070                                     $path = $assignedNode->attribute( 'path_string' );
04071                                     $subtreeArray = $limitationArray[$key];
04072                                     foreach ( $subtreeArray as $subtreeString )
04073                                     {
04074                                         if ( strstr( $path, $subtreeString ) )
04075                                         {
04076                                             $access = 'allowed';
04077                                             $accessSubtree = true;
04078                                             break;
04079                                         }
04080                                     }
04081                                 }
04082                             }
04083                             else
04084                             {
04085                                 $parentNodes = $this->attribute( 'parent_nodes' );
04086                                 if ( count( $parentNodes ) == 0 )
04087                                 {
04088                                     if ( $this->attribute( 'owner_id' ) == $userID || $this->ID == $userID )
04089                                     {
04090                                         $access = 'allowed';
04091                                         $accessSubtree = true;
04092                                     }
04093                                 }
04094                                 else
04095                                 {
04096                                     foreach ( $parentNodes as $parentNode )
04097                                     {
04098                                         $parentNode = eZContentObjectTreeNode::fetch( $parentNode, false, false );
04099                                         $path = $parentNode['path_string'];
04100 
04101                                         $subtreeArray = $limitationArray[$key];
04102                                         foreach ( $subtreeArray as $subtreeString )
04103                                         {
04104                                             if ( strstr( $path, $subtreeString ) )
04105                                             {
04106                                                 $access = 'allowed';
04107                                                 $accessSubtree = true;
04108                                                 break;
04109                                             }
04110                                         }
04111                                     }
04112                                 }
04113                             }
04114                             if ( $access != 'allowed' && $checkedNode && !$accessNode )
04115                             {
04116                                 $access = 'denied';
04117                                 // ??? TODO: if there is a limitation on Node, return two limitations?
04118                                 $limitationList = array( 'Limitation' => $key,
04119                                                          'Required' => $limitationArray[$key] );
04120                             }
04121                             else
04122                             {
04123                                 $access = 'allowed';
04124                             }
04125                             $checkedSubtree = true;
04126                         } break;
04127 
04128                         case 'User_Subtree':
04129                         {
04130                             $assignedNodes = $this->attribute( 'assigned_nodes' );
04131                             if ( count( $assignedNodes ) != 0 )
04132                             {
04133                                 foreach (  $assignedNodes as  $assignedNode )
04134                                 {
04135                                     $path = $assignedNode->attribute( 'path_string' );
04136                                     $subtreeArray = $limitationArray[$key];
04137                                     foreach ( $subtreeArray as $subtreeString )
04138                                     {
04139                                         if ( strstr( $path, $subtreeString ) )
04140                                         {
04141                                             $access = 'allowed';
04142                                         }
04143                                     }
04144                                 }
04145                             }
04146                             else
04147                             {
04148                                 $parentNodes = $this->attribute( 'parent_nodes' );
04149                                 if ( count( $parentNodes ) == 0 )
04150                                 {
04151                                     if ( $this->attribute( 'owner_id' ) == $userID || $this->ID == $userID )
04152                                     {
04153                                         $access = 'allowed';
04154                                     }
04155                                 }
04156                                 else
04157                                 {
04158                                     foreach ( $parentNodes as $parentNode )
04159                                     {
04160                                         $parentNode = eZContentObjectTreeNode::fetch( $parentNode, false, false );
04161                                         $path = $parentNode['path_string'];
04162 
04163                                         $subtreeArray = $limitationArray[$key];
04164                                         foreach ( $subtreeArray as $subtreeString )
04165                                         {
04166                                             if ( strstr( $path, $subtreeString ) )
04167                                             {
04168                                                 $access = 'allowed';
04169                                                 break;
04170                                             }
04171                                         }
04172                                     }
04173                                 }
04174                             }
04175                             if ( $access != 'allowed' )
04176                             {
04177                                 $access = 'denied';
04178                                 $limitationList = array( 'Limitation' => $key,
04179                                                          'Required' => $limitationArray[$key] );
04180                             }
04181                         } break;
04182 
04183                         default:
04184                         {
04185                             if ( strncmp( $key, 'StateGroup_', 11 ) === 0 )
04186                             {
04187                                 if ( count( array_intersect( $limitationArray[$key],
04188                                                              $this->attribute( 'state_id_array' ) ) ) == 0 )
04189                                 {
04190                                     $access = 'denied';
04191                                     $limitationList = array ( 'Limitation' => $key,
04192                                                               'Required' => $limitationArray[$key] );
04193                                 }
04194                                 else
04195                                 {
04196                                     $access = 'allowed';
04197                                 }
04198                             }
04199                         }
04200                     }
04201                     if ( $access == 'denied' )
04202                     {
04203                         break;
04204                     }
04205                 }
04206 
04207                 $policyList[] = array( 'PolicyID' => $pkey,
04208                                        'LimitationList' => $limitationList );
04209             }
04210 
04211             if ( $access == 'denied' )
04212             {
04213                 if ( $functionName == 'edit' )
04214                 {
04215                     // Check if we have 'create' access under the main parent
04216                     if ( $this->attribute( 'current_version' ) == 1 && !$this->attribute( 'status' ) )
04217                     {
04218                         $mainNode = eZNodeAssignment::fetchForObject( $this->attribute( 'id' ), $this->attribute( 'current_version' ) );
04219                         $parentObj = $mainNode[0]->attribute( 'parent_contentobject' );
04220                         $result = $parentObj->checkAccess( 'create', $this->attribute( 'contentclass_id' ),
04221                                                            $parentObj->attribute( 'contentclass_id' ), false, $originalLanguage );
04222                         if ( $result )
04223                         {
04224                             $access = 'allowed';
04225                         }
04226                         return $result;
04227                     }
04228                 }
04229             }
04230 
04231             if ( $access == 'denied' )
04232             {
04233                 if ( $returnAccessList === false )
04234                 {
04235                     return 0;
04236                 }
04237                 else
04238                 {
04239                     return array( 'FunctionRequired' => array ( 'Module' => 'content',
04240                                                                 'Function' => $origFunctionName,
04241                                                                 'ClassID' => $classID,
04242                                                                 'MainNodeID' => $this->attribute( 'main_node_id' ) ),
04243                                   'PolicyList' => $policyList );
04244                 }
04245             }
04246             else
04247             {
04248                 return 1;
04249             }
04250         }
04251     }
04252 
04253     // code-template::create-block: class-list-from-policy, is-object
04254     // code-template::auto-generated:START class-list-from-policy
04255     // This code is automatically generated from templates/classlistfrompolicy.ctpl
04256     // DO NOT EDIT THIS CODE DIRECTLY, CHANGE THE TEMPLATE FILE INSTEAD
04257 
04258     function classListFromPolicy( $policy, $allowedLanguageCodes = false )
04259     {
04260         $canCreateClassIDListPart = array();
04261         $hasClassIDLimitation = false;
04262         $user = eZUser::currentUser();
04263         $userID = $user->attribute( 'contentobject_id' );
04264 
04265         if ( isset( $policy['ParentOwner'] ) )
04266         {
04267             // if limitation value == 2, anonymous limited to current session.
04268             if ( in_array( 2, $policy['ParentOwner'] ) && $user->isAnonymous() )
04269             {
04270                 $createdObjectIDList = eZPreferences::value( 'ObjectCreationIDList' );
04271                 if ( !$createdObjectIDList ||
04272                      !in_array( $this->ID, unserialize( $createdObjectIDList ) ) )
04273                 {
04274                     return array();
04275                 }
04276             }
04277             else if ( $this->attribute( 'owner_id' ) != $userID &&
04278                       $this->ID != $userID )
04279             {
04280                 return array();
04281             }
04282         }
04283 
04284         if ( isset( $policy['ParentGroup'] ) )
04285         {
04286             $access = $this->checkGroupLimitationAccess( $policy['ParentGroup'], $userID );
04287             if ( $access !== 'allowed' )
04288             {
04289                 return array();
04290             }
04291         }
04292 
04293         if ( isset( $policy['Class'] ) )
04294         {
04295             $canCreateClassIDListPart = $policy['Class'];
04296             $hasClassIDLimitation = true;
04297         }
04298 
04299         if ( isset( $policy['User_Section'] ) )
04300         {
04301             if ( !in_array( $this->attribute( 'section_id' ), $policy['User_Section'] ) )
04302             {
04303                 return array();
04304             }
04305         }
04306 
04307         if ( isset( $policy['User_Subtree'] ) )
04308         {
04309             $allowed = false;
04310             $assignedNodes = $this->attribute( 'assigned_nodes' );
04311             foreach ( $assignedNodes as $assignedNode )
04312             {
04313                 $path = $assignedNode->attribute( 'path_string' );
04314                 foreach ( $policy['User_Subtree'] as $subtreeString )
04315                 {
04316                     if ( strstr( $path, $subtreeString ) )
04317                     {
04318                         $allowed = true;
04319                         break;
04320                     }
04321                 }
04322             }
04323             if( !$allowed )
04324             {
04325                 return array();
04326             }
04327         }
04328 
04329         if ( isset( $policy['Section'] ) )
04330         {
04331             if ( !in_array( $this->attribute( 'section_id' ), $policy['Section'] ) )
04332             {
04333                 return array();
04334             }
04335         }
04336 
04337         if ( isset( $policy['ParentClass'] ) )
04338         {
04339             if ( !in_array( $this->attribute( 'contentclass_id' ), $policy['ParentClass']  ) )
04340             {
04341                 return array();
04342             }
04343         }
04344 
04345         if ( isset( $policy['Assigned'] ) )
04346         {
04347             if ( $this->attribute( 'owner_id' ) != $userID )
04348             {
04349                 return array();
04350             }
04351         }
04352 
04353         $allowedNode = false;
04354         if ( isset( $policy['Node'] ) )
04355         {
04356             $allowed = false;
04357             foreach( $policy['Node'] as $nodeID )
04358             {
04359                 $mainNodeID = $this->attribute( 'main_node_id' );
04360                 $node = eZContentObjectTreeNode::fetch( $nodeID, false, false );
04361                 if ( $mainNodeID == $node['main_node_id'] )
04362                 {
04363                     $allowed = true;
04364                     $allowedNode = true;
04365                     break;
04366                 }
04367             }
04368             if ( !$allowed && !isset( $policy['Subtree'] ) )
04369             {
04370                 return array();
04371             }
04372         }
04373 
04374         if ( isset( $policy['Subtree'] ) )
04375         {
04376             $allowed = false;
04377             $assignedNodes = $this->attribute( 'assigned_nodes' );
04378             foreach ( $assignedNodes as $assignedNode )
04379             {
04380                 $path = $assignedNode->attribute( 'path_string' );
04381                 foreach ( $policy['Subtree'] as $subtreeString )
04382                 {
04383                     if ( strstr( $path, $subtreeString ) )
04384                     {
04385                         $allowed = true;
04386                         break;
04387                     }
04388                 }
04389             }
04390             if ( !$allowed && !$allowedNode )
04391             {
04392                 return array();
04393             }
04394         }
04395 
04396         if ( isset( $policy['Language'] ) )
04397         {
04398             if ( $allowedLanguageCodes )
04399             {
04400                 $allowedLanguageCodes = array_intersect( $allowedLanguageCodes, $policy['Language'] );
04401             }
04402             else
04403             {
04404                 $allowedLanguageCodes = $policy['Language'];
04405             }
04406         }
04407 
04408         if ( $hasClassIDLimitation )
04409         {
04410             return array( 'classes' => $canCreateClassIDListPart, 'language_codes' => $allowedLanguageCodes );
04411         }
04412         return array( 'classes' => '*', 'language_codes' => $allowedLanguageCodes );
04413     }
04414 
04415     // This code is automatically generated from templates/classlistfrompolicy.ctpl
04416     // code-template::auto-generated:END class-list-from-policy
04417 
04418     // code-template::create-block: can-instantiate-class-list, group-filter, object-policy-list, name-create, object-creation, object-sql-creation
04419     // code-template::auto-generated:START can-instantiate-class-list
04420     // This code is automatically generated from templates/classcreatelist.ctpl
04421     // DO NOT EDIT THIS CODE DIRECTLY, CHANGE THE TEMPLATE FILE INSTEAD
04422 
04423     /*!
04424      Finds all classes that the current user can create objects from and returns.
04425      It is also possible to filter the list event more with \a $includeFilter and \a $groupList.
04426 
04427      \param $asObject If \c true then it return eZContentClass objects, if not it will
04428                       be an associative array with \c name and \c id keys.
04429      \param $includeFilter If \c true then it will include only from class groups defined in
04430                            \a $groupList, if not it will exclude those groups.
04431      \param $groupList An array with class group IDs that should be used in filtering, use
04432                        \c false if you do not wish to filter at all.
04433      \param $fetchID A unique name for the current fetch, this must be supplied when filtering is
04434                      used if you want caching to work.
04435     */
04436     function canCreateClassList( $asObject = false, $includeFilter = true, $groupList = false, $fetchID = false )
04437     {
04438         $ini = eZINI::instance();
04439         $groupArray = array();
04440         $languageCodeList = eZContentLanguage::fetchLocaleList();
04441         $allowedLanguages = array( '*' => array() );
04442 
04443         $user = eZUser::currentUser();
04444         $accessResult = $user->hasAccessTo( 'content' , 'create' );
04445         $accessWord = $accessResult['accessWord'];
04446 
04447         $classIDArray = array();
04448         $classList = array();
04449         $fetchAll = false;
04450         if ( $accessWord == 'yes' )
04451         {
04452             $fetchAll = true;
04453             $allowedLanguages['*'] = $languageCodeList;
04454         }
04455         else if ( $accessWord == 'no' )
04456         {
04457             // Cannot create any objects, return empty list.
04458             return $classList;
04459         }
04460         else
04461         {
04462             $policies = $accessResult['policies'];
04463             foreach ( $policies as $policyKey => $policy )
04464             {
04465                 $policyArray = $this->classListFromPolicy( $policy, $languageCodeList );
04466                 if ( empty( $policyArray ) )
04467                 {
04468                     continue;
04469                 }
04470                 $classIDArrayPart = $policyArray['classes'];
04471                 $languageCodeArrayPart = $policyArray['language_codes'];
04472                 // No class limitation for this policy AND no previous limitation(s)
04473                 if ( $classIDArrayPart == '*' && empty( $classIDArray ) )
04474                 {
04475                     $fetchAll = true;
04476                     $allowedLanguages['*'] = array_unique( array_merge( $allowedLanguages['*'], $languageCodeArrayPart ) );
04477                 }
04478                 else if ( is_array( $classIDArrayPart ) )
04479                 {
04480                     $fetchAll = false;
04481                     foreach( $classIDArrayPart as $class )
04482                     {
04483                         if ( isset( $allowedLanguages[$class] ) )
04484                         {
04485                             $allowedLanguages[$class] = array_unique( array_merge( $allowedLanguages[$class], $languageCodeArrayPart ) );
04486                         }
04487                         else
04488                         {
04489                             $allowedLanguages[$class] = $languageCodeArrayPart;
04490                         }
04491                     }
04492                     $classIDArray = array_merge( $classIDArray, array_diff( $classIDArrayPart, $classIDArray ) );
04493                 }
04494             }
04495         }
04496 
04497         $db = eZDB::instance();
04498 
04499         $filterTableSQL = '';
04500         $filterSQL = '';
04501         // Create extra SQL statements for the class group filters.
04502         if ( is_array( $groupList ) )
04503         {
04504             if ( count( $groupList ) == 0 )
04505             {
04506                 return $classList;
04507             }
04508 
04509             $filterTableSQL = ', ezcontentclass_classgroup ccg';
04510             $filterSQL = ( " AND" .
04511                            "      cc.id = ccg.contentclass_id AND" .
04512                            "      " );
04513             $filterSQL .= $db->generateSQLINStatement( $groupList, 'ccg.group_id', !$includeFilter, true, 'int' );
04514         }
04515 
04516         $classNameFilter = eZContentClassName::sqlFilter( 'cc' );
04517 
04518         if ( $fetchAll )
04519         {
04520             // If $asObject is true we fetch all fields in class
04521             $fields = $asObject ? "cc.*, $classNameFilter[nameField]" : "cc.id, $classNameFilter[nameField]";
04522             $rows = $db->arrayQuery( "SELECT DISTINCT $fields " .
04523                                      "FROM ezcontentclass cc$filterTableSQL, $classNameFilter[from] " .
04524                                      "WHERE cc.version = " . eZContentClass::VERSION_STATUS_DEFINED . " $filterSQL AND $classNameFilter[where] " .
04525                                      "ORDER BY $classNameFilter[nameField] ASC" );
04526             $classList = eZPersistentObject::handleRows( $rows, 'eZContentClass', $asObject );
04527         }
04528         else
04529         {
04530             // If the constrained class list is empty we are not allowed to create any class
04531             if ( count( $classIDArray ) == 0 )
04532             {
04533                 return $classList;
04534             }
04535 
04536             $classIDCondition = $db->generateSQLINStatement( $classIDArray, 'cc.id' );
04537             // If $asObject is true we fetch all fields in class
04538             $fields = $asObject ? "cc.*, $classNameFilter[nameField]" : "cc.id, $classNameFilter[nameField]";
04539             $rows = $db->arrayQuery( "SELECT DISTINCT $fields " .
04540                                      "FROM ezcontentclass cc$filterTableSQL, $classNameFilter[from] " .
04541                                      "WHERE $classIDCondition AND" .
04542                                      "      cc.version = " . eZContentClass::VERSION_STATUS_DEFINED . " $filterSQL AND $classNameFilter[where] " .
04543                                      "ORDER BY $classNameFilter[nameField] ASC" );
04544             $classList = eZPersistentObject::handleRows( $rows, 'eZContentClass', $asObject );
04545         }
04546 
04547         if ( $asObject )
04548         {
04549             foreach ( $classList as $key => $class )
04550             {
04551                 $id = $class->attribute( 'id' );
04552                 if ( isset( $allowedLanguages[$id] ) )
04553                 {
04554                     $languageCodes = array_unique( array_merge( $allowedLanguages['*'], $allowedLanguages[$id] ) );
04555                 }
04556                 else
04557                 {
04558                     $languageCodes = $allowedLanguages['*'];
04559                 }
04560                 $classList[$key]->setCanInstantiateLanguages( $languageCodes );
04561             }
04562         }
04563 
04564         eZDebugSetting::writeDebug( 'kernel-content-class', $classList, "class list fetched from db" );
04565         return $classList;
04566     }
04567 
04568     // This code is automatically generated from templates/classcreatelist.ctpl
04569     // code-template::auto-generated:END can-instantiate-class-list
04570 
04571     /*!
04572      Get accesslist for specified function
04573 
04574      \param function
04575 
04576      \return AccessList
04577     */
04578     function accessList( $function )
04579     {
04580         switch( $function )
04581         {
04582             case 'read':
04583             {
04584                 return $this->checkAccess( 'read', false, false, true );
04585             } break;
04586 
04587             case 'edit':
04588             {
04589                 return $this->checkAccess( 'edit', false, false, true );
04590             } break;
04591         }
04592         return 0;
04593     }
04594 
04595     /*!
04596      \return \c true if the current user can read this content object.
04597     */
04598     function canRead( )
04599     {
04600         if ( !isset( $this->Permissions["can_read"] ) )
04601         {
04602             $this->Permissions["can_read"] = $this->checkAccess( 'read' );
04603         }
04604         return ( $this->Permissions["can_read"] == 1 );
04605     }
04606 
04607     /*!
04608      \return \c true if the current user can create a pdf of this content object.
04609     */
04610     function canPdf( )
04611     {
04612         if ( !isset( $this->Permissions["can_pdf"] ) )
04613         {
04614             $this->Permissions["can_pdf"] = $this->checkAccess( 'pdf' );
04615         }
04616         return ( $this->Permissions["can_pdf"] == 1 );
04617     }
04618 
04619     /*!
04620      \return \c true if the node can be viewed as embeded object by the current user.
04621      \sa checkAccess().
04622     */
04623     function canViewEmbed( )
04624     {
04625         if ( !isset( $this->Permissions["can_view_embed"] ) )
04626         {
04627             $this->Permissions["can_view_embed"] = $this->checkAccess( 'view_embed' );
04628         }
04629         return ( $this->Permissions["can_view_embed"] == 1 );
04630     }
04631 
04632     /*!
04633      \return \c true if the current user can diff this content object.
04634     */
04635     function canDiff( )
04636     {
04637         if ( !isset( $this->Permissions["can_diff"] ) )
04638         {
04639             $this->Permissions["can_diff"] = $this->checkAccess( 'diff' );
04640         }
04641         return ( $this->Permissions["can_diff"] == 1 );
04642     }
04643 
04644     function canCreate( )
04645     {
04646         if ( !isset( $this->Permissions["can_create"] ) )
04647         {
04648             $this->Permissions["can_create"] = $this->checkAccess( 'create' );
04649         }
04650         return ( $this->Permissions["can_create"] == 1 );
04651     }
04652 
04653     function canEdit( $originalClassID = false, $parentClassID = false, $returnAccessList = false, $language = false )
04654     {
04655         $isCalledClean = ( func_num_args() == 0 );
04656         if ( isset( $this->Permissions["can_edit"] ) && $isCalledClean )
04657         {
04658             $canEdit = $this->Permissions["can_edit"];
04659         }
04660         else
04661         {
04662             $canEdit = $this->checkAccess( 'edit', $originalClassID, $parentClassID, $returnAccessList, $language );
04663             if ( $canEdit != 1 )
04664             {
04665                  $user = eZUser::currentUser();
04666                  if ( $user->attribute( 'contentobject_id' ) === $this->attribute( 'id' ) )
04667                  {
04668                      $access = $user->hasAccessTo( 'user', 'selfedit' );
04669                      if ( $access['accessWord'] == 'yes' )
04670                      {
04671                          $canEdit = 1;
04672                      }
04673                  }
04674             }
04675 
04676             if ( $isCalledClean )
04677             {
04678                 $this->Permissions["can_edit"] = $canEdit;
04679             }
04680         }
04681         return ( $canEdit == 1 );
04682     }
04683 
04684     function canTranslate( )
04685     {
04686         if ( !isset( $this->Permissions["can_translate"] ) )
04687         {
04688             $this->Permissions["can_translate"] = $this->checkAccess( 'translate' );
04689             if ( $this->Permissions["can_translate"] != 1 )
04690             {
04691                  $user = eZUser::currentUser();
04692                  if ( $user->id() == $this->attribute( 'id' ) )
04693                  {
04694                      $access = $user->hasAccessTo( 'user', 'selfedit' );
04695                      if ( $access['accessWord'] == 'yes' )
04696                      {
04697                          $this->Permissions["can_translate"] = 1;
04698                      }
04699                  }
04700             }
04701         }
04702         return ( $this->Permissions["can_translate"] == 1 );
04703     }
04704 
04705     function canRemove( )
04706     {
04707 
04708         if ( !isset( $this->Permissions["can_remove"] ) )
04709         {
04710             $this->Permissions["can_remove"] = $this->checkAccess( 'remove' );
04711         }
04712         return ( $this->Permissions["can_remove"] == 1 );
04713     }
04714 
04715     /*!
04716      Check if the object can be moved. (actually checks 'edit' and 'remove' permissions)
04717      \return \c true if the object can be moved by the current user.
04718      \sa checkAccess().
04719      \deprecated The function canMove() is preferred since its naming is clearer.
04720     */
04721     function canMove( )
04722     {
04723         return $this->canMoveFrom();
04724     }
04725 
04726     /*!
04727      Check if the object can be moved. (actually checks 'edit' and 'remove' permissions)
04728      \return \c true if the object can be moved by the current user.
04729      \sa checkAccess().
04730     */
04731     function canMoveFrom( )
04732     {
04733 
04734         if ( !isset( $this->Permissions['can_move_from'] ) )
04735         {
04736             $this->Permissions['can_move_from'] = $this->checkAccess( 'edit' ) && $this->checkAccess( 'remove' );
04737         }
04738         return ( $this->Permissions['can_move_from'] == 1 );
04739     }
04740 
04741     /*!
04742      \return The name of the class which this object was created from.
04743 
04744      \note The object will cache the class name information so multiple calls will be fast.
04745     */
04746     function className()
04747     {
04748         if ( !is_numeric( $this->ClassID ) )
04749         {
04750             return null;
04751         }
04752 
04753         if ( $this->ClassName !== false )
04754             return $this->ClassName;
04755 
04756         $db = eZDB::instance();
04757         $id = (int)$this->ClassID;
04758         $sql = "SELECT serialized_name_list FROM ezcontentclass WHERE id=$id and version=0";
04759         $rows = $db->arrayQuery( $sql );
04760         if ( count( $rows ) > 0 )
04761         {
04762             $this->ClassName = eZContentClass::nameFromSerializedString( $rows[0]['serialized_name_list'] );
04763         }
04764         return $this->ClassName;
04765     }
04766 
04767     /*!
04768      Returns an array of the content actions which can be performed on
04769      the current object.
04770     */
04771     function contentActionList()
04772     {
04773         $version = $this->attribute( 'current_version' );
04774         $language = $this->initialLanguageCode();
04775         if ( !isset( $this->ContentObjectAttributeArray[$version][$language] ) )
04776         {
04777             $attributeList = $this->contentObjectAttributes();
04778             $this->ContentObjectAttributeArray[$version][$language] =& $attributeList;
04779         }
04780         else
04781             $attributeList = $this->ContentObjectAttributeArray[$version][$language];
04782 
04783         // Fetch content actions if not already fetched
04784         if ( $this->ContentActionList === false )
04785         {
04786             foreach ( $attributeList as $attribute )
04787             {
04788                 $contentActionList = $attribute->contentActionList();
04789                 if ( is_array( $contentActionList ) && !empty( $contentActionList ) )
04790                 {
04791                     foreach ( $contentActionList as $action )
04792                     {
04793                         if ( !$this->hasContentAction( $action['action'] ) )
04794                         {
04795                             $this->ContentActionList[] = $action;
04796                         }
04797                     }
04798                 }
04799             }
04800         }
04801         return $this->ContentActionList;
04802     }
04803 
04804     /*!
04805      \return true if the content action is in the content action list
04806     */
04807     function hasContentAction( $name )
04808     {
04809         $return = false;
04810         if ( is_array ( $this->ContentActionList ) )
04811         {
04812             foreach ( $this->ContentActionList as $action )
04813             {
04814                 if ( $action['action'] == $name )
04815                 {
04816                     $return = true;
04817                 }
04818             }
04819         }
04820         return $return;
04821     }
04822 
04823     /*!
04824      \return the languages the object has been translated into/exists in.
04825 
04826      Returns an array with the language codes.
04827 
04828      It uses the attribute \c avail_lang as the source for the language list.
04829      */
04830     function availableLanguages()
04831     {
04832         $languages = array();
04833         $languageObjects = $this->languages();
04834 
04835         foreach ( $languageObjects as $languageObject )
04836         {
04837             $languages[] = $languageObject->attribute( 'locale' );
04838         }
04839 
04840         return $languages;
04841     }
04842 
04843     function availableLanguagesJsArray()
04844     {
04845         return eZContentLanguage::jsArrayByMask( $this->LanguageMask );
04846     }
04847 
04848     function languages()
04849     {
04850         return isset( $this->LanguageMask ) ?
04851             eZContentLanguage::prioritizedLanguagesByMask( $this->LanguageMask ) :
04852             array();
04853     }
04854 
04855     function allLanguages()
04856     {
04857         $languages = isset( $this->LanguageMask ) ? eZContentLanguage::languagesByMask( $this->LanguageMask ) : array();
04858         return $languages;
04859     }
04860 
04861     static function defaultLanguage()
04862     {
04863         if ( ! isset( $GLOBALS['eZContentObjectDefaultLanguage'] ) )
04864         {
04865             $defaultLanguage = false;
04866             $language = eZContentLanguage::topPriorityLanguage();
04867             if ( $language )
04868             {
04869                 $defaultLanguage = $language->attribute( 'locale' );
04870             }
04871             else
04872             {
04873                 $ini = eZINI::instance();
04874                 if ( $ini->hasVariable( 'RegionalSettings', 'ContentObjectLocale' ) )
04875                 {
04876                     $defaultLanguage = $ini->variable( 'RegionalSettings', 'ContentObjectLocale' );
04877                     eZContentLanguage::fetchByLocale( $defaultLanguage, true );
04878                 }
04879             }
04880             $GLOBALS['eZContentObjectDefaultLanguage'] = $defaultLanguage;
04881         }
04882 
04883         return $GLOBALS['eZContentObjectDefaultLanguage'];
04884     }
04885 
04886     /*!
04887      \static
04888      Set default language. Checks if default language is valid.
04889 
04890      \param default language.
04891      \note Deprecated.
04892     */
04893     static function setDefaultLanguage( $lang )
04894     {
04895         return false;
04896     }
04897 
04898     /*!
04899 
04900     */
04901     function setClassName( $name )
04902     {
04903         $this->ClassName = $name;
04904     }
04905 
04906     /*!
04907      \returns an array with locale strings, these strings represents the languages which content objects are allowed to be translated into.
04908      \note the setting ContentSettings/TranslationList in site.ini determines the array.
04909      \sa translationList
04910     */
04911     static function translationStringList()
04912     {
04913         $translationList = array();
04914         $languageList = eZContentLanguage::fetchList();
04915 
04916         foreach ( $languageList as $language )
04917         {
04918             $translationList[] = $language->attribute( 'locale' );
04919         }
04920 
04921         return $translationList;
04922     }
04923 
04924     /*!
04925      \returns an array with locale objects, these objects represents the languages the content objects are allowed to be translated into.
04926      \note the setting ContentSettings/TranslationList in site.ini determines the array.
04927      \sa translationStringList
04928     */
04929     static function translationList()
04930     {
04931         $translationList = array();
04932         $languageList = eZContentLanguage::fetchList();
04933 
04934         foreach ( $languageList as $language )
04935         {
04936             $translationList[] = $language->localeObject();
04937         }
04938 
04939         return $translationList;
04940     }
04941 
04942     /*!
04943      Returns the attributes for the content object version \a $version and content object \a $contentObjectID.
04944      \a $language defines the language to fetch.
04945      \sa attributes
04946     */
04947     function fetchClassAttributes( $version = 0, $asObject = true )
04948     {
04949         return eZContentClassAttribute::fetchListByClassID( $this->attribute( 'contentclass_id' ), $version, $asObject );
04950     }
04951 
04952     /*!
04953      \private
04954      Maps input lange to another one if defined in $options['language_map'].
04955      If it cannot map it returns the original language.
04956      \returns string
04957      */
04958     static function mapLanguage( $language, $options )
04959     {
04960         if ( isset( $options['language_map'][$language] ) )
04961         {
04962             return $options['language_map'][$language];
04963         }
04964         return $language;
04965     }
04966 
04967     /*!
04968      \static
04969      Unserialize xml structure. Create object from xml input.
04970 
04971      \param package
04972      \param XML DOM Node
04973      \param parent node object.
04974      \param Options
04975      \param owner ID, override owner ID, null to use XML owner id (optional)
04976 
04977      \returns created object, false if could not create object/xml invalid
04978      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
04979      the calls within a db transaction; thus within db->begin and db->commit.
04980     */
04981     static function unserialize( $package, $domNode, &$options, $ownerID = false, $handlerType = 'ezcontentobject' )
04982     {
04983         if ( $domNode->localName != 'object' )
04984         {
04985             $retValue = false;
04986             return $retValue;
04987         }
04988 
04989         $initialLanguage = eZContentObject::mapLanguage( $domNode->getAttribute( 'initial_language' ), $options );
04990         if( $initialLanguage === 'skip' )
04991         {
04992             $retValue = true;
04993             return $retValue;
04994         }
04995 
04996         $sectionID = $domNode->getAttributeNS( 'http://ez.no/ezobject', 'section_id' );
04997         if ( $ownerID === false )
04998         {
04999             $ownerID = $domNode->getAttributeNS( 'http://ez.no/ezobject', 'owner_id' );
05000         }
05001         $remoteID = $domNode->getAttribute( 'remote_id' );
05002         $name = $domNode->getAttribute( 'name' );
05003         $classRemoteID = $domNode->getAttribute( 'class_remote_id' );
05004         $classIdentifier = $domNode->getAttributeNS( 'http://ez.no/ezobject', 'class_identifier' );
05005         $alwaysAvailable = ( $domNode->getAttributeNS( 'http://ez.no/ezobject', 'always_available' ) == '1' );
05006 
05007         $contentClass = eZContentClass::fetchByRemoteID( $classRemoteID );
05008         if ( !$contentClass )
05009         {
05010             $contentClass = eZContentClass::fetchByIdentifier( $classIdentifier );
05011         }
05012 
05013         if ( !$contentClass )
05014         {
05015             $options['error'] = array( 'error_code' => self::PACKAGE_ERROR_NO_CLASS,
05016                                        'element_id' => $remoteID,
05017                                        'description' => "Can't install object '$name': Unable to fetch class with remoteID: $classRemoteID." );
05018             $retValue = false;
05019             return $retValue;
05020         }
05021 
05022         $versionListNode = $domNode->getElementsByTagName( 'version-list' )->item( 0 );
05023 
05024         $importedLanguages = array();
05025         foreach( $versionListNode->getElementsByTagName( 'version' ) as $versionDOMNode )
05026         {
05027             foreach ( $versionDOMNode->getElementsByTagName( 'object-translation' ) as $versionDOMNodeChild )
05028             {
05029                 $importedLanguage = eZContentObject::mapLanguage( $versionDOMNodeChild->getAttribute( 'language' ), $options );
05030                 $language = eZContentLanguage::fetchByLocale( $importedLanguage );
05031                 // Check if the language is allowed in this setup.
05032                 if ( $language )
05033                 {
05034                     $hasTranslation = true;
05035                 }
05036                 else
05037                 {
05038                     if ( $importedLanguage == 'skip' )
05039                         continue;
05040 
05041                     // if there is no needed translation in system then add it
05042                     $locale = eZLocale::instance( $importedLanguage );
05043                     $translationName = $locale->internationalLanguageName();
05044                     $translationLocale = $locale->localeCode();
05045 
05046                     if ( $locale->isValid() )
05047                     {
05048                         eZContentLanguage::addLanguage( $locale->localeCode(), $locale->internationalLanguageName() );
05049                         $hasTranslation = true;
05050                     }
05051                     else
05052                         $hasTranslation = false;
05053                 }
05054                 if ( $hasTranslation )
05055                 {
05056                     $importedLanguages[] = $importedLanguage;
05057                     $importedLanguages = array_unique( $importedLanguages );
05058                 }
05059             }
05060         }
05061 
05062         // If object exists we return a error.
05063         // Minimum install element is an object now.
05064 
05065         $contentObject = eZContentObject::fetchByRemoteID( $remoteID );
05066         // Figure out initial language
05067         if ( !$initialLanguage ||
05068              !in_array( $initialLanguage, $importedLanguages ) )
05069         {
05070             $initialLanguage = false;
05071             foreach ( eZContentLanguage::prioritizedLanguages() as $language )
05072             {
05073                 if ( in_array( $language->attribute( 'locale' ), $importedLanguages ) )
05074                 {
05075                     $initialLanguage = $language->attribute( 'locale' );
05076                     break;
05077                 }
05078             }
05079         }
05080         if ( !$contentObject )
05081         {
05082             $firstVersion = true;
05083             $contentObject = $contentClass->instantiateIn( $initialLanguage, $ownerID, $sectionID );
05084         }
05085         else
05086         {
05087             $firstVersion = false;
05088             $description = "Object '$name' already exists.";
05089 
05090             $choosenAction = eZPackageHandler::errorChoosenAction( self::PACKAGE_ERROR_EXISTS,
05091                                                                    $options, $description, $handlerType, false );
05092 
05093             switch( $choosenAction )
05094             {
05095                 case eZPackage::NON_INTERACTIVE:
05096                 case self::PACKAGE_UPDATE:
05097                 {
05098                     // Keep existing contentobject.
05099                 } break;
05100 
05101                 case self::PACKAGE_REPLACE:
05102                 {
05103                     eZContentObjectOperations::remove( $contentObject->attribute( 'id' ) );
05104 
05105                     unset( $contentObject );
05106                     $contentObject = $contentClass->instantiateIn( $initialLanguage, $ownerID, $sectionID );
05107                     $firstVersion = true;
05108                 } break;
05109 
05110                 case self::PACKAGE_SKIP:
05111                 {
05112                     $retValue = true;
05113                     return $retValue;
05114                 } break;
05115 
05116                 case self::PACKAGE_NEW:
05117                 {
05118                     $contentObject->setAttribute( 'remote_id', eZRemoteIdUtility::generate( 'object' ) );
05119                     $contentObject->store();
05120                     unset( $contentObject );
05121                     $contentObject = $contentClass->instantiate( $ownerID, $sectionID );
05122                     $firstVersion = true;
05123                 } break;
05124 
05125                 default:
05126                 {
05127                     $options['error'] = array( 'error_code' => self::PACKAGE_ERROR_EXISTS,
05128                                                'element_id' => $remoteID,
05129                                                'description' => $description,
05130                                                'actions' => array( self::PACKAGE_REPLACE => ezpI18n::tr( 'kernel/classes', 'Replace existing object' ),
05131                                                                    self::PACKAGE_SKIP    => ezpI18n::tr( 'kernel/classes', 'Skip object' ),
05132                                                                    self::PACKAGE_NEW     => ezpI18n::tr( 'kernel/classes', 'Keep existing and create a new one' ),
05133                                                                    self::PACKAGE_UPDATE  => ezpI18n::tr( 'kernel/classes', 'Update existing object' ) ) );
05134                     $retValue = false;
05135                     return $retValue;
05136                 } break;
05137             }
05138         }
05139 
05140         $db = eZDB::instance();
05141         $db->begin();
05142 
05143         if ( $alwaysAvailable )
05144         {
05145             // Make sure always available bit is set.
05146             $contentObject->setAttribute( 'language_mask', (int)$contentObject->attribute( 'language_mask' ) | 1 );
05147         }
05148 
05149         $contentObject->setAttribute( 'section_id', $sectionID );
05150         $contentObject->store();
05151         $activeVersion = false;
05152         $lastVersion = false;
05153         $versionListActiveVersion = $versionListNode->getAttribute( 'active_version' );
05154 
05155         $contentObject->setAttribute( 'remote_id', $remoteID );
05156         $contentObject->setAttribute( 'contentclass_id', $contentClass->attribute( 'id' ) );
05157         $contentObject->store();
05158 
05159         $sectionObject = eZSection::fetch( $sectionID );
05160         if ( $sectionObject instanceof eZSection )
05161         {
05162             $updateWithParentSection = false;
05163         }
05164         else
05165         {
05166             $updateWithParentSection = true;
05167         }
05168 
05169         $options['language_array'] = $importedLanguages;
05170         $versionList = array();
05171         foreach( $versionListNode->getElementsByTagName( 'version' ) as $versionDOMNode )
05172         {
05173             unset( $nodeList );
05174             $nodeList = array();
05175             $contentObjectVersion = eZContentObjectVersion::unserialize( $versionDOMNode,
05176                                                                          $contentObject,
05177                                                                          $ownerID,
05178                                                                          $sectionID,
05179                                                                          $versionListActiveVersion,
05180                                                                          $firstVersion,
05181                                                                          $nodeList,
05182                                                                          $options,
05183                                                                          $package );
05184 
05185             if ( !$contentObjectVersion )
05186             {
05187                 $db->commit();
05188 
05189                 $retValue = false;
05190                 return $retValue;
05191             }
05192 
05193             $versionStatus = $versionDOMNode->getAttributeNS( 'http://ez.no/ezobject', 'status' );
05194             $versionList[$versionDOMNode->getAttributeNS( 'http://ez.no/ezobject', 'version' )] = array( 'node_list' => $nodeList,
05195                                                                                                          'status' =>    $versionStatus );
05196             unset( $versionStatus );
05197 
05198             $firstVersion = false;
05199             $lastVersion = $contentObjectVersion->attribute( 'version' );
05200             if ( $versionDOMNode->getAttribute( 'version' ) == $versionListActiveVersion )
05201             {
05202                 $activeVersion = $contentObjectVersion->attribute( 'version' );
05203             }
05204             eZNodeAssignment::setNewMainAssignment( $contentObject->attribute( 'id' ), $lastVersion );
05205 
05206             eZOperationHandler::execute( 'content', 'publish', array( 'object_id' => $contentObject->attribute( 'id' ),
05207                                                                       'version' => $lastVersion ) );
05208 
05209             $mainNodeInfo = null;
05210             foreach ( $nodeList as $nodeInfo )
05211             {
05212                 if ( $nodeInfo['is_main'] )
05213                 {
05214                     $mainNodeInfo =& $nodeInfo;
05215                     break;
05216                 }
05217             }
05218             if ( $mainNodeInfo )
05219             {
05220                 $existingMainNode = eZContentObjectTreeNode::fetchByRemoteID( $mainNodeInfo['parent_remote_id'], false );
05221                 if ( $existingMainNode )
05222                 {
05223                     eZContentObjectTreeNode::updateMainNodeID( $existingMainNode['node_id'],
05224                                                                $mainNodeInfo['contentobject_id'],
05225                                                                $mainNodeInfo['contentobject_version'],
05226                                                                $mainNodeInfo['parent_node'],
05227                                                                $updateWithParentSection );
05228                 }
05229             }
05230             unset( $mainNodeInfo );
05231             // Refresh $contentObject from DB.
05232             $contentObject = eZContentObject::fetch( $contentObject->attribute( 'id' ) );
05233         }
05234         if ( !$activeVersion )
05235         {
05236             $activeVersion = $lastVersion;
05237         }
05238 
05239         /*
05240         $contentObject->setAttribute( 'current_version', $activeVersion );
05241         */
05242         $contentObject->setAttribute( 'name', $name );
05243         if ( isset( $options['use_dates_from_package'] ) && $options['use_dates_from_package'] )
05244         {
05245             $contentObject->setAttribute( 'published', eZDateUtils::textToDate( $domNode->getAttributeNS( 'http://ez.no/ezobject', 'published' ) ) );
05246             $contentObject->setAttribute( 'modified', eZDateUtils::textToDate( $domNode->getAttributeNS( 'http://ez.no/ezobject', 'modified' ) ) );
05247         }
05248         $contentObject->store();
05249 
05250         $versions   = $contentObject->versions();
05251         $objectName = $contentObject->name();
05252         $objectID   = $contentObject->attribute( 'id' );
05253         foreach ( $versions as $version )
05254         {
05255             $versionNum       = $version->attribute( 'version' );
05256             $oldVersionStatus = $version->attribute( 'status' );
05257             $newVersionStatus = isset( $versionList[$versionNum] ) ? $versionList[$versionNum]['status'] : null;
05258 
05259             // set the correct status for non-published versions
05260             if ( isset( $newVersionStatus ) && $oldVersionStatus != $newVersionStatus && $newVersionStatus != eZContentObjectVersion::STATUS_PUBLISHED )
05261             {
05262                 $version->setAttribute( 'status', $newVersionStatus );
05263                 $version->store( array( 'status' ) );
05264             }
05265 
05266             // when translation does not have object name set then we copy object name from the current object version
05267             $translations = $version->translations( false );
05268             if ( !$translations )
05269                 continue;
05270             foreach ( $translations as $translation )
05271             {
05272                 if ( ! $contentObject->name( $versionNum, $translation ) )
05273                 {
05274                     eZDebug::writeNotice( "Setting name '$objectName' for version ($versionNum) of the content object ($objectID) in language($translation)" );
05275                     $contentObject->setName( $objectName, $versionNum, $translation );
05276                 }
05277             }
05278         }
05279 
05280         foreach ( $versionList[$versionListActiveVersion]['node_list'] as $nodeInfo )
05281         {
05282             unset( $parentNode );
05283             $parentNode = eZContentObjectTreeNode::fetchNode( $contentObject->attribute( 'id' ),
05284                                                                $nodeInfo['parent_node'] );
05285             if ( is_object( $parentNode ) )
05286             {
05287                 $parentNode->setAttribute( 'priority', $nodeInfo['priority'] );
05288                 $parentNode->store( array( 'priority' ) );
05289             }
05290         }
05291 
05292         $db->commit();
05293 
05294         return $contentObject;
05295     }
05296 
05297     /*!
05298       Performs additional unserialization actions that need to be performed when all
05299       objects contained in the package are already installed. (maintain objects' cross-relations)
05300     */
05301 
05302     function postUnserialize( $package )
05303     {
05304         foreach( $this->versions() as $version )
05305         {
05306             $version->postUnserialize( $package );
05307         }
05308     }
05309 
05310     /*!
05311      \return a DOM structure of the content object and it's attributes.
05312 
05313      \param package
05314      \param Content object version, true for current version, false for all, else array containing specific versions.
05315      \param package options ( optianal )
05316      \param array of allowed nodes ( optional )
05317      \param array of top nodes in current package export (optional )
05318 
05319      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
05320      the calls within a db transaction; thus within db->begin and db->commit.
05321     */
05322     function serialize( $package, $specificVersion = false, $options = false, $contentNodeIDArray = false, $topNodeIDArray = false )
05323     {
05324         if ( $options &&
05325              $options['node_assignment'] == 'main' )
05326         {
05327             if ( !isset( $contentNodeIDArray[$this->attribute( 'main_node_id' )] ) )
05328             {
05329                 return false;
05330             }
05331         }
05332 
05333         $dom = new DomDocument();
05334         $objectNode = $dom->createElementNS( 'http://ez.no/ezobject', 'ezremote:object' );
05335 
05336         $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:id', $this->ID );
05337         $objectNode->setAttribute( 'name', $this->Name );
05338         $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:section_id', $this->SectionID );
05339         $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:owner_id', $this->OwnerID );
05340         $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:class_id', $this->ClassID );
05341         $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:published', eZDateUtils::rfc1123Date( $this->attribute( 'published' ) ) );
05342         $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:modified', eZDateUtils::rfc1123Date( $this->attribute( 'modified' ) ) );
05343         if ( !$this->attribute( 'remote_id' ) )
05344         {
05345             $this->setAttribute( 'remote_id', eZRemoteIdUtility::generate( 'object' ) );
05346             $this->store();
05347         }
05348         $objectNode->setAttribute( 'remote_id', $this->attribute( 'remote_id' ) );
05349         $contentClass = $this->attribute( 'content_class' );
05350         $objectNode->setAttribute( 'class_remote_id', $contentClass->attribute( 'remote_id' ) );
05351         $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:class_identifier', $contentClass->attribute( 'identifier' ) );
05352         $alwaysAvailableText = '0';
05353         if ( (int)$this->attribute( 'language_mask' ) & 1 )
05354         {
05355             $alwaysAvailableText = '1';
05356         }
05357         $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:always_available', $alwaysAvailableText );
05358 
05359         $versions = array();
05360         $oneLanguagePerVersion = false;
05361         if ( $specificVersion === false )
05362         {
05363             $versions = $this->versions();
05364             // Since we are exporting all versions it should only contain
05365             // one language per version
05366             //$oneLanguagePerVersion = true; // uncomment to get one language per version
05367         }
05368         else if ( $specificVersion === true )
05369         {
05370             $versions[] = $this->currentVersion();
05371         }
05372         else
05373         {
05374             $versions[] = $this->version( $specificVersion );
05375             // Since we are exporting a specific version it should only contain
05376             // one language per version?
05377             $oneLanguagePerVersion = true;
05378         }
05379 
05380         $this->fetchClassAttributes();
05381 
05382         $exportedLanguages = array();
05383 
05384         $versionsNode = $dom->createElementNS( 'http://ez.no/object/', 'ezobject:version-list' );
05385         $versionsNode->setAttribute( 'active_version', $this->CurrentVersion );
05386         foreach ( $versions as $version )
05387         {
05388             if ( !$version )
05389             {
05390                 continue;
05391             }
05392             $options['only_initial_language'] = $oneLanguagePerVersion;
05393             $versionNode = $version->serialize( $package, $options, $contentNodeIDArray, $topNodeIDArray );
05394             if ( $versionNode )
05395             {
05396                 $importedVersionNode = $dom->importNode( $versionNode, true );
05397                 $versionsNode->appendChild( $importedVersionNode );
05398                 foreach ( $versionNode->getElementsByTagName( 'object-translation' ) as $versionNodeChild )
05399                 {
05400                     $exportedLanguage = $versionNodeChild->getAttribute( 'language' );
05401                     $exportedLanguages[] = $exportedLanguage;
05402                     $exportedLanguages = array_unique( $exportedLanguages );
05403                 }
05404             }
05405             unset( $versionNode );
05406             unset( $versionNode );
05407         }
05408         $initialLanguageCode = $this->attribute( 'initial_language_code' );
05409         if ( in_array( $initialLanguageCode, $exportedLanguages ) )
05410         {
05411             $objectNode->setAttribute( 'initial_language', $initialLanguageCode );
05412         }
05413         $objectNode->appendChild( $versionsNode );
05414         return $objectNode;
05415     }
05416 
05417     /*!
05418      \return a structure with information required for caching.
05419     */
05420     function cacheInfo( $Params )
05421     {
05422         $contentCacheInfo =& $GLOBALS['eZContentCacheInfo'];
05423         if ( !isset( $contentCacheInfo ) )
05424         {
05425             $user = eZUser::currentUser();
05426             $language = false;
05427             if ( isset( $Params['Language'] ) )
05428             {
05429                 $language = $Params['Language'];
05430             }
05431             $roleList = $user->roleIDList();
05432             $discountList = eZUserDiscountRule::fetchIDListByUserID( $user->attribute( 'contentobject_id' ) );
05433             $contentCacheInfo = array( 'language' => $language,
05434                                        'role_list' => $roleList,
05435                                        'discount_list' => $discountList );
05436         }
05437         return $contentCacheInfo;
05438     }
05439 
05440     /*!
05441      Sets all view cache files to be expired
05442     */
05443     static function expireAllViewCache()
05444     {
05445         eZExpiryHandler::registerShutdownFunction();
05446         $handler = eZExpiryHandler::instance();
05447         $handler->setTimestamp( 'content-view-cache', time() );
05448         $handler->store();
05449     }
05450 
05451     /*!
05452      Sets all content cache files to be expired. Both view cache and cache blocks are expired.
05453      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
05454      the calls within a db transaction; thus within db->begin and db->commit.
05455     */
05456     static function expireAllCache()
05457     {
05458         eZExpiryHandler::registerShutdownFunction();
05459         $handler = eZExpiryHandler::instance();
05460         $handler->setTimestamp( 'content-view-cache', time() );
05461         $handler->setTimestamp( 'template-block-cache', time() );
05462         $handler->setTimestamp( 'content-tree-menu', time() );
05463         $handler->store();
05464     }
05465 
05466     /*!
05467      Expires all template block cache. This should be expired anytime any content
05468      is published/modified or removed.
05469      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
05470      the calls within a db transaction; thus within db->begin and db->commit.
05471     */
05472     static function expireTemplateBlockCache()
05473     {
05474         eZExpiryHandler::registerShutdownFunction();
05475         $handler = eZExpiryHandler::instance();
05476         $handler->setTimestamp( 'template-block-cache', time() );
05477         $handler->store();
05478     }
05479 
05480     /*!
05481     \static
05482      Callse eZContentObject::xpireTemplateBlockCache() unless template caching is disabled.
05483      */
05484     static function expireTemplateBlockCacheIfNeeded()
05485     {
05486         $ini = eZINI::instance();
05487         if ( $ini->variable( 'TemplateSettings', 'TemplateCache' ) == 'enabled' )
05488             eZContentObject::expireTemplateBlockCache();
05489     }
05490 
05491     /*!
05492      Sets all complex viewmode content cache files to be expired.
05493      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
05494      the calls within a db transaction; thus within db->begin and db->commit.
05495     */
05496     static function expireComplexViewModeCache()
05497     {
05498         eZExpiryHandler::registerShutdownFunction();
05499         $handler = eZExpiryHandler::instance();
05500         $handler->setTimestamp( 'content-complex-viewmode-cache', time() );
05501         $handler->store();
05502     }
05503 
05504     /*!
05505      \return if the content cache timestamp \a $timestamp is expired.
05506     */
05507     static function isCacheExpired( $timestamp )
05508     {
05509         eZExpiryHandler::registerShutdownFunction();
05510         $handler = eZExpiryHandler::instance();
05511         if ( !$handler->hasTimestamp( 'content-view-cache' ) )
05512             return false;
05513         $expiryTime = $handler->timestamp( 'content-view-cache' );
05514         if ( $expiryTime > $timestamp )
05515             return true;
05516         return false;
05517     }
05518 
05519     /*!
05520      \return true if the viewmode is a complex viewmode.
05521     */
05522     static function isComplexViewMode( $viewMode )
05523     {
05524         $ini = eZINI::instance();
05525         $viewModes = $ini->variableArray( 'ContentSettings', 'ComplexDisplayViewModes' );
05526         return in_array( $viewMode, $viewModes );
05527     }
05528 
05529     /*!
05530      \return true if the viewmode is a complex viewmode and the viewmode timestamp is expired.
05531     */
05532     static function isComplexViewModeCacheExpired( $viewMode, $timestamp )
05533     {
05534         if ( !eZContentObject::isComplexViewMode( $viewMode ) )
05535             return false;
05536         eZExpiryHandler::registerShutdownFunction();
05537         $handler = eZExpiryHandler::instance();
05538         if ( !$handler->hasTimestamp( 'content-complex-viewmode-cache' ) )
05539             return false;
05540         $expiryTime = $handler->timestamp( 'content-complex-viewmode-cache' );
05541         if ( $expiryTime > $timestamp )
05542             return true;
05543         return false;
05544     }
05545 
05546     /*!
05547      Returns a list of all the authors for this object. The returned value is an
05548      array of eZ user objects.
05549     */
05550     function authorArray()
05551     {
05552         $db = eZDB::instance();
05553 
05554         $userArray = $db->arrayQuery( "SELECT DISTINCT ezuser.contentobject_id, ezuser.login, ezuser.email, ezuser.password_hash, ezuser.password_hash_type
05555                                        FROM ezcontentobject_version, ezuser where ezcontentobject_version.contentobject_id='$this->ID'
05556                                        AND ezcontentobject_version.creator_id=ezuser.contentobject_id" );
05557 
05558         $return = array();
05559 
05560         foreach ( $userArray as $userRow )
05561         {
05562             $return[] = new eZUser( $userRow );
05563         }
05564         return $return;
05565     }
05566 
05567     /*!
05568      \return the number of objects of the given class is created by the given user.
05569     */
05570     static function fetchObjectCountByUserID( $classID, $userID, $status = false )
05571     {
05572         $count = 0;
05573         if ( is_numeric( $classID ) and is_numeric( $userID ) )
05574         {
05575             $db         = eZDB::instance();
05576             $classID    = (int) $classID;
05577             $userID     = (int) $userID;
05578             $statusSQL  = $status !== false ? ( ' AND status=' . (int) $status ) : '';
05579             $countArray = $db->arrayQuery( "SELECT count(*) AS count FROM ezcontentobject WHERE contentclass_id=$classID AND owner_id=$userID$statusSQL" );
05580             $count = $countArray[0]['count'];
05581         }
05582         return $count;
05583     }
05584 
05585      /*!
05586      \static
05587       \deprecated This method is left here only for backward compatibility.
05588                   Use eZContentObjectVersion::removeVersions() method instead.
05589      */
05590      static function removeVersions( $versionStatus = false )
05591      {
05592          eZContentObjectVersion::removeVersions( $versionStatus );
05593      }
05594 
05595     /*!
05596      Sets the object's name to $newName: tries to find attributes that are in 'object pattern name'
05597      and updates them.
05598      \return \c true if object's name was changed, otherwise \c false.
05599     */
05600     function rename( $newName )
05601     {
05602         // get 'object name pattern'
05603         $objectNamePattern = '';
05604         $contentClass = $this->contentClass();
05605         if ( is_object( $contentClass ) )
05606             $objectNamePattern = $contentClass->ContentObjectName;
05607 
05608         if ( $objectNamePattern == '' )
05609             return false;
05610 
05611         // get parts of object's name pattern( like <attr1|attr2>, <attr3> )
05612         $objectNamePatternPartsPattern = '/<([^>]+)>/U';
05613         preg_match_all( $objectNamePatternPartsPattern, $objectNamePattern, $objectNamePatternParts );
05614 
05615         if( count( $objectNamePatternParts ) === 0 || count( $objectNamePatternParts[1] ) == 0 )
05616             return false;
05617 
05618         $objectNamePatternParts = $objectNamePatternParts[1];
05619 
05620         // replace all <...> with (.*)
05621         $newNamePattern = preg_replace( $objectNamePatternPartsPattern, '(.*)', $objectNamePattern );
05622         // add terminators
05623         $newNamePattern = '/' . $newNamePattern . '/';
05624 
05625         // find parts of $newName
05626         preg_match_all( $newNamePattern, $newName, $newNameParts );
05627 
05628         // looks ok, we can create new version of object
05629         $contentObjectVersion = $this->createNewVersion();
05630         // get contentObjectAttributes
05631         $dataMap = $contentObjectVersion->attribute( 'data_map' );
05632         if ( count( $dataMap ) === 0 )
05633             return false;
05634 
05635         // assign parts of $newName to the object's attributes.
05636         $pos = 0;
05637         while( $pos < count( $objectNamePatternParts ) )
05638         {
05639             $attributes = $objectNamePatternParts[$pos];
05640 
05641             // if we have something like <attr1|attr2> then
05642             // 'attr1' will be updated only.
05643             $attributes = explode( '|', $attributes );
05644             $attribute = $attributes[0];
05645 
05646             $newNamePart = $newNameParts[$pos+1];
05647             if ( count( $newNamePart ) === 0 )
05648             {
05649                 if( $pos === 0 )
05650                 {
05651                     // whole $newName goes into the first attribute
05652                     $attributeValue = $newName;
05653                 }
05654                 else
05655                 {
05656                     // all other attibutes will be set to ''
05657                     $attributeValue = '';
05658                 }
05659             }
05660             else
05661             {
05662                 $attributeValue = $newNamePart[0];
05663             }
05664 
05665             $contentAttribute =& $dataMap[$attribute];
05666             $dataType = $contentAttribute->dataType();
05667             if( is_object( $dataType ) && $dataType->isSimpleStringInsertionSupported() )
05668             {
05669                 $result = '';
05670                 $dataType->insertSimpleString( $this, $contentObjectVersion, false, $contentAttribute, $attributeValue, $result );
05671                 $contentAttribute->store();
05672             }
05673 
05674             ++$pos;
05675         }
05676 
05677         $operationResult = eZOperationHandler::execute( 'content', 'publish', array( 'object_id' => $this->attribute( 'id' ),
05678                                                                                      'version' => $contentObjectVersion->attribute( 'version') ) );
05679         return ($operationResult != null ? true : false);
05680     }
05681 
05682     function removeTranslation( $languageID )
05683     {
05684         $language = eZContentLanguage::fetch( $languageID );
05685 
05686         if ( !$language )
05687         {
05688             return false;
05689         }
05690 
05691         // check permissions for editing
05692         if ( !$this->checkAccess( 'edit', false, false, false, $languageID ) )
05693         {
05694             return false;
05695         }
05696 
05697         // check if it is not the initial language
05698         $objectInitialLanguageID = $this->attribute( 'initial_language_id' );
05699         if ( $objectInitialLanguageID == $languageID )
05700         {
05701             return false;
05702         }
05703 
05704         // change language_mask of the object
05705         $languageMask = (int) $this->attribute( 'language_mask' );
05706         $languageMask = (int) $languageMask & ~ (int) $languageID;
05707         $this->setAttribute( 'language_mask', $languageMask );
05708 
05709         $db = eZDB::instance();
05710         $db->begin();
05711 
05712         $this->store();
05713 
05714         $objectID = $this->ID;
05715 
05716         // If the current version has initial_language_id $languageID, change it to the initial_language_id of the object.
05717         $currentVersion = $this->currentVersion();
05718         if ( $currentVersion->attribute( 'initial_language_id' ) == $languageID )
05719         {
05720             $currentVersion->setAttribute( 'initial_language_id', $objectInitialLanguageID );
05721             $currentVersion->store();
05722         }
05723 
05724         // Remove all versions which had the language as its initial ID. Because of previous checks, it is sure we will not remove the published version.
05725         $versionsToRemove = $this->versions( true, array( 'conditions' => array( 'initial_language_id' => $languageID ) ) );
05726         foreach ( $versionsToRemove as $version )
05727         {
05728             $version->removeThis();
05729         }
05730 
05731         $altLanguageID = $languageID++;
05732 
05733         // Remove all attributes in the language
05734         $attributes = $db->arrayQuery( "SELECT * FROM ezcontentobject_attribute
05735                                         WHERE contentobject_id='$objectID'
05736                                           AND ( language_id='$languageID' OR language_id='$altLanguageID' )" );
05737         foreach ( $attributes as $attribute )
05738         {
05739             $attributeObject = new eZContentObjectAttribute( $attribute );
05740             $attributeObject->remove( $attributeObject->attribute( 'id' ), $attributeObject->attribute( 'version' ) );
05741             unset( $attributeObject );
05742         }
05743 
05744         // Remove all names in the language
05745         $db->query( "DELETE FROM ezcontentobject_name
05746                      WHERE contentobject_id='$objectID'
05747                        AND ( language_id='$languageID' OR language_id='$altLanguageID' )" );
05748 
05749         // Update masks of the objects
05750         $mask = eZContentLanguage::maskForRealLanguages() - (int) $languageID;
05751 
05752         if ( $db->databaseName() == 'oracle' )
05753         {
05754             $db->query( "UPDATE ezcontentobject_version SET language_mask = bitand( language_mask, $mask )
05755                          WHERE contentobject_id='$objectID'" );
05756         }
05757         else
05758         {
05759             $db->query( "UPDATE ezcontentobject_version SET language_mask = language_mask & $mask
05760                          WHERE contentobject_id='$objectID'" );
05761         }
05762 
05763         $urlElementfilter = new eZURLAliasQuery();
05764         $urlElementfilter->type = 'name';
05765         // We want all languages present here, so we are turning off
05766         // language filtering
05767         $urlElementfilter->languages = false;
05768         $urlElementfilter->limit = false;
05769 
05770         $nodes = $this->assignedNodes();
05771 
05772         foreach ( $nodes as $node )
05773         {
05774             $parent = null;
05775             $textMD5 = null;
05776 
05777             $urlElementfilter->actions = array( 'eznode:' . $node->attribute( 'node_id' ) );
05778             $urlElementfilter->prepare();
05779             $urlElements = $urlElementfilter->fetchAll();
05780 
05781             foreach ($urlElements as $url )
05782             {
05783                 if ( $url->attribute( 'lang_mask' ) === (int)$languageID or
05784                      $url->attribute( 'lang_mask') === (int)$altLanguageID )
05785                 {
05786                     $parent = $url->attribute( 'parent');
05787                     $textMD5 = $url->attribute( 'text_md5' );
05788                     break;
05789                 }
05790             }
05791 
05792             if ( $parent !== null and $textMD5 !== null )
05793                 eZURLAliasML::removeSingleEntry( $parent, $textMD5, $language );
05794         }
05795         $db->commit();
05796 
05797         return true;
05798     }
05799 
05800     function isAlwaysAvailable()
05801     {
05802         return ( $this->attribute( 'language_mask' ) & 1 );
05803     }
05804 
05805     function setAlwaysAvailableLanguageID( $languageID, $version = false )
05806     {
05807         $db = eZDB::instance();
05808         $db->begin();
05809 
05810         if ( $version == false )
05811         {
05812             $version = $this->currentVersion();
05813             if ( $languageID )
05814             {
05815                 $this->setAttribute( 'language_mask', (int)$this->attribute( 'language_mask' ) | 1 );
05816             }
05817             else
05818             {
05819                 $this->setAttribute( 'language_mask', (int)$this->attribute( 'language_mask' ) & ~1 );
05820             }
05821             $this->store();
05822         }
05823 
05824         $objectID = $this->attribute( 'id' );
05825         $versionID = $version->attribute( 'version' );
05826 
05827         // reset 'always available' flag
05828         $sql = "UPDATE ezcontentobject_name SET language_id=";
05829         if ( $db->databaseName() == 'oracle' )
05830         {
05831             $sql .= "bitand( language_id, -2 )";
05832         }
05833         else
05834         {
05835             $sql .= "language_id & ~1";
05836         }
05837         $sql .= " WHERE contentobject_id = '$objectID' AND content_version = '$versionID'";
05838         $db->query( $sql );
05839 
05840         if ( $languageID != false )
05841         {
05842             $newLanguageID = $languageID | 1;
05843             $sql = "UPDATE ezcontentobject_name
05844                     SET language_id='$newLanguageID'
05845                     WHERE language_id='$languageID' AND contentobject_id = '$objectID' AND content_version = '$versionID'";
05846             $db->query( $sql );
05847         }
05848 
05849         $version->setAlwaysAvailableLanguageID( $languageID );
05850 
05851         // Update url alias for all locations
05852         $nodeRows = eZContentObjectTreeNode::fetchByContentObjectID( $objectID, false );
05853         $actions = array();
05854         foreach ( $nodeRows as $nodeRow )
05855         {
05856             $nodeID = (int)$nodeRow['node_id'];
05857             $actions[] = array( 'eznode', $nodeID );
05858         }
05859         eZURLAliasML::setLangMaskAlwaysAvailable( $languageID, $actions, null );
05860 
05861         $db->commit();
05862     }
05863 
05864     function allowedAssignSectionList()
05865     {
05866         $currentUser = eZUser::currentUser();
05867         $sectionIDList = $currentUser->canAssignToObjectSectionList( $this );
05868 
05869         $sectionList = array();
05870         if ( in_array( '*', $sectionIDList ) )
05871         {
05872             $sectionList = eZSection::fetchList( false );
05873         }
05874         else
05875         {
05876             $sectionIDList[] = $this->attribute( 'section_id' );
05877             $sectionList = eZSection::fetchFilteredList( array( 'id' => array( $sectionIDList ) ), false, false, false );
05878         }
05879         return $sectionList;
05880     }
05881 
05882     /**
05883      * Gets a list of states a user is allowed to put the content object in.
05884      *
05885      * @return array the IDs of all states we are allowed to set
05886      * @param eZUser $user the user to check the policies of, when omitted the currently logged in user will be used
05887      */
05888     function allowedAssignStateIDList( eZUser $user = null )
05889     {
05890         if ( !$user instanceof eZUser )
05891         {
05892             $user = eZUser::currentUser();
05893         }
05894 
05895         $access = $user->hasAccessTo( 'state', 'assign' );
05896 
05897         $db = eZDB::instance();
05898         $sql = 'SELECT ezcobj_state.id
05899                 FROM   ezcobj_state, ezcobj_state_group
05900                 WHERE  ezcobj_state.group_id = ezcobj_state_group.id
05901                     AND ezcobj_state_group.identifier NOT LIKE \'ez%\'';
05902         if ( $access['accessWord'] == 'yes' )
05903         {
05904             $allowedStateIDList = $db->arrayQuery( $sql, array( 'column' => 'id' ) );
05905         }
05906         else if ( $access['accessWord'] == 'limited' )
05907         {
05908             $userID = $user->attribute( 'contentobject_id' );
05909             $classID = $this->attribute( 'contentclass_id' );
05910             $ownerID = $this->attribute( 'owner_id' );
05911             $sectionID = $this->attribute( 'section_id' );
05912             $stateIDArray = $this->attribute( 'state_id_array' );
05913 
05914             $allowedStateIDList = array();
05915             foreach ( $access['policies'] as $policy )
05916             {
05917                 foreach ( $policy as $ident => $values )
05918                 {
05919                     $allowed = true;
05920 
05921                     switch ( $ident )
05922                     {
05923                         case 'Class':
05924                         {
05925                             $allowed = in_array( $classID, $values );
05926                         } break;
05927 
05928                         case 'Owner':
05929                         {
05930                             $allowed = in_array( 1, $values ) and $userID != $ownerID;
05931                         } break;
05932 
05933                         case 'Group':
05934                         {
05935                             $allowed = $this->checkGroupLimitationAccess( $values, $userID ) === 'allowed';
05936                         } break;
05937 
05938                         case 'Section':
05939                         case 'User_Section':
05940                         {
05941                             $allowed = in_array( $sectionID, $values );
05942                         } break;
05943 
05944                         default:
05945                         {
05946                             if ( strncmp( $ident, 'StateGroup_', 11 ) === 0 )
05947                             {
05948                                 $allowed = count( array_intersect( $values, $stateIDArray ) ) > 0;
05949                             }
05950                         }
05951                     }
05952 
05953                     if ( !$allowed )
05954                     {
05955                         continue 2;
05956                     }
05957                 }
05958 
05959                 if ( isset( $policy['NewState'] ) and count( $policy['NewState'] ) > 0 )
05960                 {
05961                     $allowedStateIDList = array_merge( $allowedStateIDList, $policy['NewState'] );
05962                 }
05963                 else
05964                 {
05965                     $allowedStateIDList = $db->arrayQuery( $sql, array( 'column' => 'id' ) );
05966                     break;
05967                 }
05968             }
05969 
05970             $allowedStateIDList = array_merge( $allowedStateIDList, $stateIDArray );
05971         }
05972         else
05973         {
05974             $stateIDArray = $this->attribute( 'state_id_array' );
05975             $allowedStateIDList = $stateIDArray;
05976         }
05977 
05978         $allowedStateIDList = array_unique( $allowedStateIDList );
05979 
05980         return $allowedStateIDList;
05981     }
05982 
05983     function allowedAssignStateList( eZUser $user = null )
05984     {
05985         $allowedStateIDList = $this->allowedAssignStateIDList( $user );
05986 
05987         // retrieve state groups, and for each state group the allowed states (including the current state)
05988         $groups = eZContentObjectStateGroup::fetchByOffset( false, false );
05989 
05990         $allowedAssignList = array();
05991         foreach ( $groups as $group )
05992         {
05993             // we do not return any internal state
05994             // all internal states are prepended with the string : "ez_"
05995             if( strpos( $group->attribute( 'identifier' ), 'ez' ) === 0 )
05996                 continue;
05997 
05998             $states = array();
05999             $groupStates = $group->attribute( 'states' );
06000 
06001             $currentStateIDArray = $this->attribute( 'state_id_array' );
06002 
06003             $current = false;
06004             foreach ( $groupStates as $groupState )
06005             {
06006                 $stateID = $groupState->attribute( 'id' );
06007                 if ( in_array( $stateID, $allowedStateIDList ) )
06008                 {
06009                     $states[] = $groupState;
06010                 }
06011 
06012                 if ( in_array( $stateID, $currentStateIDArray ) )
06013                 {
06014                     $current = $groupState;
06015                 }
06016             }
06017 
06018             $allowedAssignList[] = array( 'group' => $group, 'states' => $states, 'current' => $current );
06019         }
06020         return $allowedAssignList;
06021     }
06022 
06023     /**
06024      * Gets the current states of the content object.
06025      *
06026      * Uses a member variable that caches the result.
06027      *
06028      * @return array an associative array with state group id => state id pairs
06029      * @param boolean $refreshCache if the cache in the member variable needs to be refreshed
06030      */
06031     function stateIDArray( $refreshCache = false )
06032     {
06033         if ( !$this->ID )
06034             return array();
06035 
06036         if ( $refreshCache || !is_array( $this->StateIDArray ) )
06037         {
06038             $this->StateIDArray = array();
06039             $contentObjectID = $this->ID;
06040             eZDebug::accumulatorStart( 'state_id_array', 'states' );
06041             $sql = "SELECT contentobject_state_id, group_id FROM ezcobj_state_link, ezcobj_state
06042                     WHERE ezcobj_state.id=ezcobj_state_link.contentobject_state_id AND
06043                           contentobject_id=$contentObjectID";
06044             $db = eZDB::instance();
06045             $rows = $db->arrayQuery( $sql );
06046             foreach ( $rows as $row )
06047             {
06048                 $this->StateIDArray[$row['group_id']] = $row['contentobject_state_id'];
06049             }
06050             eZDebug::accumulatorStop( 'state_id_array' );
06051         }
06052 
06053         return $this->StateIDArray;
06054     }
06055 
06056     function stateIdentifierArray()
06057     {
06058         if ( !$this->ID )
06059             return array();
06060 
06061         eZDebug::accumulatorStart( 'state_identifier_array', 'states' );
06062         $return = array();
06063         $sql = "SELECT l.contentobject_state_id, s.identifier AS state_identifier, g.identifier AS state_group_identifier
06064                 FROM ezcobj_state_link l, ezcobj_state s, ezcobj_state_group g
06065                 WHERE l.contentobject_id={$this->ID} AND
06066                       s.id=l.contentobject_state_id AND
06067                       g.id=s.group_id";
06068         $db = eZDB::instance();
06069         $rows = $db->arrayQuery( $sql );
06070         foreach ( $rows as $row )
06071         {
06072             $return[] = $row['state_group_identifier'] . '/' . $row['state_identifier'];
06073         }
06074         eZDebug::accumulatorStop( 'state_identifier_array' );
06075         return $return;
06076     }
06077 
06078     /**
06079      * Sets the state of a content object.
06080      *
06081      * Changes are stored immediately in the database, does not require a store() of the content object.
06082      * Should only be called on instances of eZContentObject that have a ID (that were stored already before).
06083      *
06084      * @param eZContentObjectState $state
06085      * @return boolean true when the state was set, false if the state equals the current state
06086      */
06087     function assignState( eZContentObjectState $state )
06088     {
06089         $groupID = $state->attribute( 'group_id' );
06090         $stateID = $state->attribute( 'id' );
06091         $contentObjectID = $this->ID;
06092 
06093         $currentStateIDArray = $this->stateIDArray( true );
06094         $currentStateID = $currentStateIDArray[$groupID];
06095 
06096         if ( $currentStateID == $stateID )
06097         {
06098             return false;
06099         }
06100 
06101         $sql = "UPDATE ezcobj_state_link
06102                 SET contentobject_state_id=$stateID
06103                 WHERE contentobject_state_id=$currentStateID AND
06104                       contentobject_id=$contentObjectID";
06105         eZDB::instance()->query( $sql );
06106 
06107         $this->StateIDArray[$groupID] = $stateID;
06108 
06109         return true;
06110     }
06111 
06112     /**
06113      * Sets the default states of a content object.
06114      *
06115      * This function is called upon instantiating a content object with {@link eZContentClass::instantiate()}, so
06116      * should normally not be called by any other code.
06117      */
06118     function assignDefaultStates()
06119     {
06120         $db = eZDB::instance();
06121         $db->begin();
06122         $defaultStates = eZContentObjectState::defaults();
06123         $contentObjectID = $this->ID;
06124         foreach ( $defaultStates as $state )
06125         {
06126             $stateID = $state->attribute( 'id' );
06127             $db->query( "INSERT INTO ezcobj_state_link (contentobject_state_id, contentobject_id)
06128                          VALUES($stateID, $contentObjectID)" );
06129         }
06130         $db->commit();
06131     }
06132 
06133     /**
06134      * Restores attributes for current content object when it's being restored from trash
06135      */
06136     public function restoreObjectAttributes()
06137     {
06138         $db = eZDB::instance();
06139         $db->begin();
06140 
06141         foreach ( $this->allContentObjectAttributes( $this->attribute( "id" ) ) as $contentObjectAttribute )
06142         {
06143             $datatype = $contentObjectAttribute->dataType();
06144             if ( !$datatype instanceof eZDataType )
06145             {
06146                 eZDebug::writeError( "Attribute #{$contentObjectAttribute->attribute( "id" )} from contentobject #{$this->attribute( "id" )} isn't valid", __METHOD__ );
06147                 continue;
06148             }
06149 
06150             $datatype->restoreTrashedObjectAttribute( $contentObjectAttribute );
06151         }
06152 
06153         $db->commit();
06154     }
06155 
06156     public $ID;
06157     public $Name;
06158 
06159     /// Stores the current language
06160     public $CurrentLanguage;
06161 
06162     /// Stores the current class name
06163     public $ClassName;
06164 
06165     /// Cached class identifier
06166     public $ClassIdentifier;
06167 
06168     /// Contains the datamap for content object attributes
06169     public $DataMap = array();
06170 
06171     /// Contains an array of the content object actions for the current object
06172     public $ContentActionList = false;
06173 
06174     /// Contains a cached version of the content object attributes for the given version and language
06175     public $ContentObjectAttributes = array();
06176 
06177     /// Contains the main node id for this object
06178     public $MainNodeID = false;
06179 
06180     /// Contains the arrays of relatedobject id by fetching input for this object
06181     public $InputRelationList = array();
06182 
06183     /**
06184      * Cache for the state ID array
06185      *
06186      * @var array
06187      * @see eZContentObject::stateIDArray()
06188      */
06189     private $StateIDArray = false;
06190 }
06191 
06192 ?>