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