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