function checkNodeActions( $module, $class, $object, $version, $contentObjectAttributes, $editVersion, $editLanguage, $fromLanguage ) { // If the object has been previously published we do not allow node assignment operations if ( $object->attribute( 'status' ) != eZContentObject::STATUS_DRAFT ) { if ( !$module->isCurrentAction( 'BrowseForPrimaryNodes' ) ) { return; } } $http = eZHTTPTool::instance(); if ( $module->isCurrentAction( 'BrowseForNodes' ) || $module->isCurrentAction( 'BrowseForPrimaryNodes' ) ) { // Remove custom actions from attribute editing. $http->removeSessionVariable( 'BrowseCustomAction' ); $ignoreNodesSelect = array(); $ignoreNodesClick = array(); $assigned = $version->nodeAssignments(); $publishedAssigned = $object->assignedNodes( false ); $isTopLevel = false; foreach ( $publishedAssigned as $element ) { $append = false; if ( $element['parent_node_id'] == 1 ) $isTopLevel = true; foreach ( $assigned as $ass ) { if ( $ass->attribute( 'parent_node' ) == $element['parent_node_id'] ) $append = true; } /* If the current version (draft) has no assigned nodes then * we should disallow adding assignments under nodes * the previous published version is assignned to. * Thus we avoid fatal errors in eZ Publish. */ if ( count($assigned) == 0 ) { $ignoreNodesSelect[] = $element['node_id']; $ignoreNodesClick[] = $element['node_id']; } if ( $append ) { $ignoreNodesSelect[] = $element['node_id']; $ignoreNodesClick[] = $element['node_id']; $ignoreNodesSelect[] = $element['parent_node_id']; } } if ( !$isTopLevel ) { $ignoreNodesSelect = array_unique( $ignoreNodesSelect ); $objectID = $object->attribute( 'id' ); $action = 'AddNodeAssignment'; if ( $module->isCurrentAction( 'BrowseForPrimaryNodes' ) ) { $action = 'AddPrimaryNodeAssignment'; } eZContentBrowse::browse( array( 'action_name' => $action, 'description_template' => 'design:content/browse_placement.tpl', 'keys' => array( 'class' => $class->attribute( 'id' ), 'class_id' => $class->attribute( 'identifier' ), 'classgroup' => $class->attribute( 'ingroup_id_list' ), 'section' => $object->attribute( 'section_id' ) ), 'ignore_nodes_select' => $ignoreNodesSelect, 'ignore_nodes_click' => $ignoreNodesClick, 'content' => array( 'object_id' => $objectID, 'object_version' => $editVersion, 'object_language' => $editLanguage ), 'from_page' => "/content/edit/$objectID/$editVersion/$editLanguage/$fromLanguage" ), $module ); return eZModule::HOOK_STATUS_CANCEL_RUN; } } // If node assignment handling is diabled we return $useNodeAssigments = true; if ( $http->hasPostVariable( 'UseNodeAssigments' ) ) $useNodeAssigments = (bool)$http->postVariable( 'UseNodeAssigments' ); if ( !$useNodeAssigments ) return; // Remove custom actions from attribute editing. $http->removeSessionVariable( 'BrowseCustomAction' ); if ( $module->isCurrentAction( 'ConfirmAssignmentDelete' ) && $http->hasPostVariable( 'RemoveNodeID' ) ) { $nodeID = $http->postVariable( 'RemoveNodeID' ); $db = eZDB::instance(); $db->begin(); $version->removeAssignment( $nodeID ); $db->commit(); } if ( $module->isCurrentAction( 'DeleteNode' ) ) { if ( $http->hasPostVariable( 'RemoveNodeID' ) ) { $nodeID = $http->postVariable( 'RemoveNodeID' ); } $mainNodeID = $http->postVariable( 'MainNodeID' ); // if ( $nodeID != $mainNodeID ) { $objectID = $object->attribute( 'id' ); $publishedNode = eZContentObjectTreeNode::fetchNode( $objectID, $nodeID ); if ( $publishedNode != null ) { $publishParentNodeID = $publishedNode->attribute( 'parent_node_id' ); if ( $publishParentNodeID > 1 ) { $childrenCount = $publishedNode->childrenCount(); if ( $childrenCount != 0 ) { $module->redirectToView( 'removenode', array( $objectID, $editVersion, $editLanguage, $nodeID ) ); return eZModule::HOOK_STATUS_CANCEL_RUN; } else { $db = eZDB::instance(); $db->begin(); $version->removeAssignment( $nodeID ); $db->commit(); } } } else { $nodeAssignment = eZNodeAssignment::fetch( $objectID, $version->attribute( 'version' ), $nodeID ); if ( $nodeAssignment->attribute( 'from_node_id' ) != 0 ) { $publishedNode = eZContentObjectTreeNode::fetchNode( $objectID, $nodeAssignment->attribute( 'from_node_id' ) ); $childrenCount = 0; if ( $publishedNode !== null ) $childrenCount = $publishedNode->childrenCount(); if ( $childrenCount != 0 ) { $module->redirectToView( 'removenode', array( $objectID, $editVersion, $editLanguage, $nodeID ) ); return eZModule::HOOK_STATUS_CANCEL_RUN; } } $db = eZDB::instance(); $db->begin(); $version->removeAssignment( $nodeID ); $db->commit(); } } } if ( $module->isCurrentAction( 'RemoveAssignments' ) ) { if( $http->hasPostVariable( 'AssignmentIDSelection' ) ) { $selected = $http->postVariable( 'AssignmentIDSelection' ); $objectID = $object->attribute( 'id' ); $versionInt = $version->attribute( 'version' ); $hasChildren = false; $assignmentsIDs = array(); $assignments = array(); // Determine if at least one node of ones we remove assignments for has children. foreach ( $selected as $parentNodeID ) { $assignment = eZNodeAssignment::fetch( $objectID, $versionInt, $parentNodeID ); if( !$assignment ) { eZDebug::writeWarning( "No assignment found for object $objectID version $versionInt, parent node $parentNodeID" ); continue; } $assignmentID = $assignment->attribute( 'id' ); $assignmentsIDs[] = $assignmentID; $assignments[] = $assignment; $node = $assignment->attribute( 'node' ); if( !$node ) continue; if( $node->childrenCount( false ) > 0 ) $hasChildren = true; unset( $assignment ); } if ( $hasChildren ) { // We need user confirmation if at least one node we want to remove assignment for contains children. // Aactual removal is done in content/removeassignment in this case. $http->setSessionVariable( 'AssignmentRemoveData', array( 'remove_list' => $assignmentsIDs, 'object_id' => $objectID, 'edit_version' => $versionInt, 'edit_language' => $editLanguage, 'from_language' => $fromLanguage ) ); $module->redirectToView( 'removeassignment' ); return eZModule::HOOK_STATUS_CANCEL_RUN; } else { // Just remove all the selected locations. $mainNodeChanged = false; $db = eZDB::instance(); $db->begin(); foreach ( $assignments as $assignment ) { $assignmentID = $assignment->attribute( 'id' ); if ( $assignment->attribute( 'is_main' ) ) $mainNodeChanged = true; eZNodeAssignment::removeByID( $assignmentID ); } if ( $mainNodeChanged ) eZNodeAssignment::setNewMainAssignment( $objectID, $versionInt ); $db->commit(); unset( $mainNodeChanged ); } unset( $assignmentsIDs, $assignments ); } else { eZDebug::writeNotice( 'No nodes to remove selected' ); } } if ( $module->isCurrentAction( 'MoveNode' ) ) { $objectID = $object->attribute( 'id' ); if ( $http->hasPostVariable( 'MoveNodeID' ) ) { $fromNodeID = $http->postVariable( 'MoveNodeID' ); //$sourceNodeID[0]; $oldAssignmentParentID = $fromNodeID; $fromNodeAssignment = eZNodeAssignment::fetch( $objectID, $version->attribute( 'version' ), $fromNodeID ); $publishParentNodeID = $fromNodeAssignment->attribute( 'parent_node' ); if ( $publishParentNodeID > 1 ) { if( $fromNodeAssignment->attribute( 'from_node_id' ) != 0 ) { $fromNodeID = $fromNodeAssignment->attribute( 'from_node_id' ); $oldAssignmentParentID = $fromNodeAssignment->attribute( 'parent_node' ); } // we don't allow moving object to itself, to its descendants or parent object(s) $objectAssignedNodes = $object->attribute( 'assigned_nodes' ); // nodes that are not allowed to select (via checkbox or radiobutton) when browsing $ignoreNodesSelectArray = array(); // nodes that are not allowed to click on $ignoreNodesClickArray = array(); foreach( $objectAssignedNodes as $curAN ) { // current node should be neither selectable, nor clickable $ignoreNodesClickArray[] = $curAN->NodeID; $ignoreNodesSelectArray[] = $curAN->NodeID; // parent node should be only clickable, but not selectable $ignoreNodesSelectArray[] = $curAN->ParentNodeID; } eZContentBrowse::browse( array( 'action_name' => 'MoveNodeAssignment', 'description_template' => 'design:content/browse_move_placement.tpl', 'keys' => array( 'class' => $class->attribute( 'id' ), 'class_id' => $class->attribute( 'identifier' ), 'classgroup' => $class->attribute( 'ingroup_id_list' ), 'section' => $object->attribute( 'section_id' ) ), 'start_node' => $fromNodeID, 'persistent_data' => array( 'FromNodeID' => $fromNodeID, 'OldAssignmentParentID' => $oldAssignmentParentID ), 'ignore_nodes_select' => $ignoreNodesSelectArray, 'ignore_nodes_click' => $ignoreNodesClickArray, 'content' => array( 'object_id' => $objectID, 'previous_node_id' => $fromNodeID, 'object_version' => $editVersion, 'object_language' => $editLanguage ), 'from_page' => "/content/edit/$objectID/$editVersion/$editLanguage" ), $module ); return eZModule::HOOK_STATUS_CANCEL_RUN; } } } }
// process current action if ($module->isCurrentAction('ConfirmRemoval')) { $http->removeSessionVariable('AssignmentRemoveData'); $assignments = eZNodeAssignment::fetchListByID($removeList); $mainNodeChanged = false; $db = eZDB::instance(); $db->begin(); foreach ($assignments as $assignment) { $assignmentID = $assignment->attribute('id'); if ($assignment->attribute('is_main')) { $mainNodeChanged = true; } eZNodeAssignment::purgeByID($assignmentID); } if ($mainNodeChanged) { eZNodeAssignment::setNewMainAssignment($objectID, $editVersion); } $db->commit(); return $module->redirectToView('edit', array($objectID, $editVersion, $editLanguage, $fromLanguage)); } else { if ($module->isCurrentAction('CancelRemoval')) { $http->removeSessionVariable('AssignmentRemoveData'); return $module->redirectToView('edit', array($objectID, $editVersion, $editLanguage, $fromLanguage)); } } // default action: show the confirmation dialog $assignmentsToRemove = eZNodeAssignment::fetchListByID($removeList); $removeList = array(); $canRemoveAll = true; foreach ($assignmentsToRemove as $assignment) { $node = $assignment->attribute('node');
/** * Unserialize xml structure. Creates an object from xml input. * * Transaction unsafe. If you call several transaction unsafe methods you must enclose * the calls within a db transaction; thus within db->begin and db->commit. * * @param mixed $package * @param DOMElement $domNode * @param array $options * @param int|bool $ownerID Override owner ID, null to use XML owner id (optional) * @param string $handlerType * @return array|bool|eZContentObject|null created object, false if could not create object/xml invalid */ static function unserialize( $package, $domNode, &$options, $ownerID = false, $handlerType = 'ezcontentobject' ) { if ( $domNode->localName != 'object' ) { $retValue = false; return $retValue; } $initialLanguage = eZContentObject::mapLanguage( $domNode->getAttribute( 'initial_language' ), $options ); if( $initialLanguage === 'skip' ) { $retValue = true; return $retValue; } $sectionID = $domNode->getAttributeNS( 'http://ez.no/ezobject', 'section_id' ); if ( $ownerID === false ) { $ownerID = $domNode->getAttributeNS( 'http://ez.no/ezobject', 'owner_id' ); } $remoteID = $domNode->getAttribute( 'remote_id' ); $name = $domNode->getAttribute( 'name' ); $classRemoteID = $domNode->getAttribute( 'class_remote_id' ); $classIdentifier = $domNode->getAttributeNS( 'http://ez.no/ezobject', 'class_identifier' ); $alwaysAvailable = ( $domNode->getAttributeNS( 'http://ez.no/ezobject', 'always_available' ) == '1' ); $contentClass = eZContentClass::fetchByRemoteID( $classRemoteID ); if ( !$contentClass ) { $contentClass = eZContentClass::fetchByIdentifier( $classIdentifier ); } if ( !$contentClass ) { $options['error'] = array( 'error_code' => self::PACKAGE_ERROR_NO_CLASS, 'element_id' => $remoteID, 'description' => "Can't install object '$name': Unable to fetch class with remoteID: $classRemoteID." ); $retValue = false; return $retValue; } /** @var DOMElement $versionListNode */ $versionListNode = $domNode->getElementsByTagName( 'version-list' )->item( 0 ); $importedLanguages = array(); foreach( $versionListNode->getElementsByTagName( 'version' ) as $versionDOMNode ) { /** @var DOMElement $versionDOMNode */ foreach ( $versionDOMNode->getElementsByTagName( 'object-translation' ) as $versionDOMNodeChild ) { /** @var DOMElement $versionDOMNodeChild */ $importedLanguage = eZContentObject::mapLanguage( $versionDOMNodeChild->getAttribute( 'language' ), $options ); $language = eZContentLanguage::fetchByLocale( $importedLanguage ); // Check if the language is allowed in this setup. if ( $language ) { $hasTranslation = true; } else { if ( $importedLanguage == 'skip' ) continue; // if there is no needed translation in system then add it $locale = eZLocale::instance( $importedLanguage ); $translationName = $locale->internationalLanguageName(); $translationLocale = $locale->localeCode(); if ( $locale->isValid() ) { eZContentLanguage::addLanguage( $locale->localeCode(), $locale->internationalLanguageName() ); $hasTranslation = true; } else $hasTranslation = false; } if ( $hasTranslation ) { $importedLanguages[] = $importedLanguage; $importedLanguages = array_unique( $importedLanguages ); } } } // If object exists we return a error. // Minimum install element is an object now. $contentObject = eZContentObject::fetchByRemoteID( $remoteID ); // Figure out initial language if ( !$initialLanguage || !in_array( $initialLanguage, $importedLanguages ) ) { $initialLanguage = false; foreach ( eZContentLanguage::prioritizedLanguages() as $language ) { if ( in_array( $language->attribute( 'locale' ), $importedLanguages ) ) { $initialLanguage = $language->attribute( 'locale' ); break; } } } if ( !$contentObject ) { $firstVersion = true; $contentObject = $contentClass->instantiateIn( $initialLanguage, $ownerID, $sectionID ); } else { $firstVersion = false; $description = "Object '$name' already exists."; $choosenAction = eZPackageHandler::errorChoosenAction( self::PACKAGE_ERROR_EXISTS, $options, $description, $handlerType, false ); switch( $choosenAction ) { case eZPackage::NON_INTERACTIVE: case self::PACKAGE_UPDATE: { // Keep existing contentobject. } break; case self::PACKAGE_REPLACE: { eZContentObjectOperations::remove( $contentObject->attribute( 'id' ) ); unset( $contentObject ); $contentObject = $contentClass->instantiateIn( $initialLanguage, $ownerID, $sectionID ); $firstVersion = true; } break; case self::PACKAGE_SKIP: { $retValue = true; return $retValue; } break; case self::PACKAGE_NEW: { $contentObject->setAttribute( 'remote_id', eZRemoteIdUtility::generate( 'object' ) ); $contentObject->store(); unset( $contentObject ); $contentObject = $contentClass->instantiateIn( $initialLanguage, $ownerID, $sectionID ); $firstVersion = true; } break; default: { $options['error'] = array( 'error_code' => self::PACKAGE_ERROR_EXISTS, 'element_id' => $remoteID, 'description' => $description, 'actions' => array( self::PACKAGE_REPLACE => ezpI18n::tr( 'kernel/classes', 'Replace existing object' ), self::PACKAGE_SKIP => ezpI18n::tr( 'kernel/classes', 'Skip object' ), self::PACKAGE_NEW => ezpI18n::tr( 'kernel/classes', 'Keep existing and create a new one' ), self::PACKAGE_UPDATE => ezpI18n::tr( 'kernel/classes', 'Update existing object' ) ) ); $retValue = false; return $retValue; } break; } } $db = eZDB::instance(); $db->begin(); if ( $alwaysAvailable ) { // Make sure always available bit is set. $contentObject->setAttribute( 'language_mask', (int)$contentObject->attribute( 'language_mask' ) | 1 ); } $contentObject->setAttribute( 'section_id', $sectionID ); $contentObject->store(); $activeVersion = false; $lastVersion = false; $versionListActiveVersion = $versionListNode->getAttribute( 'active_version' ); $contentObject->setAttribute( 'remote_id', $remoteID ); $contentObject->setAttribute( 'contentclass_id', $contentClass->attribute( 'id' ) ); $contentObject->store(); $sectionObject = eZSection::fetch( $sectionID ); if ( $sectionObject instanceof eZSection ) { $updateWithParentSection = false; } else { $updateWithParentSection = true; } $options['language_array'] = $importedLanguages; $versionList = array(); foreach( $versionListNode->getElementsByTagName( 'version' ) as $versionDOMNode ) { unset( $nodeList ); $nodeList = array(); $contentObjectVersion = eZContentObjectVersion::unserialize( $versionDOMNode, $contentObject, $ownerID, $sectionID, $versionListActiveVersion, $firstVersion, $nodeList, $options, $package, 'ezcontentobject', $initialLanguage ); if ( !$contentObjectVersion ) { $db->commit(); $retValue = false; return $retValue; } $versionStatus = $versionDOMNode->getAttributeNS( 'http://ez.no/ezobject', 'status' ); $versionList[$versionDOMNode->getAttributeNS( 'http://ez.no/ezobject', 'version' )] = array( 'node_list' => $nodeList, 'status' => $versionStatus ); unset( $versionStatus ); $firstVersion = false; $lastVersion = $contentObjectVersion->attribute( 'version' ); if ( $versionDOMNode->getAttribute( 'version' ) == $versionListActiveVersion ) { $activeVersion = $contentObjectVersion->attribute( 'version' ); } eZNodeAssignment::setNewMainAssignment( $contentObject->attribute( 'id' ), $lastVersion ); eZOperationHandler::execute( 'content', 'publish', array( 'object_id' => $contentObject->attribute( 'id' ), 'version' => $lastVersion ) ); $mainNodeInfo = null; foreach ( $nodeList as $nodeInfo ) { if ( $nodeInfo['is_main'] ) { $mainNodeInfo =& $nodeInfo; break; } } if ( $mainNodeInfo ) { $existingMainNode = eZContentObjectTreeNode::fetchByRemoteID( $mainNodeInfo['parent_remote_id'], false ); if ( $existingMainNode ) { eZContentObjectTreeNode::updateMainNodeID( $existingMainNode['node_id'], $mainNodeInfo['contentobject_id'], $mainNodeInfo['contentobject_version'], $mainNodeInfo['parent_node'], $updateWithParentSection ); } } unset( $mainNodeInfo ); // Refresh $contentObject from DB. $contentObject = eZContentObject::fetch( $contentObject->attribute( 'id' ) ); } if ( !$activeVersion ) { $activeVersion = $lastVersion; } /* $contentObject->setAttribute( 'current_version', $activeVersion ); */ $contentObject->setAttribute( 'name', $name ); if ( isset( $options['use_dates_from_package'] ) && $options['use_dates_from_package'] ) { $contentObject->setAttribute( 'published', eZDateUtils::textToDate( $domNode->getAttributeNS( 'http://ez.no/ezobject', 'published' ) ) ); $contentObject->setAttribute( 'modified', eZDateUtils::textToDate( $domNode->getAttributeNS( 'http://ez.no/ezobject', 'modified' ) ) ); } $contentObject->store(); $versions = $contentObject->versions(); $objectName = $contentObject->name(); $objectID = $contentObject->attribute( 'id' ); foreach ( $versions as $version ) { $versionNum = $version->attribute( 'version' ); $oldVersionStatus = $version->attribute( 'status' ); $newVersionStatus = isset( $versionList[$versionNum] ) ? $versionList[$versionNum]['status'] : null; // set the correct status for non-published versions if ( isset( $newVersionStatus ) && $oldVersionStatus != $newVersionStatus && $newVersionStatus != eZContentObjectVersion::STATUS_PUBLISHED ) { $version->setAttribute( 'status', $newVersionStatus ); $version->store( array( 'status' ) ); } // when translation does not have object name set then we copy object name from the current object version $translations = $version->translations( false ); if ( !$translations ) continue; foreach ( $translations as $translation ) { if ( ! $contentObject->name( $versionNum, $translation ) ) { eZDebug::writeNotice( "Setting name '$objectName' for version ($versionNum) of the content object ($objectID) in language($translation)" ); $contentObject->setName( $objectName, $versionNum, $translation ); } } } foreach ( $versionList[$versionListActiveVersion]['node_list'] as $nodeInfo ) { unset( $parentNode ); $parentNode = eZContentObjectTreeNode::fetchNode( $contentObject->attribute( 'id' ), $nodeInfo['parent_node'] ); if ( is_object( $parentNode ) ) { $parentNode->setAttribute( 'priority', $nodeInfo['priority'] ); $parentNode->store( array( 'priority' ) ); } } $db->commit(); return $contentObject; }
/** * Removes previously selected, now unselected assignments. * * This hook is run at "post_store" time. * * @param mixed $module Is eZModule. * @param mixed $class Is eZContentClass. * @param mixed $object Is eZContentObject. * @param mixed $version Is eZContentObjectVersion. * @param mixed $contentObjectAttributes Is eZContentObjectAttribute. * @param string $editVersion Number as String. * @param string $editLanguage E.g. eng-GB. * @param mixed $fromLanguage Or false. * @param mixed &$validation Array. * * @return int eZModule::HOOK_STATUS_CANCEL_RUN */ public function removeNodeAssignments($module, $class, $object, $version, $contentObjectAttributes, $editVersion, $editLanguage, $fromLanguage, &$validation) { $http = eZHTTPTool::instance(); if (!$http->hasPostVariable('ymcNodeAssignmentsPool')) { return eZModule::HOOK_STATUS_OK; } $ymcActiveNodeAssignmentsPool = $http->postVariable('ymcActiveNodeAssignmentsPool'); $ymcNodeAssignmentsPool = $http->postVariable('ymcNodeAssignmentsPool'); if (!is_array($ymcActiveNodeAssignmentsPool)) { $ymcActiveNodeAssignmentsPool = array(); } if (!is_array($ymcNodeAssignmentsPool)) { $ymcNodeAssignmentsPool = array(); } $selected = array(); foreach ($ymcNodeAssignmentsPool as $ymcAllNodeAssignment) { if ((int) $ymcAllNodeAssignment > 0 and !in_array($ymcAllNodeAssignment, $ymcActiveNodeAssignmentsPool)) { $selected[] = (int) $ymcAllNodeAssignment; } } $objectID = $object->attribute('id'); $versionInt = $version->attribute('version'); $hasChildren = false; $assignmentsIDs = array(); $assignments = array(); // Determine if at least one node of ones we remove assignments for has children. foreach ($selected as $parentNodeID) { $assignment = eZNodeAssignment::fetch($objectID, $versionInt, $parentNodeID); if (!$assignment) { eZDebug::writeWarning("[ymcEdit] No assignment found for object {$objectID} version {$versionInt},\n parent node {$parentNodeID}"); continue; } $assignmentID = $assignment->attribute('id'); $assignmentsIDs[] = $assignmentID; $assignments[] =& $assignment; $node =& $assignment->attribute('node'); if (!$node) { continue; } if ($node->childrenCount(false) > 0) { $hasChildren = true; } unset($assignment); } if ($hasChildren) { // We need user confirmation if at least one node we want to // remove assignment for contains children. Aactual removal is // done in content/removeassignment in this case. $http->setSessionVariable('AssignmentRemoveData', array('remove_list' => $assignmentsIDs, 'object_id' => $objectID, 'edit_version' => $versionInt, 'edit_language' => $editLanguage, 'from_language' => $fromLanguage)); $module->redirectToView('removeassignment'); return eZModule::HOOK_STATUS_CANCEL_RUN; } else { // Just remove all the selected locations. $mainNodeChanged = false; $db = eZDB::instance(); $db->begin(); foreach ($assignments as $assignment) { $assignmentID = $assignment->attribute('id'); if ($assignment->attribute('is_main')) { $mainNodeChanged = true; } eZNodeAssignment::removeByID($assignmentID); } if ($mainNodeChanged) { eZNodeAssignment::setNewMainAssignment($objectID, $versionInt); } $db->commit(); unset($mainNodeChanged); } unset($assignmentsIDs, $assignments); }