eZ Publish  [trunk]
copysubtree.php
Go to the documentation of this file.
00001 <?php
00002 /**
00003  * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved.
00004  * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2
00005  * @version //autogentag//
00006  * @package kernel
00007  */
00008 
00009 $Module = $Params['Module'];
00010 $NodeID = $Params['NodeID'];
00011 
00012 $http = eZHTTPTool::instance();
00013 
00014 if ( $http->hasPostVariable( 'BrowseCancelButton' ) )
00015 {
00016     if ( $http->hasPostVariable( 'BrowseCancelURI' ) )
00017         return $Module->redirectTo( $http->postVariable( 'BrowseCancelURI' ) );
00018 }
00019 
00020 if ( $NodeID === null ) // NodeID is returned after browsing
00021 {
00022     $NodeID = $http->postVariable( 'NodeID' );
00023 }
00024 
00025 $srcNode = eZContentObjectTreeNode::fetch( $NodeID );
00026 
00027 if ( $srcNode === null )
00028     return $Module->handleError( eZError::KERNEL_NOT_AVAILABLE, 'kernel' );
00029 
00030 if ( !$srcNode->attribute( 'can_read' ) )
00031     return $Module->handleError( eZError::KERNEL_ACCESS_DENIED, 'kernel' );
00032 
00033 if ( $Module->isCurrentAction( 'Cancel' ) )
00034 {
00035     $parentNodeID = $srcNode->attribute( 'parent_node_id' );
00036     return $Module->redirectToView( 'view', array( 'full', $parentNodeID ) );
00037 }
00038 
00039 ///// functions START =============================================================================
00040 function copyPublishContentObject( $sourceObject,
00041                                    $sourceSubtreeNodeIDList,
00042                                    &$syncNodeIDListSrc, &$syncNodeIDListNew,
00043                                    &$syncObjectIDListSrc, &$syncObjectIDListNew,
00044                                    $objectIDBlackList, &$nodeIDBlackList,
00045                                    &$notifications,
00046                                    $allVersions = false, $keepCreator = false, $keepTime = false )
00047 {
00048     $sourceObjectID = $sourceObject->attribute( 'id' );
00049 
00050     $key = array_search( $sourceObjectID, $syncObjectIDListSrc );
00051     if ( $key !== false )
00052     {
00053         eZDebug::writeDebug( "Object (ID = $sourceObjectID) has been already copied.",
00054                              "Subtree copy: copyPublishContentObject()" );
00055         return 1; // object already copied
00056     }
00057 
00058     $srcNodeList = $sourceObject->attribute( 'assigned_nodes' );
00059 
00060     // if we already failed to copy that contentobject, then just skip it:
00061     if ( in_array( $sourceObjectID, $objectIDBlackList ) )
00062         return 0;
00063     // if we already failed to copy that node, then just skip it:
00064     //if ( in_array( $sourceNodeID, $nodeIDBlackList ) )
00065     //    return 0;
00066 
00067     // if cannot read contentobject then remember it and all its nodes (nodes
00068     // which are inside subtree being copied) in black list, and skip current node:
00069     if ( !$sourceObject->attribute( 'can_read' ) )
00070     {
00071         $objectIDBlackList[] = $sourceObjectID;
00072         $notifications['Warnings'][] = ezpI18n::tr( 'kernel/content/copysubtree',
00073                                                "Object (ID = %1) was not copied: you do not have permission to read the object.",
00074                                                null, array( $sourceObjectID ) );
00075 
00076         $srcNodeList = $sourceObject->attribute( 'assigned_nodes' );
00077         foreach( $srcNodeList as $srcNode )
00078         {
00079             $srcNodeID = $srcNode->attribute( 'node_id' );
00080             $sourceParentNodeID = $srcNode->attribute( 'parent_node_id' );
00081 
00082             $key = array_search( $sourceParentNodeID, $sourceSubtreeNodeIDList );
00083             if ( $key !== false )
00084             {
00085                 $nodeIDBlackList[] = $srcNodeID;
00086                 $notifications['Warnings'][] = ezpI18n::tr( 'kernel/content/copysubtree',
00087                                                        "Node (ID = %1) was not copied: you do not have permission to read object (ID = %2).",
00088                                                        null, array( $srcNodeID, $sourceObjectID ) );
00089             }
00090         }
00091         return 0;
00092     }
00093 
00094     // check if all possible parent nodes for given contentobject are already published:
00095     $isReadyToPublish = false;
00096     foreach ( $srcNodeList as $srcNode )
00097     {
00098         $srcNodeID = $srcNode->attribute( 'node_id' );
00099 
00100         if ( in_array( $srcNodeID, $nodeIDBlackList ) )
00101             continue;
00102 
00103         $srcParentNodeID = $srcNode->attribute( 'parent_node_id' );
00104 
00105         // if parent node for this node is outside
00106         // of subtree being copied, then skip this node:
00107         $key = array_search( $srcParentNodeID, $sourceSubtreeNodeIDList );
00108         if ( $key === false )
00109             continue;
00110 
00111         // if parent node for this node wasn't copied yet and is in black list
00112         // then add that node in black list and just skip it:
00113         $key = array_search( $srcParentNodeID, $nodeIDBlackList );
00114         if ( $key !== false )
00115         {
00116             $nodeIDBlackList[] = $srcNodeID;
00117             $notifications['Warnings'][] = ezpI18n::tr( 'kernel/content/copysubtree',
00118                                                    "Node (ID = %1) was not copied: parent node (ID = %2) was not copied.",
00119                                                    null, array( $srcNodeID, $srcParentNodeID ) );
00120             continue;
00121         }
00122 
00123         $key = array_search( $srcParentNodeID, $syncNodeIDListSrc );
00124         if ( $key === false )
00125         {
00126             // if parent node is not copied yet and not in black list,
00127             // then just skip sourceObject from copying for next time
00128             eZDebug::writeDebug( "Parent node (ID = $srcParentNodeID) for contentobject (ID = $sourceObjectID) is not published yet.",
00129                                  "Subtree copy: copyPublishContentObject()" );
00130             return 2;
00131         }
00132         else
00133         {
00134             $newParentNodeID = $syncNodeIDListNew[ $key ];
00135             $newParentNode = eZContentObjectTreeNode::fetch( $newParentNodeID );
00136             if ( $newParentNode === null )
00137             {
00138                 eZDebug::writeError( "Cannot fetch one of parent nodes. Error are somewhere above",
00139                                      "Subtree copy error: copyPublishContentObject()" );
00140                 return 3;
00141             }
00142 
00143             if ( $newParentNode->checkAccess( 'create', $sourceObject->attribute( 'contentclass_id' ) ) != 1 )
00144             {
00145                 $nodeIDBlackList[] = $srcNodeID;
00146                 $notifications['Warnings'][] = ezpI18n::tr( 'kernel/content/copysubtree',
00147                                                        "Node (ID = %1) was not copied: you do not have permission to create.",
00148                                                        null, array( $srcNodeID ) );
00149 
00150                 continue;
00151             }
00152             else
00153                 $isReadyToPublish = true;
00154         }
00155     }
00156 
00157     // if all nodes of sourceObject were skiped as black list entry or
00158     // as outside of subtree being copied, then sourceObject cannot be
00159     // copied and published in any new location. So insert sourceObject
00160     // in a black list and skip it.
00161     if ( $isReadyToPublish == false )
00162     {
00163         $objectIDBlackList[] = $sourceObjectID;
00164         $notifications['Warnings'][] = ezpI18n::tr( 'kernel/content/copysubtree',
00165                                                "Object (ID = %1) was not copied: no one nodes of object was not copied.",
00166                                                null, array( $sourceObjectID) );
00167         return 0;
00168     }
00169 
00170     // make copy of source object
00171     $newObject             = $sourceObject->copy( $allVersions ); // insert source and new object's ids in $syncObjectIDList
00172     // We should reset section that will be updated in updateSectionID().
00173     // If sectionID is 0 than the object has been newly created
00174     $newObject->setAttribute( 'section_id', 0 );
00175     $newObject->store();
00176 
00177     $syncObjectIDListSrc[] = $sourceObjectID;
00178 
00179     $curVersion        = $newObject->attribute( 'current_version' );
00180     $curVersionObject  = $newObject->attribute( 'current' );
00181 
00182     $newObjAssignments = $curVersionObject->attribute( 'node_assignments' );
00183 
00184     // copy nodeassigments:
00185     $assignmentsForRemoving = array();
00186     $foundMainAssignment = false;
00187     foreach ( $newObjAssignments as $assignment )
00188     {
00189         $parentNodeID = $assignment->attribute( 'parent_node' );
00190 
00191         // if assigment is outside of subtree being copied then do not copy this assigment
00192         $key1 = array_search( $parentNodeID, $sourceSubtreeNodeIDList );
00193         $key2 = array_search( $parentNodeID, $nodeIDBlackList );
00194         if ( $key1 === false or $key2 !== false )
00195         {
00196             $assignmentsForRemoving[] = $assignment->attribute( 'id' );
00197             continue;
00198         }
00199 
00200         $key = array_search( $parentNodeID, $syncNodeIDListSrc );
00201         if ( $key === false )
00202         {
00203             eZDebug::writeError( "Cannot publish contentobject (ID=$sourceObjectID). Parent is not published yet.",
00204                                  "Subtree Copy error: copyPublishContentObject()" );
00205             return 4;
00206         }
00207 
00208         if ( $assignment->attribute( 'is_main' ) )
00209             $foundMainAssignment = true;
00210 
00211         $newParentNodeID = $syncNodeIDListNew[ $key ];
00212         $assignment->setAttribute( 'parent_node', $newParentNodeID );
00213         $assignment->store();
00214     }
00215     // remove assigments which are outside of subtree being copied:
00216     eZNodeAssignment::purgeByID( $assignmentsForRemoving );
00217 
00218     // if main nodeassigment was not copied then set as main first nodeassigment
00219     if ( $foundMainAssignment == false )
00220     {
00221         $newObjAssignments = $curVersionObject->attribute( 'node_assignments' );
00222         // We need to check if it has any assignments before changing the data.
00223         if ( isset( $newObjAssignments[0] ) )
00224         {
00225             $newObjAssignments[0]->setAttribute( 'is_main', 1 );
00226             $newObjAssignments[0]->store();
00227         }
00228     }
00229 
00230     // publish the newly created object
00231     $result = eZOperationHandler::execute( 'content', 'publish', array( 'object_id' => $newObject->attribute( 'id' ),
00232                                                                         'version'   => $curVersion ) );
00233     // Refetch the object data since it might change in the database.
00234     $newObjectID = $newObject->attribute( 'id' );
00235     $newObject = eZContentObject::fetch( $newObjectID );
00236     $newNodeList = $newObject->attribute( 'assigned_nodes' );
00237     if ( count($newNodeList) == 0 )
00238     {
00239         $newObject->purge();
00240         eZDebug::writeError( "Cannot publish contentobject.",
00241                              "Subtree Copy Error!" );
00242         $sourceObjectName = $srcNode->getName();
00243         $notifications['Warnings'][] = ezpI18n::tr( 'kernel/content/copysubtree',
00244                                                "Cannot publish object (Name: %1, ID: %2).",
00245                                                null, array( $sourceObjectName, $sourceObjectID ) );
00246         return -1;
00247     }
00248     // Only if the object has been published successfully, the object id can be added into $syncObjectIDListNew
00249     $syncObjectIDListNew[] = $newObject->attribute( 'id' );
00250 
00251     $objAssignments = $curVersionObject->attribute( 'node_assignments' );
00252     foreach ( $newNodeList as $newNode )
00253     {
00254         $newParentNode = $newNode->fetchParent();
00255         $newParentNodeID = $newParentNode->attribute( 'node_id' );
00256 
00257         $keyA = array_search( $newParentNodeID, $syncNodeIDListNew );
00258         if ( $keyA === false )
00259         {
00260             eZDebug::writeError( "Algoritm ERROR! Cannot find new parent node ID in new ID's list",
00261                                  "Subtree Copy Error!" );
00262             return -2;
00263         }
00264 
00265         $srcParentNodeID = $syncNodeIDListSrc[ $keyA ];
00266 
00267         // Update attributes of node
00268         $bSrcParentFound = false;
00269         foreach ( $srcNodeList as $srcNode )
00270         {
00271             if ( $srcNode->attribute( 'parent_node_id' ) == $srcParentNodeID )
00272             {
00273                 $newNode->setAttribute( 'priority', $srcNode->attribute( 'priority' ) );
00274                 $newNode->setAttribute( 'is_hidden', $srcNode->attribute( 'is_hidden' ) );
00275                 // Update node visibility
00276                 if ( $newParentNode->attribute( 'is_invisible' ) or $newParentNode->attribute( 'is_hidden' ) )
00277                     $newNode->setAttribute( 'is_invisible', 1 );
00278                 else
00279                     $newNode->setAttribute( 'is_invisible', $srcNode->attribute( 'is_invisible' ) );
00280 
00281                 $syncNodeIDListSrc[] = $srcNode->attribute( 'node_id' );
00282                 $syncNodeIDListNew[] = $newNode->attribute( 'node_id' );
00283                 $bSrcParentFound = true;
00284                 break;
00285             }
00286         }
00287         if ( $bSrcParentFound == false )
00288         {
00289             eZDebug::writeError( "Cannot find source parent node in list of nodes already copied.",
00290                                  "Subtree Copy Error!" );
00291         }
00292         // Create unique remote_id
00293         $newRemoteID = eZRemoteIdUtility::generate( 'node' );
00294         $oldRemoteID = $newNode->attribute( 'remote_id' );
00295         $newNode->setAttribute( 'remote_id', $newRemoteID );
00296         // Change parent_remote_id for object assignments
00297         foreach ( $objAssignments as $assignment )
00298         {
00299             if ( $assignment->attribute( 'parent_remote_id' ) == $oldRemoteID )
00300             {
00301                  $assignment->setAttribute( 'parent_remote_id', $newRemoteID );
00302                  $assignment->store();
00303             }
00304         }
00305         $newNode->store();
00306     }
00307 
00308     // if $keepCreator == true then keep owner of contentobject being
00309     // copied and creator of its published version Unchaged
00310     $isModified = false;
00311     if ( $keepTime )
00312     {
00313         $srcPublished = $sourceObject->attribute( 'published' );
00314         $newObject->setAttribute( 'published', $srcPublished );
00315         $srcModified  = $sourceObject->attribute( 'modified' );
00316         $newObject->setAttribute( 'modified', $srcModified );
00317         $isModified = true;
00318     }
00319     if ( $keepCreator )
00320     {
00321         $srcOwnerID = $sourceObject->attribute( 'owner_id' );
00322         $newObject->setAttribute( 'owner_id', $srcOwnerID );
00323         $isModified = true;
00324     }
00325     if ( $isModified )
00326         $newObject->store();
00327 
00328     if ( $allVersions )
00329     {   // copy time of creation and midification and creator id for
00330         // all versions of content object being copied.
00331         $srcVersionsList = $sourceObject->versions();
00332 
00333         foreach ( $srcVersionsList as $srcVersionObject )
00334         {
00335             $newVersionObject = $newObject->version( $srcVersionObject->attribute( 'version' ) );
00336             if ( !is_object( $newVersionObject ) )
00337                 continue;
00338 
00339             $isModified = false;
00340             if ( $keepTime )
00341             {
00342                 $srcVersionCreated  = $srcVersionObject->attribute( 'created' );
00343                 $newVersionObject->setAttribute( 'created', $srcVersionCreated );
00344                 $srcVersionModified = $srcVersionObject->attribute( 'modified' );
00345                 $newVersionObject->setAttribute( 'modified', $srcVersionModified );
00346                 $isModified = true;
00347             }
00348             if ( $keepCreator )
00349             {
00350                 $srcVersionCreatorID = $srcVersionObject->attribute( 'creator_id' );
00351                 $newVersionObject->setAttribute( 'creator_id', $srcVersionCreatorID );
00352 
00353                 $isModified = true;
00354             }
00355             if ( $isModified )
00356                 $newVersionObject->store();
00357         }
00358     }
00359     else // if not all versions copied
00360     {
00361         $srcVersionObject = $sourceObject->attribute( 'current' );
00362         $newVersionObject = $newObject->attribute( 'current' );
00363 
00364         $isModified = false;
00365         if ( $keepTime )
00366         {
00367             $srcVersionCreated  = $srcVersionObject->attribute( 'created' );
00368             $newVersionObject->setAttribute( 'created', $srcVersionCreated );
00369             $srcVersionModified = $srcVersionObject->attribute( 'modified' );
00370             $newVersionObject->setAttribute( 'modified', $srcVersionModified );
00371             $isModified = true;
00372         }
00373         if ( $keepCreator )
00374         {
00375             $srcVersionCreatorID = $srcVersionObject->attribute( 'creator_id' );
00376             $newVersionObject->setAttribute( 'creator_id', $srcVersionCreatorID );
00377             $isModified = true;
00378         }
00379         if ( $isModified )
00380             $newVersionObject->store();
00381     }
00382 
00383     return 0; // source object was copied successfully.
00384 
00385 } // function copyPublishContentObject END
00386 
00387 
00388 function copySubtree( $srcNodeID, $dstNodeID, &$notifications, $allVersions, $keepCreator, $keepTime )
00389 {
00390     // 1. Copy subtree and form the arrays of accordance of the old and new nodes and content objects.
00391 
00392     $sourceSubTreeMainNode = ( $srcNodeID ) ? eZContentObjectTreeNode::fetch( $srcNodeID ) : false;
00393     $destinationNode = ( $dstNodeID ) ? eZContentObjectTreeNode::fetch( $dstNodeID ) : false;
00394 
00395     if ( !$sourceSubTreeMainNode )
00396     {
00397         eZDebug::writeError( "Cannot get subtree main node (nodeID = $srcNodeID).",
00398                              "Subtree copy Error!" );
00399         $notifications['Errors'][] = ezpI18n::tr( 'kernel/content/copysubtree',
00400                                             "Fatal error: cannot get subtree main node (ID = %1).",
00401                                             null, array( $srcNodeID ) );
00402         $notifications['Result'] = false;
00403         return $notifications;
00404     }
00405     if ( !$destinationNode )
00406     {
00407         eZDebug::writeError( "Cannot get destination node (nodeID = $dstNodeID).",
00408                              "Subtree copy Error!" );
00409         $notifications['Errors'][] = ezpI18n::tr( 'kernel/content/copysubtree',
00410                                             "Fatal error: cannot get destination node (ID = %1).",
00411                                             null, array( $dstNodeID ) );
00412         $notifications['Result'] = false;
00413         return $notifications;
00414     }
00415 
00416     $sourceNodeList    = array();
00417 
00418     $syncNodeIDListSrc = array(); // arrays for synchronizing between source and new IDs of nodes
00419     $syncNodeIDListNew = array();
00420     $syncObjectIDListSrc = array(); // arrays for synchronizing between source and new IDs of contentobjects
00421     $syncObjectIDListNew = array();
00422 
00423     $sourceSubTreeMainNodeID = $sourceSubTreeMainNode->attribute( 'node_id' );
00424     $sourceNodeList[] = $sourceSubTreeMainNode;
00425 
00426     $syncNodeIDListSrc[] = $sourceSubTreeMainNode->attribute( 'parent_node_id' );
00427     $syncNodeIDListNew[] = (int) $dstNodeID;
00428 
00429     $nodeIDBlackList = array(); // array of nodes which are unable to copy
00430     $objectIDBlackList = array(); // array of contentobjects which are unable to copy in any location inside new subtree
00431 
00432     $sourceNodeList = array_merge( $sourceNodeList,
00433                                    eZContentObjectTreeNode::subTreeByNodeID( array( 'Limitation' => array() ), $sourceSubTreeMainNodeID ) );
00434     $countNodeList = count( $sourceNodeList );
00435 
00436     $notifications['Notifications'][] = ezpI18n::tr( 'kernel/content/copysubtree',
00437                                                 "Number of nodes of source subtree - %1",
00438                                                 null, array( $countNodeList ) );
00439 
00440     // Prepare list of source node IDs. We will need it in the future
00441     // for checking node is inside or outside of the subtree being copied.
00442     $sourceNodeIDList = array();
00443     foreach ( $sourceNodeList as $sourceNode )
00444         $sourceNodeIDList[] = $sourceNode->attribute( 'node_id' );
00445 
00446     eZDebug::writeDebug( "Source NodeID = $srcNodeID, destination NodeID = $dstNodeID",
00447                          "Subtree copy: START!" );
00448 
00449     // 1. copying and publishing source subtree
00450     $k = 0;
00451     while ( count( $sourceNodeList ) > 0 )
00452     {
00453         if ( $k > $countNodeList )
00454         {
00455             eZDebug::writeError( "Too many loops while copying nodes.",
00456                                  "Subtree Copy Error!" );
00457             break;
00458         }
00459 
00460         for ( $i = 0; $i < count( $sourceNodeList ); $i)
00461         {
00462             $sourceNodeID = $sourceNodeList[ $i ]->attribute( 'node_id' );
00463 
00464             // if node was alreaty copied
00465             if ( in_array( $sourceNodeID, $syncNodeIDListSrc ) )
00466             {
00467                 array_splice( $sourceNodeList, $i, 1 );
00468                 continue;
00469             }
00470 
00471             //////////// check permissions START
00472             // if node is already in black list, then skip current node:
00473             if ( in_array( $sourceNodeID, $nodeIDBlackList ) )
00474             {
00475                 array_splice( $sourceNodeList, $i, 1 );
00476                 continue;
00477             }
00478 
00479             $sourceObject = $sourceNodeList[ $i ]->object();
00480 
00481             $srcSubtreeNodeIDlist = ($sourceNodeID == $sourceSubTreeMainNodeID) ? $syncNodeIDListSrc : $sourceNodeIDList;
00482             $copyResult = copyPublishContentObject( $sourceObject,
00483                                                     $srcSubtreeNodeIDlist,
00484                                                     $syncNodeIDListSrc, $syncNodeIDListNew,
00485                                                     $syncObjectIDListSrc, $syncObjectIDListNew,
00486                                                     $objectIDBlackList, $nodeIDBlackList,
00487                                                     $notifications,
00488                                                     $allVersions, $keepCreator, $keepTime );
00489             if ( $copyResult === 0 )
00490             {   // if copying successful then remove $sourceNode from $sourceNodeList
00491                 array_splice( $sourceNodeList, $i, 1 );
00492             }
00493             else
00494                 $i++;
00495         }
00496         $k++;
00497     }
00498 
00499     array_shift( $syncNodeIDListSrc );
00500     array_shift( $syncNodeIDListNew );
00501 
00502     $countNewNodes = count( $syncNodeIDListNew );
00503     $countNewObjects = count( $syncObjectIDListNew );
00504 
00505     $notifications['Notifications'][] = ezpI18n::tr( 'kernel/content/copysubtree',
00506                                                 "Number of copied nodes - %1",
00507                                                 null, array( $countNewNodes ) );
00508     $notifications['Notifications'][] = ezpI18n::tr( 'kernel/content/copysubtree',
00509                                                 "Number of copied contentobjects - %1",
00510                                                 null, array( $countNewObjects ) );
00511 
00512     eZDebug::writeDebug( count( $syncNodeIDListNew ), "Number of copied nodes: " );
00513     eZDebug::writeDebug( count( $syncObjectIDListNew ), "Number of copied contentobjects: " );
00514 
00515     eZDebug::writeDebug( $objectIDBlackList, "Copy subtree: Not copied object IDs list:" );
00516     eZDebug::writeDebug( $nodeIDBlackList, "Copy subtree: Not copied node IDs list:" );
00517 
00518     $key = array_search( $sourceSubTreeMainNodeID, $syncNodeIDListSrc );
00519     if ( $key === false )
00520     {
00521         eZDebug::writeDebug( "Root node of given subtree was not copied.",
00522                              "Subtree copy:" );
00523         $notifications['Result'] = false;
00524         return $notifications;
00525     }
00526 
00527     // 2. fetch all new subtree
00528 
00529     $newSubTreeMainNodeID = $syncNodeIDListSrc[ $key ];
00530     $newSubTreeMainNode   = eZContentObjectTreeNode::fetch( $newSubTreeMainNodeID );
00531 
00532     $newNodeList[] = $newSubTreeMainNode;
00533     $newNodeList = $sourceNodeList = array_merge( $newNodeList,
00534                                                   eZContentObjectTreeNode::subTreeByNodeID( false, $newSubTreeMainNodeID ) );
00535 
00536     // 3. fix local links (in ezcontentobject_link)
00537     eZDebug::writeDebug( "Fixing global and local links...",
00538                          "Subtree copy:" );
00539 
00540     $db = eZDB::instance();
00541     if ( !$db )
00542     {
00543         eZDebug::writeError( "Cannot create instance of eZDB for fixing local links (related objects).",
00544                              "Subtree Copy Error!" );
00545         $notifications['Errors'][] = ezpI18n::tr( 'kernel/content/copysubtree',
00546                                              "Cannot create instance of eZDB to fix local links (related objects)." );
00547         return $notifications;
00548     }
00549 
00550     $idListINString = $db->generateSQLINStatement( $syncObjectIDListNew, 'from_contentobject_id', false, false, 'int' );
00551     $relatedRecordsList = $db->arrayQuery( "SELECT * FROM ezcontentobject_link WHERE $idListINString" );
00552 
00553     foreach ( $relatedRecordsList as $relatedEntry )
00554     {
00555         $kindex = array_search( $relatedEntry[ 'to_contentobject_id' ], $syncObjectIDListSrc );
00556         if ( $kindex !== false )
00557         {
00558             $newToContentObjectID = (int) $syncObjectIDListNew[ $kindex ];
00559             $linkID = (int) $relatedEntry[ 'id' ];
00560             $db->query( "UPDATE ezcontentobject_link SET  to_contentobject_id=$newToContentObjectID WHERE id=$linkID" );
00561         }
00562     }
00563 
00564     // 4. duplicating of global links for new contentobjects (in ezurl_object_link) are automatic during copy of contentobject.
00565     //    it was fixed as bug patch.
00566 
00567     // 5. fixing node_ids and object_ids in ezxmltext attributes of copied objects
00568     $conditions = array( 'contentobject_id' => '', // 5
00569                          'data_type_string' => 'ezxmltext' );
00570 
00571     foreach ( $syncObjectIDListNew as $contentObjectID )
00572     {
00573         $conditions[ 'contentobject_id' ] = $contentObjectID;
00574         $attributeList = eZPersistentObject::fetchObjectList( eZContentObjectAttribute::definition(), null, $conditions );
00575         if ( count( $attributeList ) == 0 )
00576         {
00577             continue;
00578         }
00579         foreach ( $attributeList as $xmlAttribute )
00580         {
00581             $xmlText = $xmlAttribute->attribute( 'data_text' );
00582             $xmlTextLen = strlen ( $xmlText );
00583             $isTextModified = false;
00584             $curPos = 0;
00585 
00586             while ( $curPos < $xmlTextLen )
00587             {
00588                 $literalTagBeginPos = strpos( $xmlText, "<literal", $curPos );
00589                 if ( $literalTagBeginPos )
00590                 {
00591                     $literalTagEndPos = strpos( $xmlText, "</literal>", $literalTagBeginPos );
00592                     if ( $literalTagEndPos === false )
00593                         break;
00594                     $curPos = $literalTagEndPos + 9;
00595                 }
00596 
00597                 if ( ($tagBeginPos = strpos( $xmlText, "<link", $curPos )) !== false or
00598                      ($tagBeginPos = strpos( $xmlText, "<a"   , $curPos )) !== false or
00599                      ($tagBeginPos = strpos( $xmlText, "<embed",$curPos )) !== false )
00600                 {
00601                     $tagEndPos = strpos( $xmlText, ">", $tagBeginPos + 1 );
00602                     if ( $tagEndPos === false )
00603                         break;
00604 
00605                     $tagText = substr( $xmlText, $tagBeginPos, $tagEndPos - $tagBeginPos );
00606 
00607                     if ( ($nodeIDAttributePos = strpos( $tagText, " node_id=\"" )) !== false )
00608                     {
00609                         $idNumberPos = $nodeIDAttributePos + 10;
00610                         $quoteEndPos = strpos( $tagText, "\"", $idNumberPos );
00611 
00612                         if ( $quoteEndPos !== false )
00613                         {
00614                             $idNumber = substr( $tagText, $idNumberPos, $quoteEndPos - $idNumberPos );
00615                             $key = array_search( (int) $idNumber, $syncNodeIDListSrc );
00616 
00617                             if ( $key !== false )
00618                             {
00619                                 $tagText = substr_replace( $tagText, (string) $syncNodeIDListNew[ $key ], $idNumberPos, $quoteEndPos - $idNumberPos );
00620                                 $xmlText = substr_replace( $xmlText, $tagText, $tagBeginPos, $tagEndPos - $tagBeginPos );
00621                                 $isTextModified = true;
00622                             }
00623                         }
00624                     }
00625                     else if ( ($objectIDAttributePos = strpos( $tagText, " object_id=\"" )) !== false )
00626                     {
00627                         $idNumberPos = $objectIDAttributePos + 12;
00628                         $quoteEndPos = strpos( $tagText, "\"", $idNumberPos );
00629 
00630                         if ( $quoteEndPos !== false )
00631                         {
00632                             $idNumber = substr( $tagText, $idNumberPos, $quoteEndPos - $idNumberPos );
00633                             $key = array_search( (int) $idNumber, $syncObjectIDListSrc );
00634                             if ( $key !== false )
00635                             {
00636                                 $tagText = substr_replace( $tagText, (string) $syncObjectIDListNew[ $key ], $idNumberPos, $quoteEndPos - $idNumberPos );
00637                                 $xmlText = substr_replace( $xmlText, $tagText, $tagBeginPos, $tagEndPos - $tagBeginPos );
00638                                 $isTextModified = true;
00639                             }
00640                         }
00641                     }
00642                     $curPos = $tagEndPos;
00643                 }
00644                 else if ( ($tagBeginPos = strpos( $xmlText, "<object", $curPos )) !== false )
00645                 {
00646                     $tagEndPos = strpos( $xmlText, ">", $tagBeginPos + 1 );
00647                     if ( !$tagEndPos )
00648                         break;
00649 
00650                     $tagText = substr( $xmlText, $tagBeginPos, $tagEndPos - $tagBeginPos );
00651 
00652                     if ( ($idAttributePos = strpos( $tagText, " id=\"" )) !== false )
00653                     {
00654                         $idNumberPos = $idAttributePos + 5;
00655                         $quoteEndPos = strpos( $tagText, "\"", $idNumberPos );
00656 
00657                         if ( $quoteEndPos !== false )
00658                         {
00659                             $idNumber = substr( $tagText, $idNumberPos, $quoteEndPos - $idNumberPos );
00660                             $key = array_search( (int) $idNumber, $syncObjectIDListSrc );
00661                             if ( $key !== false )
00662                             {
00663                                 $tagText = substr_replace( $tagText, (string) $syncObjectIDListNew[ $key ], $idNumberPos, $quoteEndPos - $idNumberPos );
00664                                 $xmlText = substr_replace( $xmlText, $tagText, $tagBeginPos, $tagEndPos - $tagBeginPos );
00665                                 $isTextModified = true;
00666                             }
00667                         }
00668                     }
00669                     $curPos = $tagEndPos;
00670                 }
00671                 else
00672                     break;
00673 
00674             } // while END
00675 
00676             if ( $isTextModified )
00677             {
00678                 $xmlAttribute->setAttribute( 'data_text', $xmlText );
00679                 $xmlAttribute->store();
00680             }
00681         } // foreach END
00682     }
00683 
00684     // 6. fixing datatype ezobjectrelationlist
00685     $conditions = array( 'contentobject_id' => '',
00686                          'data_type_string' => 'ezobjectrelationlist' );
00687     foreach ( $syncObjectIDListNew as $contentObjectID )
00688     {
00689         $conditions[ 'contentobject_id' ] = $contentObjectID;
00690         $attributeList = eZPersistentObject::fetchObjectList( eZContentObjectAttribute::definition(), null, $conditions );
00691         if ( count( $attributeList ) == 0 )
00692         {
00693             continue;
00694         }
00695         foreach ( $attributeList as $relationListAttribute )
00696         {
00697             $relationsXmlText = $relationListAttribute->attribute( 'data_text' );
00698             $relationsDom = eZObjectRelationListType::parseXML( $relationsXmlText );
00699             $relationItems = $relationsDom->getElementsByTagName( 'relation-item' );
00700             $isRelationModified = false;
00701 
00702             foreach ( $relationItems as $relationItem )
00703             {
00704                 $originalObjectID = $relationItem->getAttribute( 'contentobject-id' );
00705 
00706                 $key = array_search( $originalObjectID, $syncObjectIDListSrc );
00707                 if ( $key !== false )
00708                 {
00709                     $newObjectID = $syncObjectIDListNew[ $key ];
00710                     $relationItem->setAttribute( 'contentobject-id', $newObjectID );
00711                     $isRelationModified = true;
00712                 }
00713 
00714                 $originalNodeID = $relationItem->getAttribute( 'node-id' );
00715                 if ( $originalNodeID )
00716                 {
00717                     $key = array_search( $originalNodeID, $syncNodeIDListSrc );
00718                     if ( $key !== false )
00719                     {
00720                         $newNodeID = $syncNodeIDListNew[ $key ];
00721                         $relationItem->setAttribute( 'node-id', $newNodeID );
00722 
00723                         $newNode = eZContentObjectTreeNode::fetch( $newNodeID );
00724                         $newParentNodeID = $newNode->attribute( 'parent_node_id' );
00725                         $relationItem->setAttribute( 'parent-node-id', $newParentNodeID );
00726                         $isRelationModified = true;
00727                     }
00728                 }
00729             }
00730             if ( $isRelationModified )
00731             {
00732                 $attributeID = $relationListAttribute->attribute( 'id' );
00733                 $attributeVersion = $relationListAttribute->attribute( 'version' );
00734                 $changedDomString =$db->escapeString( eZObjectRelationListType::domString( $relationsDom ) );
00735                 $db->query( "UPDATE ezcontentobject_attribute SET data_text='$changedDomString'
00736                              WHERE id=$attributeID AND version=$attributeVersion" );
00737             }
00738         }
00739     }
00740 
00741     eZDebug::writeDebug( "Successfuly DONE.",
00742                          "Copy subtree:" );
00743 
00744     $notifications['Result'] = true;
00745     return $notifications;
00746 } // function copySubtree END
00747 
00748 /*!
00749 Browse for node to place the object copy into
00750 */
00751 function browse( $Module, $srcNode )
00752 {
00753     if ( $Module->hasActionParameter( 'LanguageCode' ) )
00754         $languageCode = $Module->actionParameter( 'LanguageCode' );
00755     else
00756     {
00757         $languageCode = false;
00758     }
00759 
00760     $nodeID          = $srcNode->attribute( 'node_id' );
00761     $object          = $srcNode->attribute( 'object' );
00762     $objectID        = $object->attribute( 'id' );
00763     $class           = $object->contentClass();
00764     $classID         = $class->attribute( 'id' );
00765     $srcParentNodeID = $srcNode->attribute( 'parent_node_id' );
00766 
00767     $ignoreNodesSelect = array();
00768     $ignoreNodesClick = array();
00769     foreach ( $object->assignedNodes( false ) as $element )
00770     {
00771         $ignoreNodesSelect[] = $element['node_id'];
00772         $ignoreNodesClick[]  = $element['node_id'];
00773     }
00774     $ignoreNodesSelect = array_unique( $ignoreNodesSelect );
00775     $ignoreNodesClick = array_unique( $ignoreNodesClick );
00776 
00777     $viewMode = 'full';
00778     if ( $Module->hasActionParameter( 'ViewMode' ) )
00779         $viewMode = $Module->actionParameter( 'ViewMode' );
00780 
00781     eZContentBrowse::browse(
00782          array( 'action_name'          => 'CopySubtree',
00783                 'description_template' => 'design:content/browse_copy_subtree.tpl',
00784                 'keys'                 => array( 'class'      => $classID,
00785                                                  'class_id'   => $class->attribute( 'identifier' ),
00786                                                  'classgroup' => $class->attribute( 'ingroup_id_list' ),
00787                                                  'section'    => $object->attribute( 'section_id' ) ),
00788                 'ignore_nodes_select'  => $ignoreNodesSelect,
00789                 'ignore_nodes_click'   => $ignoreNodesClick,
00790                 'persistent_data'      => array( 'ObjectID' => $objectID,
00791                                                  'NodeID'   => $nodeID ),
00792                 'permission'           => array( 'access' => 'create', 'contentclass_id' => $classID ),
00793                 'content'              => array( 'node_id' => $nodeID ),
00794                 'start_node'           => $srcParentNodeID,
00795                 'cancel_page'          => $Module->redirectionURIForModule( $Module, 'view',
00796                                                          array( $viewMode, $srcParentNodeID, $languageCode ) ),
00797                 'from_page'            => "/content/copysubtree" ),
00798          $Module );
00799 }
00800 
00801 /*!
00802 Redirect to the page that lets a user to choose which versions to copy:
00803 either all version or the current one.
00804 */
00805 function chooseOptionsToCopy( $Module, &$Result, $srcNode, $chooseVersions, $chooseCreator, $chooseTime )
00806 {
00807         $selectedNodeIDArray = eZContentBrowse::result( $Module->currentAction() );
00808 
00809 
00810         $tpl = eZTemplate::factory();
00811         $tpl->setVariable( 'node', $srcNode );
00812         $tpl->setVariable( 'selected_node_id', $selectedNodeIDArray[0] );
00813         $tpl->setVariable( 'choose_versions', $chooseVersions );
00814         $tpl->setVariable( 'choose_creator', $chooseCreator );
00815         $tpl->setVariable( 'choose_time', $chooseTime );
00816 
00817         $Result['content'] = $tpl->fetch( 'design:content/copy_subtree.tpl' );
00818         $Result['path'] = array( array( 'url' => false,
00819                                         'text' => ezpI18n::tr( 'kernel/content', 'Content' ) ),
00820                                  array( 'url' => false,
00821                                         'text' => ezpI18n::tr( 'kernel/content', 'Copy subtree' ) ) );
00822 }
00823 
00824 function showNotificationAfterCopying( $http, $Module, &$Result, &$Notifications, $srcNode )
00825 {
00826     $tpl = eZTemplate::factory();
00827 
00828     if ( $http->hasSessionVariable( "LastAccessesURI", false ) )
00829     {
00830         $tpl->setVariable( 'redirect_url', $http->sessionVariable( "LastAccessesURI" ) );
00831     }
00832     else
00833     {
00834         $parentRootNodeID = $srcNode->attribute( 'parent_node_id' );
00835         if ( $Module->hasActionParameter( 'LanguageCode' ) )
00836             $languageCode = $Module->actionParameter( 'LanguageCode' );
00837         else
00838         {
00839             $languageCode = false;
00840         }
00841 
00842         if ( $Module->hasActionParameter( 'ViewMode' ) )
00843             $viewMode = $Module->actionParameter( 'ViewMode' );
00844         else
00845             $viewMode = 'full';
00846 
00847         $redirectURI = $Module->redirectionURIForModule( $Module, 'view', array( $viewMode, $parentRootNodeID, $languageCode ) );
00848         $tpl->setVariable( 'redirect_url', $redirectURI );
00849     }
00850 
00851     $tpl->setVariable( 'source_node', $srcNode );
00852     //$tpl->setVariable( 'subtree_nodes_count', $srcSubtreeNodesCount );
00853     $tpl->setVariable( 'notifications', $Notifications );
00854 
00855     $Result['content'] = $tpl->fetch( 'design:content/copy_subtree_notification.tpl' );
00856     $Result['path'] = array( array( 'url' => false,
00857                                     'text' => ezpI18n::tr( 'kernel/content', 'Content' ) ),
00858                              array( 'url' => false,
00859                                     'text' => ezpI18n::tr( 'kernel/content', 'Copy subtree' ) ) );
00860 }
00861 /////////// functions END ==================================================================
00862 
00863 $Result = array();
00864 $notifications = array( 'Notifications' => array(),
00865                         'Warnings' => array(),
00866                         'Errors' => array(),
00867                         'Result' => false );
00868 $contentINI = eZINI::instance( 'content.ini' );
00869 
00870 // check if number of nodes being copied not more then MaxNodesCopySubtree setting
00871 $maxNodesCopySubtree = $contentINI->variable( 'CopySettings', 'MaxNodesCopySubtree' );
00872 $srcSubtreeNodesCount = $srcNode->subTreeCount();
00873 
00874 if ( $srcSubtreeNodesCount > $maxNodesCopySubtree )
00875 {
00876     $notifications['Warnings'][] = ezpI18n::tr( 'kernel/content/copysubtree',
00877                                            "You are trying to copy a subtree that contains more than ".
00878                                            "the maximum possible nodes for subtree copying. ".
00879                                            "You can copy this subtree using Subtree Copy script.",
00880                                            null, array( $maxNodesCopySubtree ) );
00881     $notifications['Result'] = false;
00882     showNotificationAfterCopying( $http, $Module, $Result, $notifications, $srcNode );
00883     return;
00884 }
00885 
00886 $versionHandling = $contentINI->variable( 'CopySettings', 'VersionHandling' );
00887 $creatorHandling = $contentINI->variable( 'CopySettings', 'CreatorHandling' );
00888 $timeHandling    = $contentINI->variable( 'CopySettings', 'TimeHandling' );
00889 $showCopySubtreeNotification = $contentINI->variable( 'CopySettings', 'ShowCopySubtreeNotification' );
00890 
00891 $chooseVersions = ( $versionHandling == 'user-defined' );
00892 $chooseCreator  = ( $creatorHandling == 'user-defined' );
00893 $chooseTime     = ( $timeHandling    == 'user-defined' );
00894 $showNotification = ( $showCopySubtreeNotification == 'enabled' );
00895 
00896 if( $chooseVersions )
00897     $allVersions = ( $Module->hasActionParameter( 'VersionChoice' ) and
00898                      $Module->actionParameter( 'VersionChoice' ) == 1 ) ? true : false;
00899 else
00900     $allVersions = ( $versionHandling == 'last-published' ) ? false : true;
00901 
00902 if ( $chooseCreator )
00903     $keepCreator = ( $Module->hasActionParameter( 'CreatorChoice' ) and
00904                      $Module->actionParameter( 'CreatorChoice' ) == 1 ) ? true : false;
00905 else
00906     $keepCreator = ( $creatorHandling == 'keep-unchanged' );
00907 
00908 if ( $chooseTime )
00909     $keepTime = ( $Module->hasActionParameter( 'TimeChoice' ) and
00910                   $Module->actionParameter( 'TimeChoice' ) == 1 ) ? true : false;
00911 else
00912     $keepTime = ( $timeHandling == 'keep-unchanged' );
00913 
00914 
00915 $keepCreator = false;
00916 $keepTime = false;
00917 
00918 if ( $Module->isCurrentAction( 'Copy' ) )
00919 {
00920     // actually do copying after a user has selected object versions to copy
00921     $newParentNodeID = $http->postVariable( 'SelectedNodeID' );
00922     copySubtree( $NodeID, $newParentNodeID, $notifications, $allVersions, $keepCreator, $keepTime );
00923 
00924     if ( $showNotification )
00925     {
00926         showNotificationAfterCopying( $http, $Module, $Result, $notifications, $srcNode );
00927         return;
00928     }
00929     return $Module->redirectToView( 'view', array( 'full', $newParentNodeID ) );
00930 }
00931 else if ( $Module->isCurrentAction( 'CopySubtree' ) )
00932 {
00933     // we get here after a user selects target node to place the source object under
00934     if( $chooseVersions or $chooseCreator or $chooseTime )
00935     {
00936         // redirect to the page with choice of versions to copy
00937         chooseOptionsToCopy( $Module, $Result, $srcNode, $chooseVersions, $chooseCreator, $chooseTime );
00938     }
00939     else
00940     {
00941         // actually do copying of the pre-configured object version(s)
00942         $selectedNodeIDArray = eZContentBrowse::result( $Module->currentAction() );
00943         $newParentNodeID = $selectedNodeIDArray[0];
00944         copySubtree( $NodeID, $newParentNodeID, $notifications, $allVersions, $keepCreator, $keepTime );
00945 
00946         if ( $showNotification )
00947         {
00948             showNotificationAfterCopying( $http, $Module, $Result, $notifications, $srcNode );
00949             return;
00950         }
00951         return $Module->redirectToView( 'view', array( 'full', $newParentNodeID ) );
00952     }
00953 }
00954 else // default, initial action
00955 {   //Browse for target node.
00956     //We get here when a user clicks "copy" button when viewing some node.
00957     browse( $Module, $srcNode );
00958 }
00959 
00960 ?>