eZ Publish  [4.0]
ezcontentobjectversion.php
Go to the documentation of this file.
00001 <?php
00002 //
00003 // Definition of eZContentObjectVersion class
00004 //
00005 // Created on: <18-Apr-2002 10:05:34 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 eZContentObjectVersion ezcontentobjectversion.php
00033   \brief The class eZContentObjectVersion handles different versions of an content object
00034   \ingourp eZKernel
00035 
00036 */
00037 
00038 //include_once( "lib/ezdb/classes/ezdb.php" );
00039 //include_once( "kernel/classes/ezpersistentobject.php" );
00040 //include_once( "kernel/classes/eznodeassignment.php" );
00041 //include_once( "kernel/classes/ezcontentobject.php" );
00042 
00043 //include_once( "kernel/classes/ezcontentobjectattribute.php" );
00044 //include_once( "kernel/classes/ezcontentobjecttranslation.php" );
00045 //include_once( "kernel/classes/datatypes/ezuser/ezuser.php" );
00046 //include_once( "kernel/classes/ezcontentclassattribute.php" );
00047 
00048 class eZContentObjectVersion extends eZPersistentObject
00049 {
00050     const STATUS_DRAFT = 0;
00051     const STATUS_PUBLISHED = 1;
00052     const STATUS_PENDING = 2;
00053     const STATUS_ARCHIVED = 3;
00054     const STATUS_REJECTED = 4;
00055     const STATUS_INTERNAL_DRAFT = 5;
00056     // used when a workflow event returns FETCH_TEMPLATE_REPEAT to allow editing again
00057     const STATUS_REPEAT = 6;
00058 
00059     function eZContentObjectVersion( $row=array() )
00060     {
00061         $this->ContentObjectAttributeArray = false;
00062         $this->DataMap = false;
00063         $this->TempNode = null;
00064         $this->VersionName = null;
00065         $this->VersionNameCache = array();
00066         $this->eZPersistentObject( $row );
00067     }
00068 
00069     static function definition()
00070     {
00071         return array( "fields" => array( 'id' =>  array( 'name' => 'ID',
00072                                                          'datatype' => 'integer',
00073                                                          'default' => 0,
00074                                                          'required' => true ),
00075                                          'contentobject_id' =>  array( 'name' => 'ContentObjectID',
00076                                                                        'datatype' => 'integer',
00077                                                                        'default' => 0,
00078                                                                        'required' => true,
00079                                                                        'foreign_class' => 'eZContentObject',
00080                                                                        'foreign_attribute' => 'id',
00081                                                                        'multiplicity' => '1..*' ),
00082                                          'creator_id' =>  array( 'name' => 'CreatorID',
00083                                                                  'datatype' => 'integer',
00084                                                                  'default' => 0,
00085                                                                  'required' => true,
00086                                                                  'foreign_class' => 'eZUser',
00087                                                                  'foreign_attribute' => 'id',
00088                                                                  'multiplicity' => '1..*' ),
00089                                          'version' =>  array( 'name' => 'Version',
00090                                                               'datatype' => 'integer',
00091                                                               'default' => 0,
00092                                                               'required' => true ),
00093                                          'status' =>  array( 'name' => 'Status',
00094                                                              'datatype' => 'integer',
00095                                                              'default' => 0,
00096                                                              'required' => true ),
00097                                          'created' =>  array( 'name' => 'Created',
00098                                                               'datatype' => 'integer',
00099                                                               'default' => 0,
00100                                                               'required' => true ),
00101                                          'modified' =>  array( 'name' => 'Modified',
00102                                                                'datatype' => 'integer',
00103                                                                'default' => 0,
00104                                                                'required' => true ),
00105                                          'workflow_event_pos' =>  array( 'name' => 'WorkflowEventPos',
00106                                                                          'datatype' => 'integer',
00107                                                                          'default' => 0,
00108                                                                          'required' => true ),
00109                                          'user_id' =>  array( 'name' => 'UserID',
00110                                                               'datatype' => 'integer',
00111                                                               'default' => 0,
00112                                                               'required' => true,
00113                                                               'foreign_class' => 'eZUser',
00114                                                               'foreign_attribute' => 'contentobject_id',
00115                                                               'multiplicity' => '1..*' ),
00116                                          'language_mask' => array( 'name' => 'LanguageMask',
00117                                                                    'datatype' => 'integer',
00118                                                                    'default' => 0,
00119                                                                    'required' => true ),
00120                                          'initial_language_id' => array( 'name' => 'InitialLanguageID',
00121                                                                          'datatype' => 'integer',
00122                                                                          'default' => 0,
00123                                                                          'required' => true,
00124                                                                          'foreign_class' => 'eZContentLanguage',
00125                                                                          'foreign_attribute' => 'id',
00126                                                                          'multiplicity' => '1..*' ) ),
00127                       'keys' => array( 'id' ),
00128                       'function_attributes' => array( // 'data' => 'fetchData',
00129                                                       'creator' => 'creator',
00130                                                       "name" => "name",
00131                                                       "version_name" => "versionName",
00132                                                       'main_parent_node_id' => 'mainParentNodeID',
00133                                                       "contentobject_attributes" => "contentObjectAttributes",
00134                                                       "related_contentobject_array" => "relatedContentObjectArray",
00135                                                       'reverse_related_object_list' => "reverseRelatedObjectList",
00136                                                       'parent_nodes' => 'parentNodes',
00137                                                       "can_read" => "canVersionRead",
00138                                                       'can_remove' => 'canVersionRemove',
00139                                                       "data_map" => "dataMap",
00140                                                       'node_assignments' => 'nodeAssignments',
00141                                                       'contentobject' => 'contentObject',
00142                                                       'initial_language' => 'initialLanguage',
00143                                                       'language_list' => 'translations',
00144                                                       'translation' => 'translation',
00145                                                       'translation_list' => 'defaultTranslationList',
00146                                                       'complete_translation_list' => 'translationList',
00147                                                       'temp_main_node' => 'tempMainNode' ),
00148                       'class_name' => "eZContentObjectVersion",
00149                       "increment_key" => "id",
00150                       'sort' => array( 'version' => 'asc' ),
00151                       'name' => 'ezcontentobject_version' );
00152     }
00153 
00154     static function statusList( $limit = false )
00155     {
00156         if ( $limit == 'remove' )
00157         {
00158             $versions = array( array( 'name' => 'Draft', 'id' =>  eZContentObjectVersion::STATUS_DRAFT ),
00159                                array( 'name' => 'Pending', 'id' =>  eZContentObjectVersion::STATUS_PENDING ),
00160                                array( 'name' => 'Archived', 'id' =>  eZContentObjectVersion::STATUS_ARCHIVED ),
00161                                array( 'name' => 'Rejected', 'id' =>  eZContentObjectVersion::STATUS_REJECTED ) );
00162         }
00163         else
00164         {
00165             $versions = array( array( 'name' => 'Draft', 'id' =>  eZContentObjectVersion::STATUS_DRAFT ),
00166                                array( 'name' => 'Published', 'id' =>  eZContentObjectVersion::STATUS_PUBLISHED ),
00167                                array( 'name' => 'Pending', 'id' =>  eZContentObjectVersion::STATUS_PENDING ),
00168                                array( 'name' => 'Archived', 'id' =>  eZContentObjectVersion::STATUS_ARCHIVED ),
00169                                array( 'name' => 'Rejected', 'id' =>  eZContentObjectVersion::STATUS_REJECTED ) );
00170         }
00171         return $versions;
00172     }
00173     /*!
00174      \return true if the requested attribute exists in object.
00175     */
00176 
00177     static function fetch( $id, $asObject = true )
00178     {
00179         return eZPersistentObject::fetchObject( eZContentObjectVersion::definition(),
00180                                                 null,
00181                                                 array( 'id' => $id ),
00182                                                 $asObject );
00183     }
00184 
00185     static function fetchVersion( $version, $contentObjectID, $asObject = true )
00186     {
00187         $ret = eZPersistentObject::fetchObjectList( eZContentObjectVersion::definition(),
00188                                                     null, array( "version" => $version,
00189                                                                  "contentobject_id" => $contentObjectID
00190                                                                  ),
00191                                                     null, null,
00192                                                     $asObject );
00193         return isset( $ret[0] ) ? $ret[0] : false;
00194     }
00195 
00196     static function fetchUserDraft( $objectID, $userID )
00197     {
00198         $versions = eZPersistentObject::fetchObjectList( eZContentObjectVersion::definition(),
00199                                                           null, array( 'creator_id' => $userID,
00200                                                                        'contentobject_id' => $objectID,
00201                                                                        'status' => array( array( eZContentObjectVersion::STATUS_DRAFT, eZContentObjectVersion::STATUS_INTERNAL_DRAFT ) ) ),
00202                                                           array( 'version' => 'asc' ), null,
00203                                                           true );
00204         if ( $versions === null or
00205              count( $versions ) == 0 )
00206             return null;
00207         return $versions[0];
00208     }
00209 
00210     static function fetchForUser( $userID, $status = eZContentObjectVersion::STATUS_DRAFT )
00211     {
00212         return eZPersistentObject::fetchObjectList( eZContentObjectVersion::definition(),
00213                                                     null, array( 'creator_id' => $userID,
00214                                                                  'status' => $status
00215                                                                  ),
00216                                                     null, null,
00217                                                     true );
00218     }
00219 
00220     static function fetchFiltered( $filters, $offset, $limit )
00221     {
00222         $limits = null;
00223         if ( $offset or $limit )
00224             $limits = array( 'offset' => $offset,
00225                              'length' => $limit );
00226         return eZPersistentObject::fetchObjectList( eZContentObjectVersion::definition(),
00227                                                     null, $filters,
00228                                                     null, $limits,
00229                                                     true );
00230     }
00231 
00232     /*!
00233      \return an eZContentObjectTreeNode object which doesn't really exist in the DB,
00234              this can be passed to a node view template.
00235     */
00236     function tempMainNode()
00237     {
00238         if ( $this->TempNode !== null )
00239             return $this->TempNode;
00240         $object = $this->contentObject();
00241         if ( $object->attribute( 'status' ) == eZContentObject::STATUS_DRAFT )
00242         {
00243             $nodeAssignments = $this->nodeAssignments();
00244             $mainNodeAssignment = null;
00245             foreach( $nodeAssignments as $nodeAssignment )
00246             {
00247                 if ( $nodeAssignment->attribute( 'is_main' ) )
00248                 {
00249                     $mainNodeAssignment = $nodeAssignment;
00250                     break;
00251                 }
00252             }
00253             if ( $mainNodeAssignment === null and
00254                  count( $nodeAssignments ) > 0 )
00255             {
00256                 $mainNodeAssignment = $nodeAssignments[0];
00257             }
00258             if ( $mainNodeAssignment )
00259             {
00260                 $this->TempNode = $mainNodeAssignment->tempNode();
00261             }
00262         }
00263         else if ( $object->attribute( 'status' ) == eZContentObject::STATUS_PUBLISHED )
00264         {
00265             $mainNode = $object->mainNode();
00266             if ( is_object( $mainNode ) )
00267             {
00268                 $this->TempNode = eZContentObjectTreeNode::create( $mainNode->attribute( 'parent_node_id' ),
00269                                                                    $mainNode->attribute( 'contentobject_id' ),
00270                                                                    $this->attribute( 'version' ),
00271                                                                    $mainNode->attribute( 'sort_field' ),
00272                                                                    $mainNode->attribute( 'sort_order' ) );
00273                 $this->TempNode->setName( $mainNode->Name );
00274             }
00275         }
00276         return $this->TempNode;
00277     }
00278 
00279     /*!
00280      \return the name of the current version, optionally in the specific language \a $lang
00281     */
00282     function name( $lang = false )
00283     {
00284         if ( $this->VersionName !== null )
00285             return $this->VersionName;
00286         $this->VersionName = $this->attribute( 'contentobject' )->versionLanguageName( $this->attribute( 'version' ),
00287                                                                                        $lang );
00288         if ( $lang !== false )
00289         {
00290             $contentObject = $this->contentObject();
00291             if ( $contentObject )
00292             {
00293                 return $contentObject->name( false, $lang );
00294             }
00295         }
00296         if ( $this->VersionName === false )
00297         {
00298             $contentObject = $this->contentObject();
00299             if ( $contentObject )
00300             {
00301                 $this->VersionName = $contentObject->name( false, $lang );
00302             }
00303         }
00304         return $this->VersionName;
00305     }
00306 
00307     /*!
00308      \return the name of the current version, optionally in the specific language \a $lang
00309     */
00310     function versionName( $lang = false )
00311     {
00312         if ( !$lang )
00313         {
00314             $lang = $this->initialLanguageCode();
00315         }
00316 
00317         if ( isset( $this->VersionNameCache[$lang] ) )
00318             return $this->VersionNameCache[$lang];
00319 
00320         $object = $this->attribute( 'contentobject' );
00321         if ( !$object )
00322         {
00323             $retValue = false;
00324             return $retValue;
00325         }
00326 
00327         $class = $object->attribute( 'content_class' );
00328         if ( !$class )
00329         {
00330             $retValue = false;
00331             return $retValue;
00332         }
00333 
00334         $this->VersionNameCache[$lang] = $class->contentObjectName( $object,
00335                                                                     $this->attribute( 'version' ),
00336                                                                     $lang );
00337         return $this->VersionNameCache[$lang];
00338     }
00339 
00340     /*!
00341      \return \c true if the current user can read this version of the object.
00342      \note The reference for the return value is required to workaround
00343            a bug with PHP references.
00344     */
00345     function canVersionRead( )
00346     {
00347         if ( !isset( $this->Permissions["can_versionread"] ) )
00348         {
00349             $this->Permissions["can_versionread"] = $this->checkAccess( 'versionread' );
00350         }
00351         return ( $this->Permissions["can_versionread"] == 1 );
00352     }
00353 
00354     /*!
00355      \return \c true if the current user can remove this version of the object.
00356      \note The reference for the return value is required to workaround
00357            a bug with PHP references.
00358     */
00359     function canVersionRemove( )
00360     {
00361         if ( !isset( $this->Permissions['can_versionremove'] ) )
00362         {
00363             $this->Permissions['can_versionremove'] = $this->checkAccess( 'versionremove' );
00364         }
00365         return ( $this->Permissions['can_versionremove'] == 1 );
00366     }
00367 
00368     function checkAccess( $functionName, $originalClassID = false, $parentClassID = false, $returnAccessList = false, $language = false )
00369     {
00370         $classID = $originalClassID;
00371         $user = eZUser::currentUser();
00372         $userID = $user->attribute( 'contentobject_id' );
00373         $accessResult =  $user->hasAccessTo( 'content' , $functionName );
00374         $accessWord = $accessResult['accessWord'];
00375         $object = $this->attribute( 'contentobject' );
00376         $objectClassID = $object->attribute( 'contentclass_id' );
00377         if ( ! $classID )
00378         {
00379             $classID = $objectClassID;
00380         }
00381 
00382         //include_once( 'kernel/classes/ezcontentlanguage.php' );
00383         // Fetch the ID of the language if we get a string with a language code
00384         // e.g. 'eng-GB'
00385         $originalLanguage = $language;
00386         if ( is_string( $language ) && strlen( $language ) > 0 )
00387         {
00388             $language = eZContentLanguage::idByLocale( $language );
00389         }
00390         else
00391         {
00392             $language = false;
00393         }
00394 
00395         // This will be filled in with the available languages of the object
00396         // if a Language check is performed.
00397         $languageList = false;
00398 
00399         // 'create' is not allowed on versions
00400         if ( $functionName == 'create' )
00401         {
00402             return false;
00403         }
00404 
00405         if ( $functionName == 'edit' )
00406         {
00407             // Extra checking for status and ownership
00408             if ( !in_array( $this->attribute( 'status' ),
00409                             array( eZContentObjectVersion::STATUS_DRAFT,
00410                                    eZContentObjectVersion::STATUS_INTERNAL_DRAFT,
00411                                    eZContentObjectVersion::STATUS_PENDING ) ) ||
00412                  $this->attribute( 'creator_id' ) != $userID )
00413             {
00414                 return false;
00415             }
00416             return true;
00417         }
00418 
00419         if ( $functionName == 'versionremove' and $this->attribute( 'status' ) == eZContentObjectVersion::STATUS_PUBLISHED )
00420         {
00421             return 0;
00422         }
00423 
00424         if ( $accessWord == 'yes' )
00425         {
00426             return 1;
00427         }
00428         elseif ( $accessWord == 'no' )
00429         {
00430             if ( $functionName == 'edit' )
00431             {
00432                 // Check if we have 'create' access under the main parent
00433                 if ( $object && $object->attribute( 'current_version' ) == 1 && !$object->attribute( 'status' ) )
00434                 {
00435                     $mainNode = eZNodeAssignment::fetchForObject( $object->attribute( 'id' ), $object->attribute( 'current_version' ) );
00436                     $parentObj = $mainNode[0]->attribute( 'parent_contentobject' );
00437                     $result = $parentObj->checkAccess( 'create', $object->attribute( 'contentclass_id' ),
00438                                                        $parentObj->attribute( 'contentclass_id' ), false, $originalLanguage );
00439                     return $result;
00440                 }
00441                 else
00442                 {
00443                     return 0;
00444                 }
00445             }
00446 
00447             return 0;
00448         }
00449         else
00450         {
00451             $limitationList = $accessResult['policies'];
00452 
00453             if ( count( $limitationList ) > 0 )
00454             {
00455                 $access = 'denied';
00456                 foreach ( $limitationList as $limitationArray  )
00457                 {
00458                     if ( $access == 'allowed' )
00459                     {
00460                         break;
00461                     }
00462 
00463                     if ( isset( $limitationArray['Subtree' ] ) )
00464                     {
00465                         $checkedSubtree = false;
00466                     }
00467                     else
00468                     {
00469                         $checkedSubtree = true;
00470                         $accessSubtree = false;
00471                     }
00472                     if ( isset( $limitationArray['Node'] ) )
00473                     {
00474                         $checkedNode = false;
00475                     }
00476                     else
00477                     {
00478                         $checkedNode = true;
00479                         $accessNode = false;
00480                     }
00481                     foreach ( $limitationArray as $key => $limitation )
00482                     {
00483                         $access = 'denied';
00484 
00485                         if ( $key == 'Class' )
00486                         {
00487                             if ( $functionName == 'create' and !$originalClassID )
00488                                 $access = 'allowed';
00489                             else if ( $functionName == 'create' and in_array( $classID, $limitation ) )
00490                                 $access = 'allowed';
00491                             elseif ( in_array( $objectClassID, $limitation ) )
00492                                 $access = 'allowed';
00493                             else
00494                             {
00495                                 $access = 'denied';
00496                                 break;
00497                             }
00498                         }
00499                         elseif ( $key == 'Status' )
00500                         {
00501                             if (  in_array( $this->attribute( 'status' ), $limitation ) )
00502                                 $access = 'allowed';
00503                             else
00504                             {
00505                                 $access = 'denied';
00506                                 break;
00507                             }
00508                         }
00509                         elseif ( $key == 'Section' || $key == 'User_Section' )
00510                         {
00511                             if (  in_array( $object->attribute( 'section_id' ), $limitation ) )
00512                                 $access = 'allowed';
00513                             else
00514                             {
00515                                 $access = 'denied';
00516                                 break;
00517                             }
00518                         }
00519                         elseif (  $key == 'Language' )
00520                         {
00521                             $languageMask = 0;
00522                             // If we don't have a language list yet we need to fetch it
00523                             // and optionally filter out based on $language.
00524                             if ( $functionName == 'create' )
00525                             {
00526                                 // If the function is 'create' we do not use the language_mask for matching.
00527                                 if ( $language !== false )
00528                                 {
00529                                     $languageMask = $language;
00530                                 }
00531                                 else
00532                                 {
00533                                     // If the create is used and no language specified then
00534                                     // we need to match against all possible languages (which
00535                                     // is all bits set, ie. -1).
00536                                     $languageMask = -1;
00537                                 }
00538                             }
00539                             else
00540                             {
00541                                 if ( $language !== false )
00542                                 {
00543                                     if ( $languageList === false )
00544                                     {
00545                                         $languageMask = $this->attribute( 'initial_language_id' );
00546                                         // We are restricting language check to just one language
00547                                         // If the specified language is not the one in the version
00548                                         // it will become 0.
00549                                         $languageMask &= $language;
00550                                     }
00551                                 }
00552                                 else
00553                                 {
00554                                     $languageMask = $this->attribute( 'initial_language_id' );
00555                                 }
00556                             }
00557                             // Fetch limit mask for limitation list
00558                             $limitMask = eZContentLanguage::maskByLocale( $limitationArray[$key] );
00559                             if ( ( $languageMask & $limitMask ) != 0 )
00560                             {
00561                                 $access = 'allowed';
00562                             }
00563                             else
00564                             {
00565                                 $access = 'denied';
00566                             }
00567                         }
00568                         elseif ( $key == 'Owner' )
00569                         {
00570                             if ( $this->attribute( 'creator_id' ) == $userID )
00571                                 $access = 'allowed';
00572                             else
00573                             {
00574                                 $access = 'denied';
00575                                 break;
00576                             }
00577                         }
00578                         elseif ( $key == 'Node' )
00579                         {
00580                             $accessNode = false;
00581                             $contentObjectID = $this->attribute( 'contentobject_id' );
00582                             foreach ( $limitation as $nodeID )
00583                             {
00584                                 $node = eZContentObjectTreeNode::fetch( $nodeID, false, false );
00585                                 $limitationObjectID = $node['contentobject_id'];
00586                                 if ( $contentObjectID == $limitationObjectID )
00587                                 {
00588                                     $access = 'allowed';
00589                                     $accessNode = true;
00590                                     break;
00591                                 }
00592                             }
00593                             if ( $access == 'denied' && $checkedSubtree && !$accessSubtree )
00594                             {
00595                                 break;
00596                             }
00597                             else
00598                             {
00599                                 $access = 'allowed';
00600                             }
00601                             $checkedNode = true;
00602                         }
00603                         elseif ( $key == 'Subtree' )
00604                         {
00605                             $accessSubtree = false;
00606                             $contentObject = $this->attribute( 'contentobject' );
00607                             foreach ( $contentObject->attribute( 'assigned_nodes' ) as  $assignedNode )
00608                             {
00609                                 $path = $assignedNode->attribute( 'path_string' );
00610                                 $subtreeArray = $limitation;
00611                                 foreach ( $subtreeArray as $subtreeString )
00612                                 {
00613                                     if ( strstr( $path, $subtreeString ) )
00614                                     {
00615                                         $access = 'allowed';
00616                                         $accessSubtree = true;
00617                                         break;
00618                                     }
00619                                 }
00620                                 if ( $access == 'allowed' )
00621                                 {
00622                                     break;
00623                                 }
00624                             }
00625                             if ( $access != 'allowed' )
00626                             {
00627                                 foreach( $this->attribute( 'node_assignments' ) as $nodeAssignment )
00628                                 {
00629                                     $parentNode = $nodeAssignment->attribute( 'parent_node_obj' );
00630                                     $path = $parentNode->attribute( 'path_string' );
00631                                     $subtreeArray = $limitation;
00632                                     foreach ( $subtreeArray as $subtreeString )
00633                                     {
00634                                         if ( strstr( $path, $subtreeString ) )
00635                                         {
00636                                             $access = 'allowed';
00637                                             $accessSubtree = true;
00638                                             break;
00639                                         }
00640                                     }
00641                                     if ( $access == 'allowed' )
00642                                     {
00643                                         break;
00644                                     }
00645                                 }
00646                             }
00647                             if ( $access == 'denied' && $checkedNode && !$accessNode )
00648                             {
00649                                 break;
00650                             }
00651                             else
00652                             {
00653                                 $access = 'allowed';
00654                             }
00655                             $checkedSubtree = true;
00656                         }
00657                         elseif ( $key == 'User_Subtree' )
00658                         {
00659                             $contentObject = $this->attribute( 'contentobject' );
00660                             foreach ( $contentObject->attribute( 'assigned_nodes' ) as  $assignedNode )
00661                             {
00662                                 $path = $assignedNode->attribute( 'path_string' );
00663                                 $subtreeArray = $limitation;
00664                                 foreach ( $subtreeArray as $subtreeString )
00665                                 {
00666                                     if ( strstr( $path, $subtreeString ) )
00667                                     {
00668                                         $access = 'allowed';
00669                                         $accessSubtree = true;
00670                                         break;
00671                                     }
00672                                 }
00673                                 if ( $access == 'allowed' )
00674                                 {
00675                                     break;
00676                                 }
00677                             }
00678                             if ( $access != 'allowed' )
00679                             {
00680                                 foreach( $this->attribute( 'node_assignments' ) as $nodeAssignment )
00681                                 {
00682                                     $parentNode = $nodeAssignment->attribute( 'parent_node_obj' );
00683                                     $path = $parentNode->attribute( 'path_string' );
00684                                     $subtreeArray = $limitation;
00685                                     foreach ( $subtreeArray as $subtreeString )
00686                                     {
00687                                         if ( strstr( $path, $subtreeString ) )
00688                                         {
00689                                             $access = 'allowed';
00690                                             break;
00691                                         }
00692                                     }
00693                                     if ( $access == 'allowed' )
00694                                     {
00695                                         break;
00696                                     }
00697                                 }
00698                             }
00699                             if ( $access == 'denied' )
00700                             {
00701                                 break;
00702                             }
00703                         }
00704                     }
00705                 }
00706 
00707                 if ( $access == 'denied' )
00708                 {
00709                     return 0;
00710                 }
00711                 else
00712                 {
00713                     return 1;
00714                 }
00715             }
00716         }
00717     }
00718 
00719     function contentObject()
00720     {
00721         if( !isset( $this->ContentObject ) )
00722         {
00723             $this->ContentObject = eZContentObject::fetch( $this->attribute( 'contentobject_id' ) );
00724         }
00725         return $this->ContentObject;
00726     }
00727 
00728     /*!
00729      \note The reference for the return value is required to workaround
00730            a bug with PHP references.
00731     */
00732     function mainParentNodeID()
00733     {
00734         $temp = eZNodeAssignment::fetchForObject( $this->attribute( 'contentobject_id' ), $this->attribute( 'version' ), 1 );
00735         if ( $temp == null )
00736         {
00737             return 1;
00738         }
00739         else
00740         {
00741             return $temp[0]->attribute( 'parent_node' );
00742         }
00743     }
00744 
00745     function parentNodes( )
00746     {
00747         $retNodes = array();
00748         $nodeAssignmentList = eZNodeAssignment::fetchForObject( $this->attribute( 'contentobject_id' ), $this->attribute( 'version' ) );
00749         foreach ( array_keys( $nodeAssignmentList ) as $key )
00750         {
00751             $nodeAssignment = $nodeAssignmentList[$key];
00752             $retNodes[] = $nodeAssignment;
00753         }
00754         return $retNodes;
00755     }
00756     function nodeAssignments()
00757     {
00758         return eZNodeAssignment::fetchForObject( $this->attribute( 'contentobject_id' ), $this->attribute( 'version' ) );
00759     }
00760 
00761     function assignToNode( $nodeID, $main = 0, $fromNodeID = 0, $sortField = null, $sortOrder = null,
00762                             $remoteID = 0 )
00763     {
00764         if ( $fromNodeID == 0 && ( $this->attribute( 'status' ) == eZContentObjectVersion::STATUS_DRAFT ||
00765                                    $this->attribute( 'status' ) == eZContentObjectVersion::STATUS_INTERNAL_DRAFT ) )
00766             $fromNodeID = -1;
00767         $nodeRow = array( 'contentobject_id' => $this->attribute( 'contentobject_id' ),
00768                           'contentobject_version' => $this->attribute( 'version' ),
00769                           'parent_node' => $nodeID,
00770                           'is_main' => $main,
00771                           'remote_id' => $remoteID,
00772                           'from_node_id' => $fromNodeID );
00773         if ( $sortField !== null )
00774             $nodeRow['sort_field'] = $sortField;
00775         if ( $sortOrder !== null )
00776             $nodeRow['sort_order'] = ( $sortOrder ? 1 : 0 );
00777 
00778         $nodeAssignment = eZNodeAssignment::create( $nodeRow );
00779         $nodeAssignment->store();
00780         return $nodeAssignment;
00781     }
00782 
00783     /*!
00784      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
00785      the calls within a db transaction; thus within db->begin and db->commit.
00786      */
00787     function removeAssignment( $nodeID )
00788     {
00789         $nodeAssignmentList = $this->attribute( 'node_assignments' );
00790         $db = eZDB::instance();
00791         $db->begin();
00792 
00793         foreach ( $nodeAssignmentList as $nodeAssignment )
00794         {
00795             if ( $nodeAssignment->attribute( 'parent_node' ) == $nodeID )
00796             {
00797                 $nodeAssignment->remove();
00798             }
00799         }
00800         $db->commit();
00801     }
00802 
00803     /*!
00804      \return the content object attribute
00805     */
00806     function dataMap()
00807     {
00808         if ( $this->ContentObjectAttributeArray === false )
00809         {
00810             $data = $this->contentObjectAttributes();
00811             // Store the attributes for later use
00812             $this->ContentObjectAttributeArray = $data;
00813         }
00814         else
00815         {
00816             $data = $this->ContentObjectAttributeArray;
00817         }
00818 
00819         if ( $this->DataMap == false )
00820         {
00821             $ret = array();
00822             foreach( $data as $item )
00823             {
00824                 $ret[$item->contentClassAttributeIdentifier()] = $item;
00825             }
00826             $this->DataMap = $ret;
00827         }
00828         else
00829         {
00830             $ret = $this->DataMap;
00831         }
00832         return $ret;
00833     }
00834 
00835     function resetDataMap()
00836     {
00837         $this->ContentObjectAttributeArray = false;
00838         $this->DataMap = false;
00839         return $this->DataMap;
00840     }
00841 
00842     /*!
00843      Returns the related objects.
00844     */
00845     function relatedContentObjectArray()
00846     {
00847         $object = $this->attribute( 'contentobject' );
00848         return $object->relatedContentObjectArray( $this->Version );
00849     }
00850 
00851     static function create( $contentobjectID, $userID = false, $version = 1, $initialLanguageCode = false )
00852     {
00853         if ( $userID === false )
00854         {
00855             $user = eZUser::currentUser();
00856             $userID = $user->attribute( 'contentobject_id' );
00857         }
00858         $time = time();
00859 
00860         if ( $initialLanguageCode == false )
00861         {
00862             $initialLanguageCode = eZContentObject::defaultLanguage();
00863         }
00864 
00865         $initialLanguageID = eZContentLanguage::idByLocale( $initialLanguageCode );
00866 
00867         $row = array(
00868             "contentobject_id" => $contentobjectID,
00869             'initial_language_id' => $initialLanguageID,
00870             'language_mask' => $initialLanguageID,
00871             "version" => $version,
00872             "created" => $time,
00873             "modified" => $time,
00874             'creator_id' => $userID );
00875         return new eZContentObjectVersion( $row );
00876     }
00877 
00878     /*!
00879      \note The reference for the return value is required to workaround
00880            a bug with PHP references.
00881     */
00882     function reverseRelatedObjectList()
00883     {
00884         return $this->attribute( 'contentobject' )->reverseRelatedObjectList( $this->Version );
00885     }
00886 
00887     /*!
00888      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
00889      the calls within a db transaction; thus within db->begin and db->commit.
00890      */
00891     function removeThis()
00892     {
00893         $contentobjectID = $this->attribute( 'contentobject_id' );
00894         $versionNum = $this->attribute( 'version' );
00895 
00896         $contentObjectTranslations = $this->translations();
00897 
00898         foreach ( $contentObjectTranslations as $contentObjectTranslation )
00899         {
00900             foreach ( $contentObjectTranslation->objectAttributes() as $attribute )
00901             {
00902                 $attribute->removeThis( $attribute->attribute( 'id' ), $versionNum );
00903             }
00904         }
00905         $db = eZDB::instance();
00906         $db->begin();
00907         $db->query( "DELETE FROM ezcontentobject_link
00908                          WHERE from_contentobject_id=$contentobjectID AND from_contentobject_version=$versionNum" );
00909         $db->query( "DELETE FROM eznode_assignment
00910                          WHERE contentobject_id=$contentobjectID AND contentobject_version=$versionNum" );
00911 
00912         $db->query( 'DELETE FROM ezcontentobject_version
00913                          WHERE id=' . $this->attribute( 'id' ) );
00914 
00915         $contentobject = $this->attribute( 'contentobject' );
00916         if ( is_object( $contentobject ) )
00917         {
00918             if ( !$contentobject->hasRemainingVersions() )
00919             {
00920                 $contentobject->purge();
00921             }
00922             else
00923             {
00924                 $version = $contentobject->CurrentVersion;
00925                 if ( $contentobject->CurrentVersion == $versionNum ) //will assign another current_version in contentObject.
00926                 {
00927                    //search for version that will be current after removing of this one.
00928                    $candidateToBeCurrent = $db->arrayQuery( "SELECT version
00929                                                      FROM ezcontentobject_version
00930                                                      WHERE contentobject_id={$contentobject->ID} AND
00931                                                            version!={$contentobject->CurrentVersion}
00932                                                      ORDER BY modified DESC",
00933                                                  array( 'offset' => 0, 'limit' => 1 ) );
00934 
00935                    if ( isset($candidateToBeCurrent[0]['version']) && is_numeric($candidateToBeCurrent[0]['version']) )
00936                    {
00937                        $contentobject->CurrentVersion = $candidateToBeCurrent[0]['version'];
00938                        $contentobject->store();
00939                    }
00940                }
00941             }
00942         }
00943         $db->query( "DELETE FROM ezcontentobject_name
00944                          WHERE contentobject_id=$contentobjectID AND content_version=$versionNum" );
00945 
00946         $db->commit();
00947     }
00948 
00949     /*!
00950      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
00951      the calls within a db transaction; thus within db->begin and db->commit.
00952      */
00953     function removeTranslation( $languageCode )
00954     {
00955 
00956         $versionNum = $this->attribute( 'version' );
00957 
00958         $contentObjectAttributes = $this->contentObjectAttributes( $languageCode );
00959 
00960         $db = eZDB::instance();
00961         $db->begin();
00962         foreach ( $contentObjectAttributes as $attribute )
00963         {
00964             $attribute->removeThis( $attribute->attribute( 'id' ), $versionNum );
00965         }
00966         $db->commit();
00967 
00968         $this->updateLanguageMask();
00969     }
00970 
00971     /*!
00972      \static
00973      Will remove all version that match the status set in \a $versionStatus.
00974      \param $versionStatus can either be a single value or an array with values,
00975                            if \c false the function will remove all status except published.
00976      \param $limit limits count of versions which should be removed.
00977      \param $expiryTime if not false then method will remove only versions which have modified time less than specified expiry time.
00978      \param $fetchPortionSize portion size for single fetch() call to avoid memory overflow erros (default 50).
00979      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
00980      the calls within a db transaction; thus within db->begin and db->commit.
00981     */
00982     static function removeVersions( $versionStatus = false, $limit = false, $expiryTime = false, $fetchPortionSize = 50 )
00983     {
00984         $statuses = array( eZContentObjectVersion::STATUS_DRAFT,
00985                            eZContentObjectVersion::STATUS_PENDING,
00986                            eZContentObjectVersion::STATUS_REJECTED,
00987                            eZContentObjectVersion::STATUS_ARCHIVED,
00988                            eZContentObjectVersion::STATUS_INTERNAL_DRAFT );
00989         if ( $versionStatus === false )
00990         {
00991             $versionStatus = $statuses;
00992         }
00993         else if ( !is_array( $versionStatus ) )
00994         {
00995             $versionStatus = array( $versionStatus );
00996         }
00997 
00998         $versionStatus = array_unique( $versionStatus );
00999         $checkIntersect = array_intersect( $versionStatus, $statuses );
01000         if ( count( $checkIntersect ) != count( $versionStatus ) )
01001         {
01002             eZDebug::writeError( 'Invalid version status was passed in.', 'eZContentObjectVersion::removeVersions()' );
01003             return false;
01004         }
01005 
01006         if ( $limit !== false and ( !is_numeric( $limit ) or $limit < 0 ) )
01007         {
01008             eZDebug::writeError( '$limit must be either false or positive numeric value.', 'eZContentObjectVersion::removeVersions()' );
01009             return false;
01010         }
01011 
01012         if ( !is_numeric( $fetchPortionSize ) or $fetchPortionSize < 1 )
01013             $fetchPortionSize = 50;
01014 
01015         $filters = array();
01016         $filters['status'] = array( $versionStatus );
01017         if ( $expiryTime !== false )
01018             $filters['modified'] = array( '<', $expiryTime );
01019 
01020         $processedCount = 0;
01021         while ( $processedCount < $limit or !$limit )
01022         {
01023             // fetch by versions by preset portion at a time to avoid memory overflow
01024             $tmpLimit = ( !$limit or ( $limit - $processedCount ) > $fetchPortionSize ) ?
01025                             $fetchPortionSize : $limit - $processedCount;
01026             $versions = eZContentObjectVersion::fetchFiltered( $filters, 0, $tmpLimit );
01027             if ( count( $versions ) < 1 )
01028                 break;
01029 
01030             $db = eZDB::instance();
01031             $db->begin();
01032             foreach ( $versions as $version )
01033             {
01034                 $version->removeThis();
01035             }
01036             $db->commit();
01037             $processedCount += count( $versions );
01038         }
01039         return $processedCount;
01040     }
01041 
01042     /*!
01043      Clones the version with new version \a $newVersionNumber and creator \a $userID
01044      \note The cloned version is not stored.
01045     */
01046     function cloneVersion( $newVersionNumber, $userID, $contentObjectID = false, $status = eZContentObjectVersion::STATUS_DRAFT )
01047     {
01048         $time = time();
01049         $clonedVersion = clone $this;
01050         $clonedVersion->setAttribute( 'id', null );
01051         if ( $contentObjectID !== false )
01052             $clonedVersion->setAttribute( 'contentobject_id', $contentObjectID );
01053         $clonedVersion->setAttribute( 'version', $newVersionNumber );
01054         $clonedVersion->setAttribute( 'created', $time );
01055         $clonedVersion->setAttribute( 'modified', $time );
01056         $clonedVersion->setAttribute( 'creator_id', $userID );
01057         if ( $status !== false )
01058             $clonedVersion->setAttribute( 'status', $status );
01059         return $clonedVersion;
01060     }
01061 
01062     /*!
01063      \return An array with all the translations for the current version.
01064      \note The reference for the return value is required to workaround
01065            a bug with PHP references.
01066     */
01067     function translations( $asObject = true )
01068     {
01069         return $this->translationList( false, $asObject );
01070     }
01071 
01072     /*!
01073      \return An array with all the translations for the current version.
01074      \note The reference for the return value is required to workaround
01075            a bug with PHP references.
01076      \deprecated
01077     */
01078     function translation( $asObject = true )
01079     {
01080         return false;
01081     }
01082 
01083     /*!
01084      \return An array with all the translations for the current version.
01085      \note The reference for the return value is required to workaround
01086            a bug with PHP references.
01087 
01088      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
01089            the calls within a db transaction; thus within db->begin and db->commit.
01090     */
01091     function translationList( $language = false, $asObject = true )
01092     {
01093         $db = eZDB::instance();
01094 
01095         $languageSQL = '';
01096         if ( $language !== false )
01097         {
01098             $language = $db->escapeString( $language );
01099             $languageSQL = "AND language_code!='$language'";
01100         }
01101 
01102         $query = "SELECT DISTINCT language_code
01103                   FROM ezcontentobject_attribute
01104                   WHERE contentobject_id='$this->ContentObjectID' AND version='$this->Version'
01105                   $languageSQL
01106                   ORDER BY language_code";
01107 
01108         $languageCodes = $db->arrayQuery( $query );
01109 
01110         $translations = array();
01111         if ( $asObject )
01112         {
01113             foreach ( $languageCodes as $languageCode )
01114             {
01115                 $translations[] = new eZContentObjectTranslation( $this->ContentObjectID, $this->Version, $languageCode["language_code"] );
01116             }
01117         }
01118         else
01119         {
01120             foreach ( $languageCodes as $languageCode )
01121             {
01122                 $translations[] = $languageCode["language_code"];
01123             }
01124         }
01125 
01126         return $translations;
01127     }
01128 
01129     /*!
01130      \return An array with all translations except default language for the this version.
01131      \note The reference for the return value is required to workaround
01132            a bug with PHP references.
01133 
01134      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
01135            the calls within a db transaction; thus within db->begin and db->commit.
01136     */
01137     function defaultTranslationList()
01138     {
01139         return $this->translationList();
01140     }
01141 
01142     /*!
01143      Returns the attributes for the current content object version.
01144      If \a $language is not specified it will use the initial language of the version.
01145     */
01146     function contentObjectAttributes( $languageCode = false, $asObject = true )
01147     {
01148         if ( $languageCode == false )
01149         {
01150             if ( $this->Status == eZContentObjectVersion::STATUS_DRAFT || $this->Status == eZContentObjectVersion::STATUS_INTERNAL_DRAFT || $this->Status == eZContentObjectVersion::STATUS_PENDING )
01151             {
01152                 $languageCode = $this->initialLanguageCode();
01153             }
01154             else if ( $this->CurrentLanguage )
01155             {
01156                 $languageCode = $this->CurrentLanguage;
01157             }
01158         }
01159 
01160         return eZContentObjectVersion::fetchAttributes( $this->Version, $this->ContentObjectID, $languageCode, $asObject );
01161     }
01162 
01163     /*!
01164      Returns the attributes for the content object version \a $version and content object \a $contentObjectID.
01165      \a $language defines the language to fetch.
01166      \static
01167      \sa attributes
01168      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
01169      the calls within a db transaction; thus within db->begin and db->commit.
01170     */
01171     static function fetchAttributes( $version, $contentObjectID, $language = false, $asObject = true )
01172     {
01173         $db = eZDB::instance();
01174         $language = $db->escapeString( $language );
01175         $contentObjectID = (int) $contentObjectID;
01176         $version =(int) $version;
01177         $query = "SELECT ezcontentobject_attribute.*, ezcontentclass_attribute.identifier as classattribute_identifier,
01178                         ezcontentclass_attribute.can_translate, ezcontentclass_attribute.serialized_name_list as attribute_serialized_name_list
01179                   FROM  ezcontentobject_attribute, ezcontentclass_attribute, ezcontentobject_version
01180                   WHERE
01181                     ezcontentclass_attribute.version = '0' AND
01182                     ezcontentclass_attribute.id = ezcontentobject_attribute.contentclassattribute_id AND
01183                     ezcontentobject_attribute.version = '$version' AND
01184                     ezcontentobject_attribute.contentobject_id = '$contentObjectID' AND
01185                     ezcontentobject_version.contentobject_id = '$contentObjectID' AND
01186                     ezcontentobject_version.version = '$version' AND ".
01187                     ( ( $language )? "ezcontentobject_attribute.language_code = '$language'": eZContentLanguage::sqlFilter( 'ezcontentobject_attribute', 'ezcontentobject_version' ) ).
01188                   " ORDER by
01189                     ezcontentclass_attribute.placement ASC";
01190 
01191         $attributeArray = $db->arrayQuery( $query );
01192 
01193         $returnAttributeArray = array();
01194         foreach ( $attributeArray as $attribute )
01195         {
01196             $attr = new eZContentObjectAttribute( $attribute );
01197 
01198             $attr->setContentClassAttributeIdentifier( $attribute['classattribute_identifier'] );
01199 
01200             $dataType = $attr->dataType();
01201 
01202             if ( is_object( $dataType ) &&
01203                  $dataType->Attributes["properties"]["translation_allowed"] &&
01204                  $attribute['can_translate'] )
01205                 $attr->setContentClassAttributeCanTranslate( 1 );
01206             else
01207                 $attr->setContentClassAttributeCanTranslate( 0 );
01208 
01209             $attr->setContentClassAttributeName( eZContentClassAttribute::nameFromSerializedString( $attribute['attribute_serialized_name_list'] ) );
01210 
01211             $returnAttributeArray[] = $attr;
01212         }
01213         return $returnAttributeArray;
01214     }
01215 
01216     /*!
01217      \private
01218      Maps input lange to another one if defined in $options['language_map'].
01219      If it cannot map it returns the original language.
01220      \returns string
01221      */
01222     static function mapLanguage( $language, $options )
01223     {
01224         if ( isset( $options['language_map'][$language] ) )
01225         {
01226             return $options['language_map'][$language];
01227         }
01228         return $language;
01229     }
01230 
01231     /*!
01232      \static
01233      Unserialize xml structure. Create object from xml input.
01234 
01235      \param XML DOM Node
01236      \param contentobject.
01237      \param owner ID
01238      \param section ID
01239      \param new object, true if first version of new object
01240      \param options
01241      \param package
01242 
01243      \returns created object, false if could not create object/xml invalid
01244      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
01245      the calls within a db transaction; thus within db->begin and db->commit.
01246     */
01247     static function unserialize( $domNode, $contentObject, $ownerID, $sectionID, $activeVersion, $firstVersion, &$nodeList, &$options, $package, $handlerType = 'ezcontentobject' )
01248     {
01249 
01250         $oldVersion = $domNode->getAttributeNS( 'http://ez.no/ezobject', 'version' );
01251         $status = $domNode->getAttributeNS( 'http://ez.no/ezobject', 'status' );
01252         $languageNodeArray = $domNode->getElementsByTagName( 'object-translation' );
01253 
01254         $initialLanguage   = false;
01255         $importedLanguages = $options['language_array'];
01256         $currentLanguages  = array();
01257         foreach( $languageNodeArray as $languageNode )
01258         {
01259             $language = eZContentObjectVersion::mapLanguage( $languageNode->getAttribute( 'language' ), $options );
01260             if ( in_array( $language, $importedLanguages ) )
01261             {
01262                 $currentLanguages[] = $language;
01263             }
01264         }
01265         foreach ( eZContentLanguage::prioritizedLanguages() as $language )
01266         {
01267             if ( in_array( $language->attribute( 'locale' ), $currentLanguages ) )
01268             {
01269                 $initialLanguage = $language->attribute( 'locale' );
01270                 break;
01271             }
01272         }
01273         if ( !$initialLanguage )
01274         {
01275             $initialLanguage = $currentLanguages[0];
01276         }
01277 
01278         if ( $firstVersion )
01279         {
01280             $contentObjectVersion = $contentObject->version( 1 );
01281         }
01282         else
01283         {
01284             // Create new version in specific language but with empty data.
01285             $contentObjectVersion = $contentObject->createNewVersionIn( $initialLanguage );
01286         }
01287 
01288         //include_once( 'lib/ezlocale/classes/ezdateutils.php' );
01289         $created = eZDateUtils::textToDate( $domNode->getAttributeNS( 'http://ez.no/ezobject', 'created' ) );
01290         $modified = eZDateUtils::textToDate( $domNode->getAttributeNS( 'http://ez.no/ezobject', 'modified' ) );
01291         $contentObjectVersion->setAttribute( 'created', $created );
01292         $contentObjectVersion->setAttribute( 'modified', $modified );
01293 
01294         $contentObjectVersion->setAttribute( 'status', eZContentObjectVersion::STATUS_DRAFT );
01295         $contentObjectVersion->store();
01296 
01297         $db = eZDB::instance();
01298         $db->begin();
01299         foreach( $languageNodeArray as $languageNode )
01300         {
01301             $language = eZContentObjectVersion::mapLanguage( $languageNode->getAttribute( 'language' ), $options );
01302             // Only import allowed languages.
01303             if ( !in_array( $language, $importedLanguages ) )
01304             {
01305                 continue;
01306             }
01307 
01308             $attributeArray = $contentObjectVersion->contentObjectAttributes( $language );
01309             if ( count( $attributeArray ) == 0)
01310             {
01311                 $hasTranslation = eZContentLanguage::fetchByLocale( $language );
01312 
01313                 if ( !$hasTranslation )
01314                 {
01315                     // if there is no needed translation in system then add it
01316                     $locale = eZLocale::instance( $language );
01317 
01318                     if ( $locale->isValid() )
01319                     {
01320                         eZContentLanguage::addLanguage( $locale->localeCode(), $locale->internationalLanguageName() );
01321                         $hasTranslation = true;
01322                     }
01323                     else
01324                         $hasTranslation = false;
01325                 }
01326 
01327                 if ( $hasTranslation )
01328                 {
01329                     // Add translated attributes for the translation
01330                     $originalContentAttributes = $contentObjectVersion->contentObjectAttributes( $initialLanguage );
01331                     foreach ( $originalContentAttributes as $originalContentAttribute )
01332                     {
01333                         $contentAttribute = $originalContentAttribute->translateTo( $language );
01334                         $contentAttribute->sync();
01335                         $attributeArray[] = $contentAttribute;
01336                     }
01337                 }
01338 
01339                 // unserialize object name in current version-translation
01340                 $objectName = $languageNode->getAttribute( 'object_name' );
01341                 if ( $objectName )
01342                     $contentObject->setName( $objectName, $contentObjectVersion->attribute( 'version' ), $language );
01343             }
01344 
01345             $xpath = new DOMXPath( $domNode->ownerDocument );
01346             $xpath->registerNamespace( 'ezobject', 'http://ez.no/object/' );
01347             $xpath->registerNamespace( 'ezremote', 'http://ez.no/ezobject' );
01348 
01349             foreach( $attributeArray as $attribute )
01350             {
01351                 $attributeIdentifier = $attribute->attribute( 'contentclass_attribute_identifier' );
01352                 $xpathQuery = "ezobject:attribute[@ezremote:identifier='$attributeIdentifier']";
01353                 $attributeDomNodes = $xpath->query( $xpathQuery, $languageNode );
01354                 $attributeDomNode = $attributeDomNodes->item( 0 );
01355                 if ( !$attributeDomNode )
01356                 {
01357                     continue;
01358                 }
01359                 $attribute->unserialize( $package, $attributeDomNode );
01360                 $attribute->store();
01361             }
01362         }
01363 
01364         $objectRelationList = $domNode->getElementsByTagName( 'object-relation-list' )->item( 0 );
01365         if ( $objectRelationList )
01366         {
01367             $objectRelationArray = $objectRelationList->getElementsByTagName( 'related-object-remote-id' );
01368             foreach( $objectRelationArray as $objectRelation )
01369             {
01370                 $relatedObjectRemoteID = $objectRelation->textContent;
01371                 if ( $relatedObjectRemoteID )
01372                 {
01373                     $object = eZContentObject::fetchByRemoteID( $relatedObjectRemoteID );
01374                     $relatedObjectID = ( $object !== null ) ? $object->attribute( 'id' ) : null;
01375 
01376                     if ( $relatedObjectID )
01377                     {
01378                         $contentObject->addContentObjectRelation( $relatedObjectID, $contentObjectVersion->attribute( 'version' ) );
01379                     }
01380                     else
01381                     {
01382                         if ( !isset( $options['suspended-relations'] ) )
01383                         {
01384                             $options['suspended-relations'] = array();
01385                         }
01386 
01387                         $options['suspended-relations'][] = array( 'related-object-remote-id' => $relatedObjectRemoteID,
01388                                                                    'contentobject-id'         => $contentObject->attribute( 'id' ),
01389                                                                    'contentobject-version'    => $contentObjectVersion->attribute( 'version' ) );
01390                     }
01391                 }
01392             }
01393         }
01394 
01395         $nodeAssignmentNodeList = $domNode->getElementsByTagName( 'node-assignment-list' )->item( 0 );
01396         $nodeAssignmentNodeArray = $nodeAssignmentNodeList->getElementsByTagName( 'node-assignment' );
01397         foreach( $nodeAssignmentNodeArray as $nodeAssignmentNode )
01398         {
01399             $result = eZContentObjectTreeNode::unserialize( $nodeAssignmentNode,
01400                                                             $contentObject,
01401                                                             $contentObjectVersion->attribute( 'version' ),
01402                                                             ( $oldVersion == $activeVersion ? 1 : 0 ),
01403                                                             $nodeList,
01404                                                             $options,
01405                                                             $handlerType );
01406             if ( $result === false )
01407             {
01408                 $db->commit();
01409                 $retValue = false;
01410                 return $retValue;
01411             }
01412         }
01413 
01414         $contentObjectVersion->store();
01415         $db->commit();
01416 
01417         return $contentObjectVersion;
01418     }
01419 
01420     function postUnserialize( $package )
01421     {
01422         foreach( $this->translations( false ) as $language )
01423         {
01424             foreach( $this->contentObjectAttributes( $language ) as $attribute )
01425             {
01426                 $attribute->postUnserialize( $package );
01427             }
01428         }
01429     }
01430 
01431     /*!
01432      \return a DOM structure of the content object version, it's translations and attributes.
01433 
01434      \param package
01435      \param package options ( optianal )
01436      \param array of allowed nodes ( optional )
01437      \param array of top nodes in current package export (optional )
01438      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
01439      the calls within a db transaction; thus within db->begin and db->commit.
01440     */
01441     function serialize( $package, $options = false, $contentNodeIDArray = false, $topNodeIDArray = false )
01442     {
01443         $dom = new DOMDocument( '1.0', 'utf-8' );
01444 
01445         $versionNode = $dom->createElementNS( 'http://ez.no/object/', 'ezobject:version' );
01446         $dom->appendChild( $versionNode );
01447 
01448         //include_once( 'lib/ezlocale/classes/ezdateutils.php' );
01449 
01450         $versionNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:version', $this->Version );
01451         $versionNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:status', $this->Status );
01452         $versionNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:created', eZDateUtils::rfc1123Date( $this->attribute( 'created' ) ) );
01453         $versionNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:modified', eZDateUtils::rfc1123Date( $this->attribute( 'modified' ) ) );
01454 
01455         $translationList = $this->translationList( false, false );
01456         $contentObject   = $this->attribute( 'contentobject' );
01457 
01458         $db = eZDB::instance();
01459         $db->begin();
01460         $allowedLanguages = $options['language_array'];
01461         if ( $options['only_initial_language'] )
01462         {
01463             $initialLanguageCode = $this->initialLanguageCode();
01464             if ( !in_array( $initialLanguageCode, $allowedLanguages ) )
01465             {
01466                 // We can only export initial language but is not in the allowed
01467                 // language list so we return false, ie. no export of this version.
01468                 return false;
01469             }
01470             // Make sure only the initial language is exported
01471             $allowedLanguages = array( $initialLanguageCode );
01472         }
01473         $exportedLanguages = array();
01474         foreach ( $translationList as $translationItem )
01475         {
01476             $language = $translationItem;
01477             if ( !in_array( $language, $allowedLanguages ) )
01478             {
01479                 continue;
01480             }
01481 
01482             $translationNode = $dom->createElementNS( 'http://ez.no/object/', 'ezobject:object-translation' );
01483             $translationNode->setAttribute( 'language', $language );
01484 
01485             // serialize object name in current version-translation
01486             $objectName = $contentObject->name( $this->Version, $language );
01487             if ( $objectName )
01488             {
01489                 $translationNode->setAttribute( 'object_name', $objectName );
01490             }
01491             else
01492             {
01493                 eZDebug::writeWarning( sprintf( "Name for object %s of version %s in translation %s not found",
01494                                                 $contentObject->attribute( 'id' ),
01495                                                 $this->Version,
01496                                                 $language ) );
01497             }
01498 
01499             $attributes = $this->contentObjectAttributes( $language );
01500             foreach ( $attributes as $attribute )
01501             {
01502                 $serializedAttributeNode = $attribute->serialize( $package );
01503                 $importedSerializedAttributeNode = $dom->importNode( $serializedAttributeNode, true );
01504                 $translationNode->appendChild( $importedSerializedAttributeNode );
01505             }
01506 
01507             $versionNode->appendChild( $translationNode );
01508             $exportedLanguages[] = $language;
01509         }
01510 
01511         $nodeAssignmentListNode = $dom->createElementNS( 'http://ez.no/object/', 'ezobject:node-assignment-list' );
01512         $versionNode->appendChild( $nodeAssignmentListNode );
01513 
01514         $contentNodeArray = eZContentObjectTreeNode::fetchByContentObjectID( $this->ContentObjectID, true, $this->Version );
01515         foreach( $contentNodeArray as $contentNode )
01516         {
01517             $contentNodeDOMNode = $contentNode->serialize( $options, $contentNodeIDArray, $topNodeIDArray );
01518             if ( $contentNodeDOMNode !== false )
01519             {
01520                 $importedContentDOMNode = $dom->importNode( $contentNodeDOMNode, true );
01521                 $nodeAssignmentListNode->appendChild( $importedContentDOMNode );
01522             }
01523         }
01524         $initialLanguage = $this->attribute( 'initial_language' );
01525         $initialLanguageCode = $initialLanguage->attribute( 'locale' );
01526         if ( in_array( $initialLanguageCode, $exportedLanguages ) )
01527         {
01528             $versionNode->setAttribute( 'initial_language', $initialLanguageCode );
01529         }
01530 
01531         if ( $options['related_objects'] === 'selected' )
01532         {
01533             $relatedObjectArray = $contentObject->relatedContentObjectList( $this->Version, $contentObject->ID, 0, false,
01534                                                                              array( 'AllRelations' => eZContentObject::RELATION_COMMON ) );
01535             if ( count( $relatedObjectArray ) )
01536             {
01537                 $relationListNode = $dom->createElementNS( 'http://ez.no/object/', 'ezobject:object-relation-list' );
01538 
01539                 foreach( $relatedObjectArray as $relatedObject )
01540                 {
01541                     $relatedObjectRemoteID = $relatedObject->attribute( 'remote_id' );
01542 
01543                     $relationNode = $dom->createElement( 'related-object-remote-id' );
01544                     $relationNode->appendChild( $dom->createTextNode( $relatedObjectRemoteID ) );
01545 
01546                     $relationListNode->appendChild( $relationNode );
01547                 }
01548                 $versionNode->appendChild( $relationListNode );
01549             }
01550         }
01551 
01552         $db->commit();
01553         return $versionNode;
01554     }
01555 
01556     /*!
01557      \return the creator of the current version.
01558     */
01559     function creator()
01560     {
01561         if ( isset( $this->CreatorID ) and $this->CreatorID )
01562         {
01563             //include_once( 'kernel/classes/datatypes/ezuser/ezuser.php' );
01564             return eZContentObject::fetch( $this->CreatorID );
01565         }
01566         return null;
01567     }
01568 
01569     /*!
01570      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
01571      the calls within a db transaction; thus within db->begin and db->commit.
01572      */
01573     function unpublish()
01574     {
01575         if ( $this->attribute( 'status' ) == eZContentObjectVersion::STATUS_PUBLISHED )
01576         {
01577             $this->setAttribute( 'status', eZContentObjectVersion::STATUS_ARCHIVED );
01578             $parentNodeList = $this->attribute( 'parent_nodes' );
01579             $parentNodeIDList = array();
01580             foreach( $parentNodeList as $parentNode )
01581             {
01582                 $parentNodeIDList[] = $parentNode->attribute( 'parent_node' );
01583             }
01584             if ( count( $parentNodeIDList ) == 0 )
01585             {
01586                 eZDebug::writeWarning( $this, "unable to get parent nodes for version" );
01587                 return;
01588             }
01589             $parentNodeIDString = implode( ',' , $parentNodeIDList );
01590             $contentObjectID = $this->attribute( 'contentobject_id' );
01591             $version = $this->attribute( 'version' );
01592             $db = eZDB::instance();
01593             $query = "update ezcontentobject_tree
01594                       set contentobject_is_published = '0'
01595                       where parent_node_id in ( $parentNodeIDString ) and
01596                             contentobject_id = $contentObjectID and
01597                             contentobject_version = $version" ;
01598             $db->query( $query );
01599         }
01600         else
01601         {
01602             eZDebug::writeWarning( $this, "trying to unpublish non published version" );
01603         }
01604 
01605     }
01606 
01607     /*!
01608      \returns an array with locale objects, these objects represents the languages the content objects are allowed to be translated into.
01609               The array will not include locales that has been translated in this version.
01610     */
01611     function nonTranslationList()
01612     {
01613         $translationList = eZContentObject::translationList();
01614         if ( $translationList === null )
01615         {
01616             $retValue = null;
01617             return $retValue;
01618         }
01619 
01620         $translations = $this->translations( false );
01621         $nonTranslationList = array();
01622         foreach ( $translationList as $translationItem )
01623         {
01624             $locale = $translationItem->attribute( 'locale_code' );
01625             if ( !in_array( $locale, $translations ) )
01626                 $nonTranslationList[] = $translationItem;
01627         }
01628         return $nonTranslationList;
01629     }
01630 
01631     function languageMask()
01632     {
01633         return (int)$this->attribute( 'language_mask' );
01634     }
01635 
01636     function updateLanguageMask( $mask = false, $forceStore = true )
01637     {
01638         if ( $mask == false )
01639         {
01640             $mask = eZContentLanguage::maskByLocale( $this->translationList( false, false ), true );
01641         }
01642 
01643         $this->setAttribute( 'language_mask', $mask );
01644 
01645         if ( $forceStore )
01646         {
01647             $this->store();
01648         }
01649     }
01650 
01651     function initialLanguage()
01652     {
01653         return eZContentLanguage::fetch( $this->InitialLanguageID );
01654     }
01655 
01656     function initialLanguageCode()
01657     {
01658         $initialLanguage = $this->initialLanguage();
01659 
01660         $initialLanguageCode = $initialLanguage !== false ?  $initialLanguage->attribute( 'locale' ) : false;
01661 
01662         return $initialLanguageCode;
01663     }
01664 
01665     function nonTranslatableAttributesToUpdate( )
01666     {
01667         $object = $this->contentObject();
01668         $version = $this->attribute( 'version' );
01669         $objectID = $object->attribute( 'id' );
01670         $initialLanguageID = $object->attribute( 'initial_language_id' );
01671         $db = eZDB::instance();
01672 
01673         $attributeRows = $db->arrayQuery( "SELECT ezcontentobject_attribute.id, ezcontentobject_attribute.version
01674             FROM ezcontentobject_version,
01675                  ezcontentobject_attribute,
01676                 ezcontentclass_attribute
01677             WHERE
01678                     ezcontentobject_version.contentobject_id='$objectID'
01679                 AND ( ezcontentobject_version.status in ( " .
01680                       eZContentObjectVersion::STATUS_DRAFT . ", " . eZContentObjectVersion::STATUS_PENDING . ", " . eZContentObjectVersion::STATUS_INTERNAL_DRAFT .
01681                       " ) OR ( ezcontentobject_version.status = '1' AND ezcontentobject_version.version = '$version' ) )
01682                 AND ezcontentobject_attribute.contentobject_id=ezcontentobject_version.contentobject_id
01683                 AND ezcontentobject_attribute.version=ezcontentobject_version.version
01684                 AND ezcontentobject_attribute.language_id!='$initialLanguageID'
01685                 AND ezcontentobject_attribute.contentclassattribute_id=ezcontentclass_attribute.id
01686                 AND ezcontentclass_attribute.can_translate=0" );
01687 
01688         $attributes = array();
01689         foreach( $attributeRows as $row )
01690         {
01691             $attributes[] = eZContentObjectAttribute::fetch( $row['id'], $row['version'] );
01692         }
01693         return $attributes;
01694     }
01695 
01696     function setAlwaysAvailableLanguageID( $languageID )
01697     {
01698         $db = eZDB::instance();
01699         $db->begin();
01700 
01701         $objectID = $this->attribute( 'contentobject_id' );
01702         $version = $this->attribute( 'version' );
01703 
01704         // reset 'always available' flag
01705         $sql = "UPDATE ezcontentobject_attribute SET language_id=";
01706         if ( $db->databaseName() == 'oracle' )
01707         {
01708             $sql .= "bitand( language_id, -2 )";
01709         }
01710         else
01711         {
01712             $sql .= "language_id & ~1";
01713         }
01714         $sql .= " WHERE contentobject_id = '$objectID' AND version = '$version'";
01715         $db->query( $sql );
01716 
01717         if ( $languageID != false )
01718         {
01719             $newLanguageID = $languageID | 1;
01720 
01721             $sql = "UPDATE ezcontentobject_attribute
01722                     SET language_id='$newLanguageID'
01723                 WHERE language_id='$languageID' AND contentobject_id = '$objectID' AND version = '$version'";
01724             $db->query( $sql );
01725         }
01726 
01727         $db->commit();
01728     }
01729 
01730     function clearAlwaysAvailableLanguageID()
01731     {
01732         $this->setAlwaysAvailableLanguageID( false );
01733     }
01734 
01735     // Checks if there is another version (published or archived status) which has higher modification time than the
01736     // current version creation time.
01737     // Typically this function can be used before object's publishing to prevent conflicts.
01738     //
01739     // \return  array of version objects that caused conflict or false.
01740     function hasConflicts( $editLanguage = false )
01741     {
01742         $object = $this->contentObject();
01743         if ( !$editLanguage )
01744             $editLanguage = $this->initialLanguageCode();
01745 
01746         // Get versions (with published or archived status)
01747         $versions = $object->versions( true, array( 'conditions' => array( 'status' => array( array( eZContentObjectVersion::STATUS_PUBLISHED, eZContentObjectVersion::STATUS_ARCHIVED ) ),
01748                                                                            'language_code' => $editLanguage ) ) );
01749 
01750         $conflictVersions = array();
01751         foreach ( array_keys( $versions ) as $key )
01752         {
01753             $version =& $versions[$key];
01754             if ( $version->attribute( 'modified' ) > $this->attribute( 'created' ) )
01755             {
01756                 $conflictVersions[] = $version;
01757             }
01758         }
01759         if ( !count( $conflictVersions ) )
01760         {
01761             return false;
01762         }
01763 
01764         return $conflictVersions;
01765     }
01766 
01767     public function store( $fieldFilters = null )
01768     {
01769         eZContentObject::clearCache( $this->attribute( 'contentobject_id' ) );
01770         parent::store( $fieldFilters );
01771     }
01772 
01773     public $CurrentLanguage = false;
01774 }
01775 
01776 ?>