eZ Publish  [trunk]
ezobjectrelationlisttype.php
Go to the documentation of this file.
00001 <?php
00002 /**
00003  * File containing the eZObjectRelationListType class.
00004  *
00005  * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved.
00006  * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2
00007  * @version //autogentag//
00008  * @package kernel
00009  */
00010 
00011 /*!
00012   \class eZObjectRelationListType ezobjectrelationlisttype.php
00013   \ingroup eZDatatype
00014   \brief A content datatype which handles object relations
00015 
00016 Bugs/missing/deprecated features:
00017 - No identifier support yet
00018 - Validation and fixup for "Add new object" functionality
00019 - Proper embed views for admin classes
00020 - No translation page support yet (maybe?)
00021 - is_modified is deprecated and is used for BC only.
00022 
00023 */
00024 
00025 class eZObjectRelationListType extends eZDataType
00026 {
00027     const DATA_TYPE_STRING = "ezobjectrelationlist";
00028 
00029     /*!
00030      Initializes with a string id and a description.
00031     */
00032     function eZObjectRelationListType()
00033     {
00034         $this->eZDataType( self::DATA_TYPE_STRING, ezpI18n::tr( 'kernel/classes/datatypes', "Object relations", 'Datatype name' ),
00035                            array( 'serialize_supported' => true ) );
00036     }
00037 
00038     /*!
00039      Validates the input and returns true if the input was
00040      valid for this datatype.
00041     */
00042     function validateObjectAttributeHTTPInput( $http, $base, $contentObjectAttribute )
00043     {
00044         $postVariableName = $base . "_data_object_relation_list_" . $contentObjectAttribute->attribute( "id" );
00045         if ( $http->hasPostVariable( $postVariableName ) && !( $contentObjectAttribute->validateIsRequired() && $http->postVariable( $postVariableName ) == array( "no_relation" ) ) )
00046         {
00047             return eZInputValidator::STATE_ACCEPTED;
00048         }
00049 
00050         $contentClassAttribute = $contentObjectAttribute->contentClassAttribute();
00051 
00052         // Check if selection type is not browse
00053         $classContent = $contentClassAttribute->content();
00054 
00055         if ( $classContent['selection_type'] != 0 )
00056         {
00057             if (
00058                 $contentObjectAttribute->validateIsRequired()
00059                 && ( !$http->hasPostVariable( $postVariableName ) || $http->postVariable( $postVariableName ) == array( "no_relation" ) )
00060             )
00061             {
00062                 $contentObjectAttribute->setValidationError(
00063                     ezpI18n::tr(
00064                         'kernel/classes/datatypes',
00065                         'Missing objectrelation list input.'
00066                     )
00067                 );
00068                 return eZInputValidator::STATE_INVALID;
00069             }
00070             return eZInputValidator::STATE_ACCEPTED;
00071         }
00072 
00073         // The following code is only there for the support of [BackwardCompatibilitySettings]/AdvancedObjectRelationList
00074         // which happens only when $classContent['selection_type'] == 0
00075         $content = $contentObjectAttribute->content();
00076         if ( $contentObjectAttribute->validateIsRequired() && empty( $content['relation_list'] ) )
00077         {
00078             $contentObjectAttribute->setValidationError(
00079                 ezpI18n::tr(
00080                     'kernel/classes/datatypes',
00081                     'Missing objectrelation list input.'
00082                 )
00083             );
00084             return eZInputValidator::STATE_INVALID;
00085         }
00086 
00087         $status = eZInputValidator::STATE_ACCEPTED;
00088         $inputParameters = $contentObjectAttribute->inputParameters();
00089         $parameters = $contentObjectAttribute->validationParameters();
00090         if ( isset( $parameters['prefix-name'] ) && $parameters['prefix-name'] )
00091             $parameters['prefix-name'][] = $contentClassAttribute->attribute( 'name' );
00092         else
00093             $parameters['prefix-name'] = array( $contentClassAttribute->attribute( 'name' ) );
00094 
00095         foreach ( $content['relation_list'] as $relationItem )
00096         {
00097             if ( !$relationItem['is_modified'] )
00098             {
00099                 continue;
00100             }
00101 
00102             $subObjectID = $relationItem['contentobject_id'];
00103             $object = eZContentObject::fetch( $subObjectID );
00104 
00105             if ( !$object )
00106             {
00107                 continue;
00108             }
00109 
00110             $attributes = $object->contentObjectAttributes(
00111                 true,
00112                 $relationItem['contentobject_version'],
00113                 $contentObjectAttribute->attribute( 'language_code' )
00114             );
00115 
00116             $validationResult = $object->validateInput(
00117                 $attributes,
00118                 $base . '_ezorl_edit_object_' . $subObjectID,
00119                 $inputParameters,
00120                 $parameters
00121             );
00122             $content['temp'][$subObjectID] = array(
00123                 'require-fixup' => $validationResult['require-fixup'],
00124                 'attributes' => $attributes,
00125                 'object' => $object,
00126             );
00127             foreach ( $validationResult['status-map'] as $statusItem )
00128             {
00129                 $statusValue = $statusItem['value'];
00130                 if ( $statusValue == eZInputValidator::STATE_INTERMEDIATE && $status == eZInputValidator::STATE_ACCEPTED )
00131                 {
00132                     $status = eZInputValidator::STATE_INTERMEDIATE;
00133                 }
00134                 else if ( $statusValue == eZInputValidator::STATE_INVALID )
00135                 {
00136                     $contentObjectAttribute->setHasValidationError( false );
00137                     $status = eZInputValidator::STATE_INVALID;
00138                 }
00139             }
00140         }
00141 
00142         $contentObjectAttribute->setContent( $content );
00143         return $status;
00144     }
00145 
00146     /*!
00147      Validates the input and returns true if the input was
00148      valid for this datatype.
00149     */
00150     function fixupObjectAttributeHTTPInput( $http, $base, $contentObjectAttribute )
00151     {
00152         $content = $contentObjectAttribute->content();
00153         for ( $i = 0; $i < count( $content['relation_list'] ); ++$i )
00154         {
00155             $relationItem = $content['relation_list'][$i];
00156             if ( $relationItem['is_modified'] )
00157             {
00158                 $subObjectID = $relationItem['contentobject_id'];
00159                 $attributeBase = $base . '_ezorl_edit_object_' . $subObjectID;
00160                 $object = $content['temp'][$subObjectID]['object'];
00161                 $requireFixup = $content['temp'][$subObjectID]['require-fixup'];
00162                 if ( $object and
00163                      $requireFixup )
00164                 {
00165                     $attributes = $content['temp'][$subObjectID]['attributes'];
00166                     $object->fixupInput( $attributes, $attributeBase );
00167                 }
00168             }
00169         }
00170     }
00171 
00172     /*!
00173      Fetches the http post var string input and stores it in the data instance.
00174     */
00175     function fetchObjectAttributeHTTPInput( $http, $base, $contentObjectAttribute )
00176     {
00177         $content = $contentObjectAttribute->content();
00178         // new object creation
00179         $newObjectPostVariableName = "attribute_" . $contentObjectAttribute->attribute( "id" ) . "_new_object_name";
00180         if ( $http->hasPostVariable( $newObjectPostVariableName ) )
00181         {
00182             $name = $http->postVariable( $newObjectPostVariableName );
00183             if ( !empty( $name ) )
00184             {
00185                 $content['new_object'] = $name;
00186             }
00187         }
00188         $singleSelectPostVariableName = "single_select_" . $contentObjectAttribute->attribute( "id" );
00189         if ( $http->hasPostVariable( $singleSelectPostVariableName ) )
00190             $content['singleselect'] = true;
00191 
00192         $postVariableName = $base . "_data_object_relation_list_" . $contentObjectAttribute->attribute( "id" );
00193         $contentClassAttribute = $contentObjectAttribute->contentClassAttribute();
00194         $classContent = $contentClassAttribute->content();
00195 
00196         $selectedObjectIDArray = $http->hasPostVariable( $postVariableName ) ? $http->postVariable( $postVariableName ) : false;
00197 
00198         // If we got an empty object id list
00199         if ( ( $selectedObjectIDArray === false && $classContent['selection_type'] != 0 ) || ( isset( $selectedObjectIDArray[0] ) && $selectedObjectIDArray[0] === 'no_relation' ) )
00200         {
00201             $content['relation_list'] = array();
00202             $contentObjectAttribute->setContent( $content );
00203             $contentObjectAttribute->store();
00204             return true;
00205         }
00206 
00207         // Check if selection type is not browse
00208         if ( $classContent['selection_type'] != 0 )
00209         {
00210             $priority = 0;
00211             $content['relation_list'] = array();
00212             foreach ( $selectedObjectIDArray as $objectID )
00213             {
00214                 // Check if the given object ID has a numeric value, if not go to the next object.
00215                 if ( !is_numeric( $objectID ) )
00216                 {
00217                     eZDebug::writeError( "Related object ID (objectID): '$objectID', is not a numeric value.", __METHOD__ );
00218 
00219                     continue;
00220                 }
00221                 ++$priority;
00222                 $content['relation_list'][] = $this->appendObject( $objectID, $priority, $contentObjectAttribute );
00223             }
00224 
00225             $contentObjectAttribute->setContent( $content );
00226             return true;
00227         }
00228 
00229         $priorities               = array();
00230         $priorityBase             = $base . '_priority';
00231         $contentObjectAttributeID = $contentObjectAttribute->attribute( 'id' );
00232         if ( $http->hasPostVariable( $priorityBase ) )
00233             $priorities = $http->postVariable( $priorityBase );
00234 
00235         // Add new relations
00236         if ( $selectedObjectIDArray )
00237         {
00238             foreach ( $selectedObjectIDArray as $x => $objectID )
00239             {
00240                 // Check if the given object ID has a numeric value, if not go to the next object.
00241                 if ( !is_numeric( $objectID ) )
00242                 {
00243                     eZDebug::writeError( "Related object ID (objectID): '$objectID', is not a numeric value.", __METHOD__ );
00244 
00245                     continue;
00246                 }
00247                 for ( $y = 0, $c = count( $content['relation_list'] ); $y < $c; ++$y )
00248                 {
00249                     if ( $objectID == $content['relation_list'][$y]['contentobject_id'] )
00250                     {
00251                         continue 2;
00252                     }
00253                 }
00254                 $content['relation_list'][] = $this->appendObject( $objectID, $priorities[$contentObjectAttributeID][$x], $contentObjectAttribute );
00255             }
00256         }
00257 
00258         $reorderedRelationList    = array();
00259         // Contains existing priorities
00260         $existsPriorities = array();
00261 
00262         for ( $i = 0, $c = count( $content['relation_list'] ); $i < $c; ++$i )
00263         {
00264             $priorities[$contentObjectAttributeID][$i] = (int) $priorities[$contentObjectAttributeID][$i];
00265             $existsPriorities[$i] = $priorities[$contentObjectAttributeID][$i];
00266 
00267             // Change objects' priorities providing their uniqueness.
00268             for ( $j = 0; $j < $c; ++$j )
00269             {
00270                 if ( $i == $j ) continue;
00271                 if ( $priorities[$contentObjectAttributeID][$i] == $priorities[$contentObjectAttributeID][$j] )
00272                 {
00273                     $index = $priorities[$contentObjectAttributeID][$i];
00274                     while ( in_array( $index, $existsPriorities ) )
00275                         ++$index;
00276                     $priorities[$contentObjectAttributeID][$j] = $index;
00277                 }
00278             }
00279             $relationItem = $content['relation_list'][$i];
00280             if ( $relationItem['is_modified'] )
00281             {
00282                 $subObjectID = $relationItem['contentobject_id'];
00283                 $attributeBase = $base . '_ezorl_edit_object_' . $subObjectID;
00284                 $object = $content['temp'][$subObjectID]['object'];
00285                 if ( $object )
00286                 {
00287                     $attributes = $content['temp'][$subObjectID]['attributes'];
00288 
00289                     $customActionAttributeArray = array();
00290                     $fetchResult = $object->fetchInput( $attributes, $attributeBase,
00291                                                         $customActionAttributeArray,
00292                                                         $contentObjectAttribute->inputParameters() );
00293                     $content['temp'][$subObjectID]['attribute-input-map'] = $fetchResult['attribute-input-map'];
00294                     $content['temp'][$subObjectID]['attributes'] = $attributes;
00295                     $content['temp'][$subObjectID]['object'] = $object;
00296                 }
00297             }
00298             if ( isset( $priorities[$contentObjectAttributeID][$i] ) )
00299                 $relationItem['priority'] = $priorities[$contentObjectAttributeID][$i];
00300             $reorderedRelationList[$relationItem['priority']] = $relationItem;
00301         }
00302         ksort( $reorderedRelationList );
00303         unset( $content['relation_list'] );
00304         $content['relation_list'] = array();
00305         reset( $reorderedRelationList );
00306         $i = 0;
00307         while ( list( $key, $relationItem ) = each( $reorderedRelationList ) )
00308         {
00309             $content['relation_list'][] = $relationItem;
00310             $content['relation_list'][$i]['priority'] = $i + 1;
00311             ++$i;
00312         }
00313         $contentObjectAttribute->setContent( $content );
00314         return true;
00315     }
00316 
00317     function createNewObject( $contentObjectAttribute, $name )
00318     {
00319         $classAttribute = $contentObjectAttribute->attribute( 'contentclass_attribute' );
00320         $classContent = $classAttribute->content();
00321         $classID = $classContent['object_class'];
00322         if ( !isset( $classID ) or !is_numeric( $classID ) )
00323             return false;
00324 
00325         $defaultPlacementNode = ( is_array( $classContent['default_placement'] ) and isset( $classContent['default_placement']['node_id'] ) ) ? $classContent['default_placement']['node_id'] : false;
00326         if ( !$defaultPlacementNode )
00327         {
00328             eZDebug::writeError( 'Default placement is missing', __METHOD__ );
00329             return false;
00330         }
00331 
00332         $node = eZContentObjectTreeNode::fetch( $defaultPlacementNode );
00333         // Check if current user can create a new node as child of this node.
00334         if ( !$node or !$node->canCreate() )
00335         {
00336             eZDebug::writeError( 'Default placement is wrong or the current user can\'t create a new node as child of this node.', __METHOD__ );
00337             return false;
00338         }
00339 
00340         $classList = $node->canCreateClassList( false );
00341         $canCreate = false;
00342         // Check if current user can create object of class (with $classID)
00343         foreach ( $classList as $class )
00344         {
00345             if ( $class['id'] == $classID )
00346             {
00347                 $canCreate = true;
00348                 break;
00349             }
00350         }
00351         if ( !$canCreate )
00352         {
00353             eZDebug::writeError( 'The current user is not allowed to create objects of class (ID=' . $classID . ')', __METHOD__ );
00354             return false;
00355         }
00356 
00357         $class = eZContentClass::fetch( $classID );
00358         if ( !$class )
00359             return false;
00360 
00361         $currentObject = $contentObjectAttribute->attribute( 'object' );
00362         $sectionID = $currentObject->attribute( 'section_id' );
00363         //instantiate object, same section, currentuser as owner (i.e. provide false as param)
00364         $newObjectInstance = $class->instantiate( false, $sectionID );
00365         $nodeassignment = $newObjectInstance->createNodeAssignment( $defaultPlacementNode, true );
00366         $nodeassignment->store();
00367         $newObjectInstance->sync();
00368         $operationResult = eZOperationHandler::execute( 'content', 'publish', array( 'object_id' => $newObjectInstance->attribute( 'id' ), 'version' => 1 ) );
00369         // so it updates the attributes
00370         $newObjectInstance->rename( $name );
00371 
00372         return $newObjectInstance->attribute( 'id' );
00373     }
00374 
00375     function storeObjectAttribute( $attribute )
00376     {
00377         $content = $attribute->content();
00378         if ( isset( $content['new_object'] ) )
00379         {
00380             $newID = $this->createNewObject( $attribute, $content['new_object'] );
00381             // if this is a single element selection mode (radio or dropdown), then the newly created item is the only one selected
00382             if ( $newID )
00383             {
00384                 if ( isset( $content['singleselect'] ) )
00385                     $content['relation_list'] = array();
00386                 $content['relation_list'][] = $this->appendObject( $newID, 0, $attribute );
00387             }
00388             unset( $content['new_object'] );
00389             $attribute->setContent( $content );
00390         }
00391 
00392         $contentClassAttributeID = $attribute->ContentClassAttributeID;
00393         $contentObjectID = $attribute->ContentObjectID;
00394         $contentObjectVersion = $attribute->Version;
00395 
00396         $obj = $attribute->object();
00397         //get eZContentObjectVersion
00398         $currVerobj = $obj->version( $contentObjectVersion );
00399 
00400         // create translation List
00401         // $translationList will contain for example eng-GB, ita-IT etc.
00402         $translationList = $currVerobj->translations( false );
00403 
00404         // get current language_code
00405         $langCode = $attribute->attribute( 'language_code' );
00406         // get count of LanguageCode in translationList
00407         $countTsl = count( $translationList );
00408         // order by asc
00409         sort( $translationList );
00410 
00411         if ( ( $countTsl == 1 ) or ( $countTsl > 1 and $translationList[0] == $langCode ) )
00412         {
00413              eZContentObject::fetch( $contentObjectID )->removeContentObjectRelation( false, $contentObjectVersion, $contentClassAttributeID, eZContentObject::RELATION_ATTRIBUTE );
00414         }
00415 
00416         foreach( $content['relation_list'] as $relationItem )
00417         {
00418             // Installing content object, postUnserialize is not called yet,
00419             // so object's ID is unknown.
00420             if ( !$relationItem['contentobject_id'] || !isset( $relationItem['contentobject_id'] ) )
00421                 continue;
00422 
00423             $subObjectID = $relationItem['contentobject_id'];
00424             $subObjectVersion = $relationItem['contentobject_version'];
00425 
00426             eZContentObject::fetch( $contentObjectID )->addContentObjectRelation( $subObjectID, $contentObjectVersion, $contentClassAttributeID, eZContentObject::RELATION_ATTRIBUTE );
00427 
00428             if ( $relationItem['is_modified'] && isset( $content['temp'][$subObjectID]['object' ] ) )
00429             {
00430                 // handling sub-objects
00431                 $object = $content['temp'][$subObjectID]['object'];
00432                 if ( $object )
00433                 {
00434                     $attributes = $content['temp'][$subObjectID]['attributes'];
00435                     $attributeInputMap = $content['temp'][$subObjectID]['attribute-input-map'];
00436                     $object->storeInput( $attributes,
00437                                          $attributeInputMap );
00438                     $version = eZContentObjectVersion::fetchVersion( $subObjectVersion, $subObjectID );
00439                     if ( $version )
00440                     {
00441                         $version->setAttribute( 'modified', time() );
00442                         $version->setAttribute( 'status', eZContentObjectVersion::STATUS_DRAFT );
00443                         $version->store();
00444                     }
00445 
00446                     $object->setAttribute( 'status', eZContentObject::STATUS_DRAFT );
00447                     $object->store();
00448                 }
00449             }
00450         }
00451         return $this->storeObjectAttributeContent( $attribute, $content );
00452     }
00453 
00454     function onPublish( $contentObjectAttribute, $contentObject, $publishedNodes )
00455     {
00456         $content = $contentObjectAttribute->content();
00457         foreach( $content['relation_list'] as $key => $relationItem )
00458         {
00459             if ( $relationItem['is_modified'] )
00460             {
00461                 $subObjectID = $relationItem['contentobject_id'];
00462                 $subObjectVersion = $relationItem['contentobject_version'];
00463                 $object = eZContentObject::fetch( $subObjectID );
00464 
00465                 $time = time();
00466 
00467                 $version = eZContentObjectVersion::fetchVersion( $subObjectVersion, $subObjectID );
00468                 $version->setAttribute( 'modified', $time );
00469                 $version->store();
00470 
00471                 if ( $relationItem['parent_node_id'] > 0 )
00472                 {
00473                     // action 1: edit a normal object
00474                     if ( !eZNodeAssignment::fetch( $object->attribute( 'id' ), $object->attribute( 'current_version' ), $relationItem['parent_node_id'], false ) )
00475                     {
00476                         $nodeAssignment = eZNodeAssignment::create( array( 'contentobject_id' => $object->attribute( 'id' ),
00477                                                                            'contentobject_version' => $object->attribute( 'current_version' ),
00478                                                                            'parent_node' => $relationItem['parent_node_id'],
00479                                                                            'sort_field' => eZContentObjectTreeNode::SORT_FIELD_PUBLISHED,
00480                                                                            'sort_order' => eZContentObjectTreeNode::SORT_ORDER_DESC,
00481                                                                            'is_main' => 1 ) );
00482                         $nodeAssignment->store();
00483                     }
00484                     $operationResult = eZOperationHandler::execute( 'content', 'publish', array( 'object_id' => $object->attribute( 'id' ),
00485                                                                                                  'version' => $subObjectVersion ) );
00486                     $objectNodeID = $object->attribute( 'main_node_id' );
00487                     $content['relation_list'][$key]['node_id'] = $objectNodeID;
00488                 }
00489                 else
00490                 {
00491                     // action 2: edit a nodeless object (or creating a new node
00492                     // Make the previous version archived
00493                     $currentVersion = $object->currentVersion();
00494                     $currentVersion->setAttribute( 'status', eZContentObjectVersion::STATUS_ARCHIVED );
00495                     $currentVersion->setAttribute( 'modified', $time );
00496                     $currentVersion->store();
00497 
00498                     $version->setAttribute( 'status', eZContentObjectVersion::STATUS_PUBLISHED );
00499                     $version->store();
00500 
00501                     $object->setAttribute( 'status', eZContentObject::STATUS_PUBLISHED );
00502                     if ( !$object->attribute( 'published' ) )
00503                         $object->setAttribute( 'published', $time );
00504                     $object->setAttribute( 'modified', $time );
00505                     $object->setAttribute( 'current_version', $subObjectVersion );
00506                     $class = $object->contentClass();
00507                     $objectName = $class->contentObjectName( $object, $version->attribute( 'version' ) );
00508                     $object->setName( $objectName, $version->attribute( 'version' ) );
00509                     $object->store();
00510 
00511                     if ( !eZNodeAssignment::fetch( $object->attribute( 'id' ), $object->attribute( 'current_version' ), $contentObject->attribute( 'main_node_id' ), false ) )
00512                     {
00513                         $nodeAssignment = eZNodeAssignment::create( array( 'contentobject_id' => $object->attribute( 'id' ),
00514                                                                            'contentobject_version' => $object->attribute( 'current_version' ),
00515                                                                            'parent_node' => $contentObject->attribute( 'main_node_id' ),
00516                                                                            'sort_field' => eZContentObjectTreeNode::SORT_FIELD_PUBLISHED,
00517                                                                            'sort_order' => eZContentObjectTreeNode::SORT_ORDER_DESC,
00518                                                                            'is_main' => 1 ) );
00519                         $nodeAssignment->store();
00520                     }
00521                 }
00522                 $content['relation_list'][$key]['is_modified'] = false;
00523             }
00524         }
00525         $this->storeObjectAttributeContent( $contentObjectAttribute, $content );
00526         $contentObjectAttribute->setContent( $content );
00527         $contentObjectAttribute->store();
00528     }
00529 
00530     function initializeObjectAttribute( $contentObjectAttribute, $currentVersion, $originalContentObjectAttribute )
00531     {
00532 
00533         static $copiedRelatedAccordance;
00534         if ( !isset( $copiedRelatedAccordance ) )
00535             $copiedRelatedAccordance = array();
00536 
00537         if ( $currentVersion != false )
00538         {
00539             $dataText = $originalContentObjectAttribute->attribute( 'data_text' );
00540             $contentObjectAttribute->setAttribute( 'data_text', $dataText );
00541             $contentObjectID = $contentObjectAttribute->attribute( 'contentobject_id' );
00542             $originalContentObjectID = $originalContentObjectAttribute->attribute( 'contentobject_id' );
00543 
00544             if ( $contentObjectID != $originalContentObjectID )
00545             {
00546                 $classContent = $this->defaultClassAttributeContent();
00547                 if ( !$classContent['default_placement'] )
00548                 {
00549                     $content = $originalContentObjectAttribute->content();
00550                     $contentModified = false;
00551 
00552                     foreach ( $content['relation_list'] as $key => $relationItem )
00553                     {
00554                         // create related object copies only if they are subobjects
00555                         $object = eZContentObject::fetch( $relationItem['contentobject_id'] );
00556                         if ( !$object instanceof eZContentObject )
00557                         {
00558                             unset( $content['relation_list'][$key] );
00559                             $contentModified = true;
00560                             continue;
00561                         }
00562 
00563                         $mainNode = $object->attribute( 'main_node' );
00564                         if ( $mainNode instanceof eZContentObjectTreeNode )
00565                         {
00566                             $node = ( is_numeric( $relationItem['node_id'] ) and $relationItem['node_id'] ) ?
00567                                       eZContentObjectTreeNode::fetch( $relationItem['node_id'] ) : null;
00568 
00569                             if ( !$node or $node->attribute( 'contentobject_id' ) != $relationItem['contentobject_id'] )
00570                             {
00571                                 $content['relation_list'][$key]['node_id'] = $mainNode->attribute( 'node_id' );
00572                                 $node = $mainNode;
00573                                 $contentModified = true;
00574                             }
00575 
00576                             if ( $node instanceof eZContentObjectTreeNode )
00577                                 $parentNodeID =  $node->attribute( 'parent_node_id' );
00578                             else
00579                                 $parentNodeID = -1;
00580 
00581                             if ( $relationItem['parent_node_id'] != $parentNodeID )
00582                             {
00583                                 $content['relation_list'][$key]['parent_node_id'] = $parentNodeID;
00584                                 $contentModified = true;
00585                             }
00586                         }
00587                         else
00588                         {
00589                             if ( !isset( $copiedRelatedAccordance[ $relationItem['contentobject_id'] ] ) )
00590                                 $copiedRelatedAccordance[ $relationItem['contentobject_id'] ] = array();
00591 
00592                             if ( isset( $copiedRelatedAccordance[ $relationItem['contentobject_id'] ] ) and
00593                                  isset( $copiedRelatedAccordance[ $relationItem['contentobject_id'] ][ $contentObjectID ] ) )
00594                             {
00595                                 $newObjectID = $copiedRelatedAccordance[ $relationItem['contentobject_id'] ][ $contentObjectID ][ 'to' ];
00596                             }
00597                             else
00598                             {
00599                                 $newObject = $object->copy( true );
00600                                 $newObjectID = $newObject->attribute( 'id' );
00601                                 $copiedRelatedAccordance[ $relationItem['contentobject_id'] ][ $contentObjectID ] = array( 'to' => $newObjectID,
00602                                                                                                                            'from' => $originalContentObjectID );
00603                             }
00604                             $content['relation_list'][$key]['contentobject_id'] = $newObjectID;
00605                             $contentModified = true;
00606                         }
00607                     }
00608 
00609                     if ( $contentModified )
00610                     {
00611                         $contentObjectAttribute->setContent( $content );
00612                         $contentObjectAttribute->store();
00613                     }
00614                 }
00615             }
00616         }
00617     }
00618 
00619     function validateClassAttributeHTTPInput( $http, $base, $classAttribute )
00620     {
00621         return eZInputValidator::STATE_ACCEPTED;
00622     }
00623 
00624     function fixupClassAttributeHTTPInput( $http, $base, $classAttribute )
00625     {
00626     }
00627 
00628     function fetchClassAttributeHTTPInput( $http, $base, $classAttribute )
00629     {
00630         $content = $classAttribute->content();
00631         $postVariable = 'ContentClass_ezobjectrelationlist_class_list_' . $classAttribute->attribute( 'id' );
00632         if ( $http->hasPostVariable( $postVariable ) )
00633         {
00634             $constrainedList = $http->postVariable( $postVariable );
00635             $constrainedClassList = array();
00636             foreach ( $constrainedList as $constraint )
00637             {
00638                 if ( trim( $constraint ) != '' )
00639                     $constrainedClassList[] = $constraint;
00640             }
00641             $content['class_constraint_list'] = $constrainedClassList;
00642         }
00643         $typeVariable = 'ContentClass_ezobjectrelationlist_type_' . $classAttribute->attribute( 'id' );
00644         if ( $http->hasPostVariable( $typeVariable ) )
00645         {
00646             $type = $http->postVariable( $typeVariable );
00647             $content['type'] = $type;
00648         }
00649         $selectionTypeVariable = 'ContentClass_ezobjectrelationlist_selection_type_' . $classAttribute->attribute( 'id' );
00650         if ( $http->hasPostVariable( $selectionTypeVariable ) )
00651         {
00652             $selectionType = $http->postVariable( $selectionTypeVariable );
00653             $content['selection_type'] = $selectionType;
00654         }
00655         $objectClassVariable = 'ContentClass_ezobjectrelation_object_class_' . $classAttribute->attribute( 'id' );
00656         if ( $http->hasPostVariable( $objectClassVariable ) )
00657         {
00658             $content['object_class'] = $http->postVariable( $objectClassVariable );
00659         }
00660 
00661         $classAttribute->setContent( $content );
00662         $classAttribute->store();
00663         return true;
00664     }
00665 
00666     function initializeClassAttribute( $classAttribute )
00667     {
00668         $xmlText = $classAttribute->attribute( 'data_text5' );
00669         if ( trim( $xmlText ) == '' )
00670         {
00671             $content = $this->defaultClassAttributeContent();
00672             return $this->storeClassAttributeContent( $classAttribute, $content );
00673         }
00674     }
00675 
00676     function preStoreClassAttribute( $classAttribute, $version )
00677     {
00678         $content = $classAttribute->content();
00679         return $this->storeClassAttributeContent( $classAttribute, $content );
00680     }
00681 
00682     function storeClassAttributeContent( $classAttribute, $content )
00683     {
00684         if ( is_array( $content ) )
00685         {
00686             $doc = $this->createClassDOMDocument( $content );
00687             $this->storeClassDOMDocument( $doc, $classAttribute );
00688             return true;
00689         }
00690         return false;
00691     }
00692 
00693     function storeObjectAttributeContent( $objectAttribute, $content )
00694     {
00695         if ( is_array( $content ) )
00696         {
00697             $doc = $this->createObjectDOMDocument( $content );
00698             $this->storeObjectDOMDocument( $doc, $objectAttribute );
00699             return true;
00700         }
00701         return false;
00702     }
00703 
00704     static function storeClassDOMDocument( $doc, $classAttribute )
00705     {
00706         $docText = self::domString( $doc );
00707         $classAttribute->setAttribute( 'data_text5', $docText );
00708     }
00709 
00710     static function storeObjectDOMDocument( $doc, $objectAttribute )
00711     {
00712         $docText = self::domString( $doc );
00713         $objectAttribute->setAttribute( 'data_text', $docText );
00714     }
00715 
00716     /*!
00717      \static
00718      \return the XML structure in \a $domDocument as text.
00719              It will take of care of the necessary charset conversions
00720              for content storage.
00721     */
00722     static function domString( $domDocument )
00723     {
00724         $ini = eZINI::instance();
00725         $xmlCharset = $ini->variable( 'RegionalSettings', 'ContentXMLCharset' );
00726         if ( $xmlCharset == 'enabled' )
00727         {
00728             $charset = eZTextCodec::internalCharset();
00729         }
00730         else if ( $xmlCharset == 'disabled' )
00731             $charset = true;
00732         else
00733             $charset = $xmlCharset;
00734         if ( $charset !== true )
00735         {
00736             $charset = eZCharsetInfo::realCharsetCode( $charset );
00737         }
00738         $domString = $domDocument->saveXML();
00739         return $domString;
00740     }
00741 
00742     static function createClassDOMDocument( $content )
00743     {
00744         $doc = new DOMDocument( '1.0', 'utf-8' );
00745         $root = $doc->createElement( 'related-objects' );
00746         $constraints = $doc->createElement( 'constraints' );
00747         foreach ( $content['class_constraint_list'] as $constraintClassIdentifier )
00748         {
00749             unset( $constraintElement );
00750             $constraintElement = $doc->createElement( 'allowed-class' );
00751             $constraintElement->setAttribute( 'contentclass-identifier', $constraintClassIdentifier );
00752             $constraints->appendChild( $constraintElement );
00753         }
00754         $root->appendChild( $constraints );
00755         $constraintType = $doc->createElement( 'type' );
00756         $constraintType->setAttribute( 'value', $content['type'] );
00757         $root->appendChild( $constraintType );
00758         $selectionType = $doc->createElement( 'selection_type' );
00759         $selectionType->setAttribute( 'value', $content['selection_type'] );
00760         $root->appendChild( $selectionType );
00761         $objectClass = $doc->createElement( 'object_class' );
00762         $objectClass->setAttribute( 'value', $content['object_class'] );
00763         $root->appendChild( $objectClass );
00764 
00765         $placementNode = $doc->createElement( 'contentobject-placement' );
00766         if ( $content['default_placement'] )
00767         {
00768             $placementNode->setAttribute( 'node-id',  $content['default_placement']['node_id'] );
00769         }
00770         $root->appendChild( $placementNode );
00771         $doc->appendChild( $root );
00772         return $doc;
00773     }
00774 
00775     static function createObjectDOMDocument( $content )
00776     {
00777         $doc = new DOMDocument( '1.0', 'utf-8' );
00778         $root = $doc->createElement( 'related-objects' );
00779         $relationList = $doc->createElement( 'relation-list' );
00780         $attributeDefinitions = self::contentObjectArrayXMLMap();
00781 
00782         foreach ( $content['relation_list'] as $relationItem )
00783         {
00784             unset( $relationElement );
00785             $relationElement = $doc->createElement( 'relation-item' );
00786 
00787             foreach ( $attributeDefinitions as $attributeXMLName => $attributeKey )
00788             {
00789                 if ( isset( $relationItem[$attributeKey] ) && $relationItem[$attributeKey] !== false )
00790                 {
00791                     $value = $relationItem[$attributeKey];
00792                     $relationElement->setAttribute( $attributeXMLName, $value );
00793                 }
00794             }
00795 
00796             $relationList->appendChild( $relationElement );
00797         }
00798         $root->appendChild( $relationList );
00799         $doc->appendChild( $root );
00800         return $doc;
00801     }
00802 
00803     static function contentObjectArrayXMLMap()
00804     {
00805         return array( 'identifier' => 'identifier',
00806                       'priority' => 'priority',
00807                       'in-trash' => 'in_trash',
00808                       'contentobject-id' => 'contentobject_id',
00809                       'contentobject-version' => 'contentobject_version',
00810                       'node-id' => 'node_id',
00811                       'parent-node-id' => 'parent_node_id',
00812                       'contentclass-id' => 'contentclass_id',
00813                       'contentclass-identifier' => 'contentclass_identifier',
00814                       'is-modified' => 'is_modified',
00815                       'contentobject-remote-id' => 'contentobject_remote_id' );
00816     }
00817 
00818     function deleteStoredObjectAttribute( $objectAttribute, $version = null )
00819     {
00820         $content = $objectAttribute->content();
00821         if ( is_array( $content ) and
00822              is_array( $content['relation_list'] ) )
00823         {
00824             $db = eZDB::instance();
00825             $db->begin();
00826             foreach ( $content['relation_list'] as $deletionItem )
00827             {
00828                 $this->removeRelationObject( $objectAttribute, $deletionItem );
00829             }
00830             $db->commit();
00831         }
00832     }
00833 
00834     function customObjectAttributeHTTPAction( $http, $action, $contentObjectAttribute, $parameters )
00835     {
00836         $contentobjectID = false;
00837         if ( eZDataType::fetchActionValue( $action, 'new_class', $classID ) or
00838              $action == 'new_class' )
00839         {
00840             if ( $action == 'new_class' )
00841             {
00842                 $base = $parameters['base_name'];
00843                 $classVariableName = $base . '_new_class';
00844                 if ( $http->hasPostVariable( $classVariableName ) )
00845                 {
00846                     $classVariable = $http->postVariable( $classVariableName );
00847                     $classID = $classVariable[$contentObjectAttribute->attribute( 'id' )];
00848                     $class = eZContentClass::fetch( $classID );
00849                 }
00850                 else
00851                     return false;
00852             }
00853             else
00854                 $class = eZContentClass::fetch( $classID );
00855             if ( $class )
00856             {
00857                 $classAttribute = $contentObjectAttribute->attribute( 'contentclass_attribute' );
00858                 $class_content = $classAttribute->content();
00859                 $content = $contentObjectAttribute->content();
00860                 $priority = 0;
00861                 for ( $i = 0; $i < count( $content['relation_list'] ); ++$i )
00862                 {
00863                     if ( $content['relation_list'][$i]['priority'] > $priority )
00864                         $priority = $content['relation_list'][$i]['priority'];
00865                 }
00866 
00867                 $base = $parameters['base_name'];
00868                 $nodePlacement = false;
00869                 $nodePlacementName = $base . '_object_initial_node_placement';
00870                 if ( $http->hasPostVariable( $nodePlacementName ) )
00871                 {
00872                     $nodePlacementMap = $http->postVariable( $nodePlacementName );
00873                     if ( isset( $nodePlacementMap[$contentObjectAttribute->attribute( 'id' )] ) )
00874                         $nodePlacement = $nodePlacementMap[$contentObjectAttribute->attribute( 'id' )];
00875                 }
00876                 $relationItem = $this->createInstance( $class,
00877                                                        $priority + 1,
00878                                                        $contentObjectAttribute,
00879                                                        $nodePlacement );
00880                 if ( $class_content['default_placement'] )
00881                 {
00882                     $relationItem['parent_node_id'] = $class_content['default_placement']['node_id'];
00883                 }
00884 
00885                 $content['relation_list'][] = $relationItem;
00886 
00887                 $hasAttributeInput = false;
00888                 $attributeInputVariable = $base . '_has_attribute_input';
00889                 if ( $http->hasPostVariable( $attributeInputVariable ) )
00890                 {
00891                     $attributeInputMap = $http->postVariable( $attributeInputVariable );
00892                     if ( isset( $attributeInputMap[$contentObjectAttribute->attribute( 'id' )] ) )
00893                         $hasAttributeInput = $attributeInputMap[$contentObjectAttribute->attribute( 'id' )];
00894                 }
00895 
00896                 if ( $hasAttributeInput )
00897                 {
00898                     $object = $relationItem['object'];
00899                     $attributes = $object->contentObjectAttributes();
00900                     foreach ( $attributes as $attribute )
00901                     {
00902                         $attributeBase = $base . '_ezorl_init_class_' . $object->attribute( 'contentclass_id' ) . '_attr_' . $attribute->attribute( 'contentclassattribute_id' );
00903                         $oldAttributeID = $attribute->attribute( 'id' );
00904                         $attribute->setAttribute( 'id', false );
00905                         if ( $attribute->fetchInput( $http, $attributeBase ) )
00906                         {
00907                             $attribute->setAttribute( 'id', $oldAttributeID );
00908                             $attribute->store();
00909                         }
00910                     }
00911                 }
00912 
00913                 $contentObjectAttribute->setContent( $content );
00914                 $contentObjectAttribute->store();
00915             }
00916             else
00917 
00918                 eZDebug::writeError( "Unknown class ID $classID, cannot instantiate object", __METHOD__ );
00919         }
00920         else if ( eZDataType::fetchActionValue( $action, 'edit_objects', $contentobjectID ) or
00921                   $action == 'edit_objects' or
00922                   $action == 'remove_objects' )
00923         {
00924             $base = $parameters['base_name'];
00925             $selectionBase = $base . '_selection';
00926             $selections = array();
00927             $http = eZHTTPTool::instance();
00928             if ( $http->hasPostVariable( $selectionBase ) )
00929             {
00930                 $selectionMap = $http->postVariable( $selectionBase );
00931                 $selections = $selectionMap[$contentObjectAttribute->attribute( 'id' )];
00932             }
00933             if ( $contentobjectID !== false )
00934                 $selections[] = $contentobjectID;
00935             if ( $action == 'edit_objects' or
00936                  eZDataType::fetchActionValue( $action, 'edit_objects', $contentobjectID ) )
00937             {
00938                 $content = $contentObjectAttribute->content();
00939                 foreach ( $content['relation_list'] as $key => $relationItem )
00940                 {
00941                     if ( !$relationItem['is_modified'] and
00942                          in_array( $relationItem['contentobject_id'], $selections ) )
00943                     {
00944                         $object = eZContentObject::fetch( $relationItem['contentobject_id'] );
00945                         if ( $object->attribute( 'can_edit' ) )
00946                         {
00947                             $content['relation_list'][$key]['is_modified'] = true;
00948 
00949                             $translationSourceBase = $base . '_translation_source_' .
00950                                                      $contentObjectAttribute->attribute( 'id' ) . '_' .
00951                                                      $relationItem['contentobject_id'];
00952                             $languageFrom = false;
00953                             if( $http->hasPostVariable( $translationSourceBase ) &&
00954                                     $http->postVariable( $translationSourceBase ) !== '' )
00955                             {
00956                                 $languageFrom = $http->postVariable( $translationSourceBase );
00957                             }
00958 
00959                             $version = $object->createNewVersionIn( $contentObjectAttribute->attribute( 'language_code' ), $languageFrom );
00960                             $content['relation_list'][$key]['contentobject_version'] = $version->attribute( 'version' );
00961                         }
00962                     }
00963                 }
00964                 $contentObjectAttribute->setContent( $content );
00965                 $contentObjectAttribute->store();
00966             }
00967             else if ( $action == 'remove_objects' )
00968             {
00969                 $content = $contentObjectAttribute->content();
00970                 $relationList = $content['relation_list'];
00971                 $newRelationList = array();
00972                 foreach( $relationList as $relationItem )
00973                 {
00974                     if ( in_array( $relationItem['contentobject_id'], $selections ) )
00975                     {
00976                         $this->removeRelationObject( $contentObjectAttribute, $relationItem );
00977                     }
00978                     else
00979                     {
00980                         $newRelationList[] = $relationItem;
00981                     }
00982                 }
00983                 $content['relation_list'] = $newRelationList;
00984                 $contentObjectAttribute->setContent( $content );
00985                 $contentObjectAttribute->store();
00986             }
00987         }
00988         else if ( $action == 'browse_objects' )
00989         {
00990             $module = $parameters['module'];
00991             $redirectionURI = $parameters['current-redirection-uri'];
00992 
00993             $ini = eZINI::instance( 'content.ini' );
00994             $browseType = 'AddRelatedObjectListToDataType';
00995             $browseTypeINIVariable = $ini->variable( 'ObjectRelationDataTypeSettings', 'ClassAttributeStartNode' );
00996             foreach ( $browseTypeINIVariable as $value )
00997             {
00998                 list( $classAttributeID, $type ) = explode( ';',$value );
00999                 if ( is_numeric( $classAttributeID ) and
01000                      $classAttributeID == $contentObjectAttribute->attribute( 'contentclassattribute_id' ) and
01001                      strlen( $type ) > 0 )
01002                 {
01003                     $browseType = $type;
01004                     break;
01005                 }
01006             }
01007 
01008             // Fetch the list of "allowed" classes .
01009             // A user can select objects of only those allowed classes when browsing.
01010             $classAttribute = $contentObjectAttribute->attribute( 'contentclass_attribute' );
01011             $classContent   = $classAttribute->content();
01012             if ( isset( $classContent['class_constraint_list'] ) )
01013             {
01014                 $classConstraintList = $classContent['class_constraint_list'];
01015             }
01016             else
01017             {
01018                 $classConstraintList = array();
01019             }
01020 
01021             $browseParameters = array( 'action_name' => 'AddRelatedObject_' . $contentObjectAttribute->attribute( 'id' ),
01022                                        'type' =>  $browseType,
01023                                        'browse_custom_action' => array( 'name' => 'CustomActionButton[' . $contentObjectAttribute->attribute( 'id' ) . '_set_object_relation_list]',
01024                                                                         'value' => $contentObjectAttribute->attribute( 'id' ) ),
01025                                        'persistent_data' => array( 'HasObjectInput' => 0 ),
01026                                        'from_page' => $redirectionURI );
01027             $base = $parameters['base_name'];
01028             $nodePlacementName = $base . '_browse_for_object_start_node';
01029             if ( $http->hasPostVariable( $nodePlacementName ) )
01030             {
01031                 $nodePlacement = $http->postVariable( $nodePlacementName );
01032                 if ( isset( $nodePlacement[$contentObjectAttribute->attribute( 'id' )] ) )
01033                     $browseParameters['start_node'] = eZContentBrowse::nodeAliasID( $nodePlacement[$contentObjectAttribute->attribute( 'id' )] );
01034             }
01035             if ( count($classConstraintList) > 0 )
01036                 $browseParameters['class_array'] = $classConstraintList;
01037 
01038             eZContentBrowse::browse( $browseParameters,
01039                                      $module );
01040         }
01041         else if ( $action == 'set_object_relation_list' )
01042         {
01043             if ( !$http->hasPostVariable( 'BrowseCancelButton' ) )
01044             {
01045                 $selectedObjectIDArray = $http->postVariable( "SelectedObjectIDArray" );
01046                 $content = $contentObjectAttribute->content();
01047                 $priority = 0;
01048                 for ( $i = 0; $i < count( $content['relation_list'] ); ++$i )
01049                 {
01050                     if ( $content['relation_list'][$i]['priority'] > $priority )
01051                         $priority = $content['relation_list'][$i]['priority'];
01052                 }
01053                 if( $selectedObjectIDArray !== null )
01054                 {
01055                     foreach ( $selectedObjectIDArray as $objectID )
01056                     {
01057                         // Check if the given object ID has a numeric value, if not go to the next object.
01058                         if ( !is_numeric( $objectID ) )
01059                         {
01060                             eZDebug::writeError( "Related object ID (objectID): '$objectID', is not a numeric value.", __METHOD__ );
01061 
01062                             continue;
01063                         }
01064 
01065                         /* Here we check if current object is already in the related objects list.
01066                          * If so, we don't add it again.
01067                          * FIXME: Stupid linear search. Maybe there's some better way?
01068                          */
01069                         $found = false;
01070                         foreach ( $content['relation_list'] as $i )
01071                         {
01072                             if ( $i['contentobject_id'] == $objectID )
01073                             {
01074                                 $found = true;
01075                                 break;
01076                             }
01077                         }
01078                         if ( $found )
01079                             continue;
01080 
01081                         ++$priority;
01082                         $content['relation_list'][] = $this->appendObject( $objectID, $priority, $contentObjectAttribute );
01083                         $contentObjectAttribute->setContent( $content );
01084                         $contentObjectAttribute->store();
01085                     }
01086                 }
01087             }
01088         }
01089         else
01090         {
01091             eZDebug::writeError( "Unknown custom HTTP action: " . $action,
01092                                  'eZObjectRelationListType' );
01093         }
01094     }
01095 
01096     function handleCustomObjectHTTPActions( $http, $attributeDataBaseName,
01097                                             $customActionAttributeArray, $customActionParameters )
01098     {
01099         $contentObjectAttribute = $customActionParameters['contentobject_attribute'];
01100         $content = $contentObjectAttribute->content();
01101         foreach( $content['relation_list'] as $relationItem )
01102         {
01103             if ( $relationItem['is_modified'] )
01104             {
01105                 $subObjectID = $relationItem['contentobject_id'];
01106                 $subObjectVersion = $relationItem['contentobject_version'];
01107 
01108                 $attributeBase = $attributeDataBaseName . '_ezorl_edit_object_' . $subObjectID;
01109                 if ( eZContentObject::recursionProtect( $subObjectID ) )
01110                 {
01111                     if ( isset ( $content['temp'] ) )
01112                         $object = $content['temp'][$subObjectID]['object'];
01113                     else
01114                         $object = eZContentObject::fetch( $subObjectID );
01115                     if ( $object )
01116                         $object->handleAllCustomHTTPActions( $attributeBase,
01117                                                              $customActionAttributeArray,
01118                                                              $customActionParameters,
01119                                                              $subObjectVersion );
01120                 }
01121             }
01122         }
01123     }
01124 
01125     /*!
01126      \static
01127      \return \c true if the relation item \a $relationItem exist in the content tree.
01128     */
01129     static function isItemPublished( $relationItem )
01130     {
01131         return is_numeric( $relationItem['node_id'] ) and $relationItem['node_id'] > 0;
01132     }
01133 
01134     /*!
01135      \private
01136      Removes the relation object \a $deletionItem if the item is owned solely by this
01137      version and is not published in the content tree.
01138     */
01139     static function removeRelationObject( $contentObjectAttribute, $deletionItem )
01140     {
01141         if ( self::isItemPublished( $deletionItem ) )
01142         {
01143             return;
01144         }
01145 
01146         $hostObject = $contentObjectAttribute->attribute( 'object' );
01147         $hostObjectVersions = $hostObject->versions();
01148         $isDeletionAllowed = true;
01149 
01150         // check if the relation item to be deleted is unique in the domain of all host-object versions
01151         foreach ( $hostObjectVersions as $version )
01152         {
01153             if ( $isDeletionAllowed and
01154                  $version->attribute( 'version' ) != $contentObjectAttribute->attribute( 'version' ) )
01155             {
01156                 $relationAttribute = eZPersistentObject::fetchObjectList( eZContentObjectAttribute::definition(),
01157                                                                            null,
01158                                                                            array( 'version' => $version->attribute( 'version' ),
01159                                                                                   'contentobject_id' => $hostObject->attribute( 'id' ),
01160                                                                                   'contentclassattribute_id' => $contentObjectAttribute->attribute( 'contentclassattribute_id' ) ) );
01161 
01162                 if ( count( $relationAttribute ) > 0 )
01163                 {
01164                     $relationContent = $relationAttribute[0]->content();
01165                     if ( is_array( $relationContent ) and
01166                          is_array( $relationContent['relation_list'] ) )
01167                     {
01168                         foreach( $relationContent['relation_list'] as $relationItem )
01169                         {
01170                             if ( $deletionItem['contentobject_id'] == $relationItem['contentobject_id'] &&
01171                                  $deletionItem['contentobject_version'] == $relationItem['contentobject_version'] )
01172                             {
01173                                 $isDeletionAllowed = false;
01174                                 break 2;
01175                             }
01176                         }
01177                     }
01178                 }
01179             }
01180         }
01181 
01182         if ( $isDeletionAllowed )
01183         {
01184             $subObjectVersion = eZContentObjectVersion::fetchVersion( $deletionItem['contentobject_version'],
01185                                                                       $deletionItem['contentobject_id'] );
01186             if ( $subObjectVersion instanceof eZContentObjectVersion )
01187             {
01188                 $subObjectVersion->removeThis();
01189             }
01190             else
01191             {
01192                 eZDebug::writeError( 'Cleanup of subobject-version failed. Could not fetch object from relation list.\n' .
01193                                      'Requested subobject id: ' . $deletionItem['contentobject_id'] . '\n' .
01194                                      'Requested Subobject version: ' . $deletionItem['contentobject_version'],
01195                                      __METHOD__ );
01196             }
01197         }
01198     }
01199 
01200 
01201     function createInstance( $class, $priority, $contentObjectAttribute, $nodePlacement = false )
01202     {
01203         $currentObject = $contentObjectAttribute->attribute( 'object' );
01204         $sectionID = $currentObject->attribute( 'section_id' );
01205         $object = $class->instantiate( false, $sectionID, false, $contentObjectAttribute->attribute( 'language_code' ) );
01206         if ( !is_numeric( $nodePlacement ) or $nodePlacement <= 0 )
01207             $nodePlacement = false;
01208         $object->sync();
01209         $relationItem = array( 'identifier' => false,
01210                                'priority' => $priority,
01211                                'in_trash' => false,
01212                                'contentobject_id' => $object->attribute( 'id' ),
01213                                'contentobject_version' => $object->attribute( 'current_version' ),
01214                                'contentobject_remote_id' => $object->attribute( 'remote_id' ),
01215                                'node_id' => false,
01216                                'parent_node_id' => $nodePlacement,
01217                                'contentclass_id' => $class->attribute( 'id' ),
01218                                'contentclass_identifier' => $class->attribute( 'identifier' ),
01219                                'is_modified' => true );
01220         $relationItem['object'] = $object;
01221         return $relationItem;
01222     }
01223 
01224     /**
01225      * Generate array with object relation info
01226      *
01227      * @param integer $objectID The id of the object to add as relation
01228      * @param integer $priority The priortity of the relation
01229      * @param eZContentObjectAttribute $contentObjectAttribute Not used
01230      * @return array|null A array containing relation information or null if object does not exist
01231      */
01232     public function appendObject( $objectID, $priority, $contentObjectAttribute )
01233     {
01234         $object = eZContentObject::fetch( $objectID );
01235 
01236         if ( null === $object )
01237         {
01238             return;
01239         }
01240 
01241         $class = $object->attribute( 'content_class' );
01242         $sectionID = $object->attribute( 'section_id' );
01243         $relationItem = array( 'identifier' => false,
01244                                'priority' => $priority,
01245                                'in_trash' => false,
01246                                'contentobject_id' => $object->attribute( 'id' ),
01247                                'contentobject_version' => $object->attribute( 'current_version' ),
01248                                'contentobject_remote_id' => $object->attribute( 'remote_id' ),
01249                                'node_id' => $object->attribute( 'main_node_id' ),
01250                                'parent_node_id' => $object->attribute( 'main_parent_node_id' ),
01251                                'contentclass_id' => $class->attribute( 'id' ),
01252                                'contentclass_identifier' => $class->attribute( 'identifier' ),
01253                                'is_modified' => false );
01254         $relationItem['object'] = $object;
01255         return $relationItem;
01256     }
01257 
01258 
01259     function fixRelatedObjectItem ( $contentObjectAttribute, $objectID, $mode )
01260     {
01261         switch ( $mode )
01262         {
01263             case 'move':
01264             {
01265                 $this->fixRelationsMove( $objectID, $contentObjectAttribute );
01266             } break;
01267 
01268             case 'trash':
01269             {
01270                 $this->fixRelationsTrash( $objectID, $contentObjectAttribute );
01271             } break;
01272 
01273             case 'restore':
01274             {
01275                 $this->fixRelationsRestore( $objectID, $contentObjectAttribute );
01276             } break;
01277 
01278             case 'remove':
01279             {
01280                 $this->fixRelationsRemove( $objectID, $contentObjectAttribute );
01281             } break;
01282 
01283             case 'swap':
01284             {
01285                 $this->fixRelationsSwap( $objectID, $contentObjectAttribute );
01286             } break;
01287 
01288             default:
01289             {
01290                 eZDebug::writeWarning( 'Unknown mode: ' . $mode, __METHOD__ );
01291             } break;
01292         }
01293     }
01294 
01295     function fixRelationsMove ( $objectID, $contentObjectAttribute )
01296     {
01297         $this->fixRelationsSwap( $objectID, $contentObjectAttribute );
01298     }
01299 
01300     function fixRelationsTrash ( $objectID, $contentObjectAttribute )
01301     {
01302         $content = $contentObjectAttribute->attribute( 'content' );
01303         foreach ( array_keys( $content['relation_list'] ) as $key )
01304         {
01305             if ( $content['relation_list'][$key]['contentobject_id'] == $objectID )
01306             {
01307                 $content['relation_list'][$key]['in_trash'] = true;
01308                 $content['relation_list'][$key]['node_id'] = null;
01309                 $content['relation_list'][$key]['parent_node_id']= null;
01310             }
01311         }
01312         $this->storeObjectAttributeContent( $contentObjectAttribute, $content );
01313         $contentObjectAttribute->setContent( $content );
01314         $contentObjectAttribute->storeData();
01315     }
01316 
01317     function fixRelationsRestore ( $objectID, $contentObjectAttribute )
01318     {
01319         $content = $contentObjectAttribute->content();
01320 
01321         foreach ( array_keys( $content['relation_list'] ) as $key )
01322         {
01323             if ( $content['relation_list'][$key]['contentobject_id'] == $objectID )
01324             {
01325                 $priority = $content['relation_list'][$key]['priority'];
01326                 $content['relation_list'][$key] = $this->appendObject( $objectID, $priority, $contentObjectAttribute);
01327             }
01328         }
01329         $this->storeObjectAttributeContent( $contentObjectAttribute, $content );
01330         $contentObjectAttribute->setContent( $content );
01331         $contentObjectAttribute->storeData();
01332     }
01333 
01334     function fixRelationsRemove ( $objectID, $contentObjectAttribute )
01335     {
01336         $this->removeRelatedObjectItem( $contentObjectAttribute, $objectID );
01337         $contentObjectAttribute->storeData();
01338     }
01339 
01340     function fixRelationsSwap ( $objectID, $contentObjectAttribute )
01341     {
01342         $content =& $contentObjectAttribute->content();
01343 
01344         foreach ( array_keys( $content['relation_list'] ) as $key )
01345         {
01346             $relatedObject =& $content['relation_list'][$key];
01347             if ( $relatedObject['contentobject_id'] == $objectID )
01348             {
01349                 $priority = $content['relation_list'][$key]['priority'];
01350                 $content['relation_list'][$key] = $this->appendObject($objectID, $priority, $contentObjectAttribute );
01351             }
01352         }
01353 
01354         $this->storeObjectAttributeContent( $contentObjectAttribute, $content );
01355         $contentObjectAttribute->setContent( $content );
01356         $contentObjectAttribute->storeData();
01357     }
01358 
01359 
01360     /*!
01361      Returns the content.
01362     */
01363     function objectAttributeContent( $contentObjectAttribute )
01364     {
01365         $xmlText = $contentObjectAttribute->attribute( 'data_text' );
01366         if ( trim( $xmlText ) == '' )
01367         {
01368             $objectAttributeContent = $this->defaultObjectAttributeContent();
01369             return $objectAttributeContent;
01370         }
01371         $doc = $this->parseXML( $xmlText );
01372         $content = $this->createObjectContentStructure( $doc );
01373 
01374         return $content;
01375     }
01376 
01377     function classAttributeContent( $classAttribute )
01378     {
01379         $xmlText = $classAttribute->attribute( 'data_text5' );
01380         if ( trim( $xmlText ) == '' )
01381         {
01382             return $this->defaultClassAttributeContent();
01383         }
01384         $doc = $this->parseXML( $xmlText );
01385         return $this->createClassContentStructure( $doc );
01386     }
01387 
01388     static function parseXML( $xmlText )
01389     {
01390         $dom = new DOMDocument( '1.0', 'utf-8' );
01391         $dom->loadXML( $xmlText );
01392         return $dom;
01393     }
01394 
01395     function defaultClassAttributeContent()
01396     {
01397         return array( 'object_class' => '',
01398                       'selection_type' => 0,
01399                       'type' => 0,
01400                       'class_constraint_list' => array(),
01401                       'default_placement' => false );
01402     }
01403 
01404     function defaultObjectAttributeContent()
01405     {
01406         return array( 'relation_list' => array() );
01407     }
01408 
01409     function createClassContentStructure( $doc )
01410     {
01411         $content = $this->defaultClassAttributeContent();
01412         $root = $doc->documentElement;
01413         $objectPlacement = $root->getElementsByTagName( 'contentobject-placement' )->item( 0 );
01414 
01415         if ( $objectPlacement and $objectPlacement->hasAttributes() )
01416         {
01417             $nodeID = $objectPlacement->getAttribute( 'node-id' );
01418             $content['default_placement'] = array( 'node_id' => $nodeID );
01419         }
01420         $constraints = $root->getElementsByTagName( 'constraints' )->item( 0 );
01421         if ( $constraints )
01422         {
01423             $allowedClassList = $constraints->getElementsByTagName( 'allowed-class' );
01424             foreach( $allowedClassList as $allowedClass )
01425             {
01426                 $content['class_constraint_list'][] = $allowedClass->getAttribute( 'contentclass-identifier' );
01427             }
01428         }
01429         $type = $root->getElementsByTagName( 'type' )->item( 0 );
01430         if ( $type )
01431         {
01432             $content['type'] = $type->getAttribute( 'value' );
01433         }
01434         $selectionType = $root->getElementsByTagName( 'selection_type' )->item( 0 );
01435         if ( $selectionType )
01436         {
01437             $content['selection_type'] = $selectionType->getAttribute( 'value' );
01438         }
01439         $objectClass = $root->getElementsByTagName( 'object_class' )->item( 0 );
01440         if ( $objectClass )
01441         {
01442             $content['object_class'] = $objectClass->getAttribute( 'value' );
01443         }
01444 
01445         return $content;
01446     }
01447 
01448     function createObjectContentStructure( $doc )
01449     {
01450         $content = $this->defaultObjectAttributeContent();
01451         $root = $doc->documentElement;
01452         $relationList = $root->getElementsByTagName( 'relation-list' )->item( 0 );
01453         if ( $relationList )
01454         {
01455             $contentObjectArrayXMLMap = $this->contentObjectArrayXMLMap();
01456             $relationItems = $relationList->getElementsByTagName( 'relation-item' );
01457             foreach ( $relationItems as $relationItem )
01458             {
01459                 $hash = array();
01460 
01461                 foreach ( $contentObjectArrayXMLMap as $attributeXMLName => $attributeKey )
01462                 {
01463                     $attributeValue = $relationItem->hasAttribute( $attributeXMLName ) ? $relationItem->getAttribute( $attributeXMLName ) : false;
01464                     $hash[$attributeKey] = $attributeValue;
01465                 }
01466                 $content['relation_list'][] = $hash;
01467             }
01468         }
01469         return $content;
01470     }
01471 
01472     function customClassAttributeHTTPAction( $http, $action, $classAttribute )
01473     {
01474         switch ( $action )
01475         {
01476             case 'browse_for_placement':
01477             {
01478                 $module = $classAttribute->currentModule();
01479                 $customActionName = 'CustomActionButton[' . $classAttribute->attribute( 'id' ) . '_browsed_for_placement]';
01480                 eZContentBrowse::browse( array( 'action_name' => 'SelectObjectRelationListNode',
01481                                                 'content' => array( 'contentclass_id' => $classAttribute->attribute( 'contentclass_id' ),
01482                                                                     'contentclass_attribute_id' => $classAttribute->attribute( 'id' ),
01483                                                                     'contentclass_version' => $classAttribute->attribute( 'version' ),
01484                                                                     'contentclass_attribute_identifier' => $classAttribute->attribute( 'identifier' ) ),
01485                                                 'persistent_data' => array( $customActionName => '',
01486                                                                             'ContentClassHasInput' => false ),
01487                                                 'description_template' => 'design:class/datatype/browse_objectrelationlist_placement.tpl',
01488                                                 'from_page' => $module->currentRedirectionURI() ),
01489                                          $module );
01490             } break;
01491             case 'browsed_for_placement':
01492             {
01493                 $nodeSelection = eZContentBrowse::result( 'SelectObjectRelationListNode' );
01494                 if ( $nodeSelection and count( $nodeSelection ) > 0 )
01495                 {
01496                     $nodeID = $nodeSelection[0];
01497                     $content = $classAttribute->content();
01498                     $content['default_placement'] = array( 'node_id' => $nodeID );
01499                     $classAttribute->setContent( $content );
01500                 }
01501             } break;
01502             case 'disable_placement':
01503             {
01504                 $content = $classAttribute->content();
01505                 $content['default_placement'] = false;
01506                 $classAttribute->setContent( $content );
01507             } break;
01508             default:
01509             {
01510                 eZDebug::writeError( "Unknown objectrelationlist action '$action'", __METHOD__ );
01511             } break;
01512         }
01513     }
01514 
01515     /*!
01516      Returns the meta data used for storing search indexes.
01517     */
01518     function metaData( $contentObjectAttribute )
01519     {
01520         $metaDataArray = $attributes = array();
01521         $content = $contentObjectAttribute->content();
01522         $language = $contentObjectAttribute->attribute( 'language_code' );
01523         foreach( $content['relation_list'] as $relationItem )
01524         {
01525             $subObjectID = $relationItem['contentobject_id'];
01526             if ( !$subObjectID )
01527                 continue;
01528 
01529             if ( isset( $content['temp'] ) )
01530                 $attributes = $content['temp'][$subObjectID]['attributes'];
01531             else
01532             {
01533                 $subObjectVersion = $relationItem['contentobject_version'];
01534                 $object = eZContentObject::fetch( $subObjectID );
01535                 if ( eZContentObject::recursionProtect( $subObjectID ) )
01536                 {
01537                     if ( !$object )
01538                     {
01539                         continue;
01540                     }
01541                     $attributes = $object->contentObjectAttributes( true, $subObjectVersion, $language );
01542                 }
01543             }
01544 
01545             $attributeMetaDataArray = eZContentObjectAttribute::metaDataArray( $attributes );
01546             $metaDataArray = array_merge( $metaDataArray, $attributeMetaDataArray );
01547         }
01548 
01549         return $metaDataArray;
01550     }
01551 
01552     /*!
01553      \return string representation of an contentobjectattribute data for simplified export
01554 
01555     */
01556     function toString( $contentObjectAttribute )
01557     {
01558         $objectAttributeContent = $contentObjectAttribute->attribute( 'content' );
01559         $objectIDList = array();
01560         foreach( $objectAttributeContent['relation_list'] as $objectInfo )
01561         {
01562             $objectIDList[] = $objectInfo['contentobject_id'];
01563         }
01564         return implode( '-', $objectIDList );
01565     }
01566 
01567     function fromString( $contentObjectAttribute, $string )
01568     {
01569         $objectIDList = explode( '-', $string );
01570 
01571         $content = $this->defaultObjectAttributeContent();
01572         $priority = 0;
01573         foreach( $objectIDList as $objectID )
01574         {
01575             $object = eZContentObject::fetch( $objectID );
01576             if ( $object )
01577             {
01578                 ++$priority;
01579                 $content['relation_list'][] = $this->appendObject( $objectID, $priority, $contentObjectAttribute );
01580             }
01581             else
01582             {
01583                 eZDebug::writeWarning( $objectID, "Can not create relation because object is missing" );
01584             }
01585         }
01586         $contentObjectAttribute->setContent( $content );
01587         return true;
01588     }
01589 
01590     function hasObjectAttributeContent( $contentObjectAttribute )
01591     {
01592         $content = $contentObjectAttribute->content();
01593         return count( $content['relation_list'] ) > 0;
01594     }
01595 
01596     function isIndexable()
01597     {
01598         return true;
01599     }
01600 
01601     /*!
01602      Returns the content of the string for use as a title,
01603      for simplicity this is the name of the first object referenced or false.
01604     */
01605     function title( $contentObjectAttribute, $name = null )
01606     {
01607         $objectAttributeContent = $this->objectAttributeContent( $contentObjectAttribute );
01608 
01609         if ( count( $objectAttributeContent['relation_list'] ) > 0 )
01610         {
01611             $target = $objectAttributeContent['relation_list'][0];
01612             $targetObject = eZContentObject::fetch( $target['contentobject_id'] );
01613             $attributeLanguage = $contentObjectAttribute->attribute( 'language_code' );
01614             $targetObjectName = $targetObject->name( false, $attributeLanguage );
01615             return $targetObjectName;
01616         }
01617         else
01618         {
01619             return false;
01620         }
01621     }
01622 
01623     function serializeContentClassAttribute( $classAttribute, $attributeNode, $attributeParametersNode )
01624     {
01625         $dom = $attributeParametersNode->ownerDocument;
01626         $content = $classAttribute->content();
01627         if ( $content['default_placement'] )
01628         {
01629             $defaultPlacementNode = $dom->createElement( 'default-placement' );
01630             $defaultPlacementNode->setAttribute( 'node-id', $content['default_placement']['node_id'] );
01631             $attributeParametersNode->appendChild( $defaultPlacementNode );
01632         }
01633 
01634         $type = is_numeric( $content['type'] ) ? $content['type'] : '0';
01635         $typeNode = $dom->createElement( 'type' );
01636         $typeNode->appendChild( $dom->createTextNode( $type ) );
01637         $attributeParametersNode->appendChild( $typeNode );
01638 
01639         $classConstraintsNode = $dom->createElement( 'class-constraints' );
01640         $attributeParametersNode->appendChild( $classConstraintsNode );
01641         foreach ( $content['class_constraint_list'] as $classConstraint )
01642         {
01643             $classConstraintIdentifier = $classConstraint;
01644             $classConstraintNode = $dom->createElement( 'class-constraint' );
01645             $classConstraintNode->setAttribute( 'class-identifier', $classConstraintIdentifier );
01646             $classConstraintsNode->appendChild( $classConstraintNode );
01647         }
01648 
01649         if ( isset( $content['selection_type'] ) && is_numeric( $content['selection_type'] ) )
01650         {
01651             $selectionTypeNode = $dom->createElement( 'selection-type' );
01652             $selectionTypeNode->appendChild( $dom->createTextNode( $content['selection_type'] ) );
01653             $attributeParametersNode->appendChild( $selectionTypeNode );
01654         }
01655 
01656         if ( isset( $content['object_class'] ) && is_numeric( $content['object_class'] ) )
01657         {
01658             $objectClassNode = $dom->createElement( 'object-class' );
01659             $objectClassNode->appendChild( $dom->createTextNode( $content['object_class'] ) );
01660             $attributeParametersNode->appendChild( $objectClassNode );
01661         }
01662     }
01663 
01664     function unserializeContentClassAttribute( $classAttribute, $attributeNode, $attributeParametersNode )
01665     {
01666         $content = $classAttribute->content();
01667         $defaultPlacementNode = $attributeParametersNode->getElementsByTagName( 'default-placement' )->item( 0 );
01668         $content['default_placement'] = false;
01669 
01670         if ( $defaultPlacementNode )
01671         {
01672             $content['default_placement'] = array( 'node_id' => $defaultPlacementNode->getAttribute( 'node-id' ) );
01673         }
01674         $content['type'] = $attributeParametersNode->getElementsByTagName( 'type' )->item( 0 )->textContent;
01675         $classConstraintsNode = $attributeParametersNode->getElementsByTagName( 'class-constraints' )->item( 0 );
01676         $classConstraintList = $classConstraintsNode->getElementsByTagName( 'class-constraint' );
01677         $content['class_constraint_list'] = array();
01678         foreach ( $classConstraintList as $classConstraintNode )
01679         {
01680             $classIdentifier = $classConstraintNode->getAttribute( 'class-identifier' );
01681             $content['class_constraint_list'][] = $classIdentifier;
01682         }
01683 
01684         $objectClassNode = $attributeParametersNode->getElementsByTagName( 'object-class' )->item( 0 );
01685         if ( $objectClassNode )
01686         {
01687             $content['object_class'] = $objectClassNode->textContent;
01688         }
01689 
01690         $selectionTypeNode = $attributeParametersNode->getElementsByTagName( 'selection-type' )->item( 0 );
01691         if ( $selectionTypeNode )
01692         {
01693             $content['selection_type'] = $selectionTypeNode->textContent;
01694         }
01695 
01696         $classAttribute->setContent( $content );
01697         $this->storeClassAttributeContent( $classAttribute, $content );
01698     }
01699 
01700     /*!
01701      For each relation export its priority and content object remote_id, like this:
01702       <related-objects>
01703         <relation-list>
01704           <relation-item priority="1"
01705                          contentobject-remote-id="faaeb9be3bd98ed09f606fc16d144eca" />
01706           <relation-item priority="2"
01707                          contentobject-remote-id="1bb4fe25487f05527efa8bfd394cecc7" />
01708         </relation-list>
01709      To do this we fetch content XML and strip all the relation attributes except of "priority" from there,
01710      and add "contentobject-remote-id" attribute.
01711     */
01712     function serializeContentObjectAttribute( $package, $objectAttribute )
01713     {
01714         $node = $this->createContentObjectAttributeDOMNode( $objectAttribute );
01715 
01716         eZDebug::writeDebug( $objectAttribute->attribute( 'data_text' ), 'xml string from data_text field' );
01717         if ( $objectAttribute->attribute( 'data_text' ) === null )
01718         {
01719             $content = array( 'relation_list' => array() );
01720             $dom = $this->createObjectDOMDocument( $content );
01721         }
01722         else
01723         {
01724             $dom = new DOMDocument( '1.0', 'utf-8' );
01725             $success = $dom->loadXML( $objectAttribute->attribute( 'data_text' ) );
01726         }
01727         $rootNode = $dom->documentElement;
01728         $relationList = $rootNode->getElementsByTagName( 'relation-list' )->item( 0 );
01729         if ( $relationList )
01730         {
01731             $relationItems = $relationList->getElementsByTagName( 'relation-item' );
01732             for ( $i = 0; $i < $relationItems->length; $i++ )
01733             {
01734                 $relationItem = $relationItems->item( $i );
01735                 // Add related object remote id as attribute to the relation item.
01736                 $relatedObjectID = $relationItem->getAttribute( 'contentobject-id' );
01737                 $relatedObject = eZContentObject::fetch( $relatedObjectID );
01738                 $relatedObjectRemoteID = $relatedObject->attribute( 'remote_id' );
01739                 $relationItem->setAttribute( 'contentobject-remote-id', $relatedObjectRemoteID );
01740 
01741                 $attributes = $relationItem->attributes;
01742                 // Remove all other relation item attributes except of "priority".
01743                 // This loop intentionally starts with the last attribute, otherwise you will get unexpected results
01744                 for ( $j = $attributes->length - 1; $j >= 0; $j-- )
01745                 {
01746                     $attribute = $attributes->item( $j );
01747                     $attrName = $attribute->localName;
01748 
01749                     eZDebug::writeDebug( $attrName );
01750                     if ( $attrName != 'priority' && $attrName != 'contentobject-remote-id' )
01751                     {
01752                         $success = $relationItem->removeAttribute( $attribute->localName );
01753                         if ( !$success )
01754                         {
01755                             eZDebug::writeError( 'failed removing attribute ' . $attrName . ' from relation-item element' );
01756                         }
01757                     }
01758                 }
01759             }
01760         }
01761 
01762         eZDebug::writeDebug( $dom->saveXML(), 'old xml doc' );
01763 
01764         $importedRootNode = $node->ownerDocument->importNode( $rootNode, true );
01765         $node->appendChild( $importedRootNode );
01766 
01767         return $node;
01768     }
01769 
01770     function unserializeContentObjectAttribute( $package, $objectAttribute, $attributeNode )
01771     {
01772         $rootNode = $attributeNode->getElementsByTagName( 'related-objects' )->item( 0 );
01773         $xmlString = $rootNode ? $rootNode->ownerDocument->saveXML( $rootNode ) : '';
01774         $objectAttribute->setAttribute( 'data_text', $xmlString );
01775     }
01776 
01777     function postUnserializeContentObjectAttribute( $package, $objectAttribute )
01778     {
01779         $xmlString = $objectAttribute->attribute( 'data_text' );
01780         $doc = $this->parseXML( $xmlString );
01781         $rootNode = $doc->documentElement;
01782 
01783         $relationList = $rootNode->getElementsByTagName( 'relation-list' )->item( 0 );
01784         if ( !$relationList )
01785             return false;
01786 
01787         $relationItems = $relationList->getElementsByTagName( 'relation-item' );
01788         for ( $i = $relationItems->length - 1; $i >= 0; $i-- )
01789         {
01790             $relationItem = $relationItems->item( $i );
01791             $relatedObjectRemoteID = $relationItem->getAttribute( 'contentobject-remote-id' );
01792             $object = eZContentObject::fetchByRemoteID( $relatedObjectRemoteID );
01793 
01794             if ( $object === null )
01795             {
01796                 eZDebug::writeWarning( "Object with remote id '$relatedObjectRemoteID' not found: removing the link.", __METHOD__ );
01797                 $relationItem->parentNode->removeChild( $relationItem );
01798                 continue;
01799             }
01800 
01801             $relationItem->setAttribute( 'contentobject-id',        $object->attribute( 'id' ) );
01802             $relationItem->setAttribute( 'contentobject-version',   $object->attribute( 'current_version' ) );
01803             $relationItem->setAttribute( 'node-id',                 $object->attribute( 'main_node_id' ) );
01804             $relationItem->setAttribute( 'parent-node-id',          $object->attribute( 'main_parent_node_id' ) );
01805             $relationItem->setAttribute( 'contentclass-id',         $object->attribute( 'contentclass_id' ) );
01806             $relationItem->setAttribute( 'contentclass-identifier', $object->attribute( 'class_identifier' ) );
01807         }
01808 
01809         $newXmlString = $doc->saveXML( $rootNode );
01810 
01811         $objectAttribute->setAttribute( 'data_text', $newXmlString );
01812         return true;
01813     }
01814 
01815     /*!
01816      Removes objects with given ID from the relations list
01817     */
01818     function removeRelatedObjectItem( $contentObjectAttribute, $objectID )
01819     {
01820         $xmlText = $contentObjectAttribute->attribute( 'data_text' );
01821         if ( trim( $xmlText ) == '' ) return;
01822 
01823         $doc = $this->parseXML( $xmlText );
01824 
01825         $return = false;
01826         $root = $doc->documentElement;
01827         $relationList = $root->getElementsByTagName( 'relation-list' )->item( 0 );
01828         if ( $relationList )
01829         {
01830             $relationItems = $relationList->getElementsByTagName( 'relation-item' );
01831             if ( !empty( $relationItems ) )
01832             {
01833                 foreach( $relationItems as $relationItem )
01834                 {
01835                     if ( $relationItem->getAttribute( 'contentobject-id' ) == $objectID )
01836                     {
01837                         $relationList->removeChild( $relationItem );
01838                         $return = true;
01839                     }
01840                 }
01841             }
01842         }
01843         $this->storeObjectDOMDocument( $doc, $contentObjectAttribute );
01844         return $return;
01845     }
01846 
01847     function supportsBatchInitializeObjectAttribute()
01848     {
01849         return true;
01850     }
01851 
01852     /// \privatesection
01853 }
01854 
01855 eZDataType::register( eZObjectRelationListType::DATA_TYPE_STRING, "eZObjectRelationListType" );
01856 
01857 ?>