Index: kernel/db/db_event_handler.php =================================================================== --- kernel/db/db_event_handler.php (revision 14318) +++ kernel/db/db_event_handler.php (working copy) @@ -2931,4 +2931,15 @@ $clones[$subitem_prefix] = Array ('ParentPrefix' => $event->Prefix); $this->Application->setUnitOption($event->MasterEvent->Prefix, 'Clones', $clones); } + + /** + * Returns constrain for priority calculations + * + * @see PriorityEventHandler + * @param kEvent $event + */ + function OnGetConstrainInfo(&$event) + { + $event->setEventParam('constrain_info', Array ('', '')); + } } \ No newline at end of file Index: units/categories/categories_event_handler.php =================================================================== --- units/categories/categories_event_handler.php (revision 14318) +++ units/categories/categories_event_handler.php (working copy) @@ -763,12 +763,6 @@ $object->SetDBField('Template', $this->_getDefaultDesign()); } - $priority_helper =& $this->Application->recallObject('PriorityHelper'); - /* @var $priority_helper kPriorityHelper */ - - // 3. prepare priorities dropdown - $priority_helper->preparePriorities($event, true, 'ParentId = ' . $category_id); - // 4. set default owner $object->SetDBField('CreatedById', $this->Application->RecallVar('user_id')); } @@ -801,16 +795,6 @@ return ; } - // 1. update priorities - $tmp = $this->Application->RecallVar('priority_changes'.$this->Application->GetVar('m_wid')); - $changes = $tmp ? unserialize($tmp) : Array (); - $changed_ids = array_keys($changes); - - $priority_helper =& $this->Application->recallObject('PriorityHelper'); - /* @var $priority_helper kPriorityHelper */ - - $priority_helper->updatePriorities($event, $changes, Array (0 => $event->getEventParam('ids'))); - if ($this->Application->RecallVar('PermCache_UpdateRequired')) { $this->Application->RemoveVar('IsRootCategory_' . $this->Application->GetVar('m_wid')); } @@ -819,7 +803,7 @@ $this->_resetMenuCache(); if ($is_editing) { - // 2. send email event to category owner, when it's status is changed (from admin) + // send email event to category owner, when it's status is changed (from admin) $object->SwitchToLive(); $new_statuses = $this->_getCategoryStatus($ids); $process_statuses = Array (STATUS_ACTIVE, STATUS_DISABLED); @@ -867,12 +851,6 @@ return ; } - $priority_helper =& $this->Application->recallObject('PriorityHelper'); - /* @var $priority_helper kPriorityHelper */ - - $category_id = $this->Application->GetVar('m_cat_id'); - $priority_helper->preparePriorities($event, true, 'ParentId = ' . $category_id); - parent::OnPreSaveCreated($event); } @@ -895,7 +873,6 @@ $this->Conn->Query($sql); } - /** * Exclude root categories from deleting * @@ -920,25 +897,6 @@ $this->Application->StoreVar('root_delete_error', 1); } } - - $change_events = Array ('OnPreSave', 'OnPreSaveCreated', 'OnUpdate', 'OnSave'); - if ($type == 'after' && in_array($event->Name, $change_events)) { - $object =& $event->getObject(); - - $tmp = $this->Application->RecallVar('priority_changes'.$this->Application->GetVar('m_wid')); - $changes = $tmp ? unserialize($tmp) : array(); - - if (!isset($changes[$object->GetID()])) { - $changes[$object->GetId()]['old'] = $object->GetID() == 0 ? 'new' : $object->GetDBField('OldPriority'); - } - - if ($changes[$object->GetId()]['old'] == $object->GetDBField('Priority')) return ; - - $changes[$object->GetId()]['new'] = $object->GetDBField('Priority'); - $changes[$object->GetId()]['parent'] = $object->GetDBField('ParentId'); - - $this->Application->StoreVar('priority_changes'.$this->Application->GetVar('m_wid'), serialize($changes)); - } } /** @@ -1026,14 +984,6 @@ } $this->clearSelectedIDs($event); - // update priorities - $priority_helper =& $this->Application->recallObject('PriorityHelper'); - /* @var $priority_helper kPriorityHelper */ - - // after deleting categories, all priorities should be recalculated - $parent_id = $this->Application->GetVar('m_cat_id'); - $priority_helper->recalculatePriorities($event, 'ParentId = ' . $parent_id); - $this->Application->StoreVar('RefreshStructureTree', 1); $this->_resetMenuCache(); } @@ -1159,13 +1109,21 @@ /* @var $priority_helper kPriorityHelper */ if ($clipboard_data['cut']) { - $priority_helper->recalculatePriorities($event, 'ParentId = '.$source_category_id); + $ids = $priority_helper->recalculatePriorities($event, 'ParentId = '.$source_category_id); + + if ($ids) { + $priority_helper->massUpdateChanged($event->Prefix, $ids); + } } // recalculate priorities of newly pasted categories in destination category $parent_id = $this->Application->GetVar('m_cat_id'); - $priority_helper->recalculatePriorities($event, 'ParentId = ' . $parent_id); + $ids = $priority_helper->recalculatePriorities($event, 'ParentId = ' . $parent_id); + if ($ids) { + $priority_helper->massUpdateChanged($event->Prefix, $ids); + } + if ($clipboard_data['cut'] || $clipboard_data['copy']) { // rebuild with progress bar if ($this->Application->ConfigValue('QuickCategoryPermissionRebuild')) { @@ -1966,37 +1924,9 @@ unset($field_options['options'][$remove_category]); } $object->SetFieldOptions('ParentId', $field_options); - - $priority_helper =& $this->Application->recallObject('PriorityHelper'); - /* @var $priority_helper kPriorityHelper */ - - $priority_helper->preparePriorities($event, false, 'ParentId = '.$object->GetDBField('ParentId')); - - // storing prioriry right after load for comparing when updating - $object->SetDBField('OldPriority', $object->GetDBField('Priority')); } /** - * Builds list - * - * @param kEvent $event - * @access protected - */ - function OnListBuild(&$event) - { - parent::OnListBuild($event); - - if (!$this->Application->isAdminUser) { - return ; - } - - $priority_helper =& $this->Application->recallObject('PriorityHelper'); - /* @var $priority_helper kPriorityHelper */ - - $priority_helper->preparePriorities($event, false, 'ParentId = '.$this->Application->GetVar('m_cat_id')); - } - - /** * Enter description here... * * @param kEvent $event @@ -2059,39 +1989,9 @@ return; } - $object =& $event->getObject( Array('skip_autoload' => true) ); - $ids = $this->StoreSelectedIDs($event); + $this->Application->SetVar('priority_prefix', $event->getPrefixSpecial()); + $event->CallSubEvent('priority:' . $event->Name); - if ($ids) { - $id_field = $this->Application->getUnitOption($event->Prefix, 'IDField'); - $table_name = $this->Application->getUnitOption($event->Prefix, 'TableName'); - $parent_id = $this->Application->GetVar('m_cat_id'); - - $sql = 'SELECT Priority, '.$id_field.' - FROM '.$table_name.' - WHERE '.$id_field.' IN ('.implode(',', $ids).')'; - $priorities = $this->Conn->GetCol($sql, $id_field); - - $priority_helper =& $this->Application->recallObject('PriorityHelper'); - /* @var $priority_helper kPriorityHelper */ - - foreach ($ids as $id) { - $new_priority = $priorities[$id] + ($event->Name == 'OnMassMoveUp' ? +1 : -1); - - $changes = Array ( - $id => Array ('old' => $priorities[$id], 'new' => $new_priority, 'parent' => $parent_id), - ); - - $sql = 'UPDATE '.$table_name.' - SET Priority = '.$new_priority.' - WHERE '.$id_field.' = '.$id; - $this->Conn->Query($sql); - - $priority_helper->updatePriorities($event, $changes, Array ($id => $id)); - } - } - - $this->clearSelectedIDs($event); $this->Application->StoreVar('RefreshStructureTree', 1); $this->_resetMenuCache(); } @@ -2108,11 +2008,9 @@ return; } - $priority_helper =& $this->Application->recallObject('PriorityHelper'); - /* @var $priority_helper kPriorityHelper */ + $this->Application->SetVar('priority_prefix', $event->getPrefixSpecial()); + $event->CallSubEvent('priority:OnRecalculatePriorities'); - $parent_id = $this->Application->GetVar('m_cat_id'); - $priority_helper->recalculatePriorities($event, 'ParentId = ' . $parent_id); $this->_resetMenuCache(); } @@ -2438,4 +2336,34 @@ $object->setID($id); } } + + /** + * Returns constrain for priority calculations + * + * @see PriorityEventHandler + * @param kEvent $event + */ + function OnGetConstrainInfo(&$event) + { + $contarain = ''; // for OnSave + + $event_name = $event->getEventParam('original_event'); + $actual_event_name = $event->getEventParam('actual_event'); + + if ( $actual_event_name == 'OnSavePriorityChanges' || $event_name == 'OnAfterItemLoad' || $event_name == 'OnAfterItemDelete' ) { + $object =& $event->getObject(); + $contarain = 'ParentId = ' . $object->GetDBField('ParentId'); + } + elseif ( $actual_event_name == 'OnPreparePriorities' ) { + $contarain = 'ParentId = ' . $this->Application->GetVar('m_cat_id'); + } + elseif ($event_name == 'OnSave') { + $contarain = ''; + } + else { + $contarain = 'ParentId = ' . $this->Application->GetVar('m_cat_id'); + } + + $event->setEventParam('constrain_info', Array ($contarain, '')); + } } \ No newline at end of file Index: units/helpers/priority_helper.php =================================================================== --- units/helpers/priority_helper.php (revision 14318) +++ units/helpers/priority_helper.php (working copy) @@ -16,16 +16,16 @@ class kPriorityHelper extends kHelper { - /** * Prepares options for priority dropdown * * @param kEvent $event * @param bool $is_new for newly created items add new priority to the end * @param string $constrain constrain for priority selection (if any) + * @param string $joins left joins, used by constrain (if any) * */ - function preparePriorities(&$event, $is_new = false, $constrain = '') + function preparePriorities(&$event, $is_new = false, $constrain = '', $joins = '') { $object =& $event->getObject(); @@ -33,30 +33,33 @@ $table_name = $this->Application->getUnitOption($event->Prefix, 'TableName'); $sql = 'SELECT COUNT(*) - FROM '.$table_name; - if ($constrain) { - $sql .= ' WHERE '.$constrain; + FROM '.$table_name . ' item_table + ' . $joins; + + if ( $constrain ) { + $sql .= ' WHERE ' . $this->normalizeConstrain($constrain); } - if (!isset($object->Fields['OldPriority'])) { + if ( !isset($object->Fields['OldPriority']) ) { $object->VirtualFields['OldPriority'] = Array('type' => 'int', 'default' => 0); } $items_count = $this->Conn->GetOne($sql); - // instanceof is not used, because PHP4 doesn't support it + // TODO: instanceof is not used, because PHP4 doesn't support it $current_priority = is_a($object, 'kDBList') ? 0 : $object->GetDBField('Priority'); - if ($is_new || $current_priority == -($items_count+1)) { + if ( $is_new || $current_priority == -($items_count + 1) ) { $items_count++; } - if ($is_new) { + if ( $is_new ) { // add new item to the end of list $object->SetDBField('Priority', -$items_count); $object->SetDBField('OldPriority', -$items_count); } else { + // storing prioriry right after load for comparing when updating $object->SetDBField('OldPriority', $current_priority); } @@ -65,26 +68,29 @@ } $object->SetFieldOptions('Priority', $field_options); - // storing prioriry right after load for comparing when updating } /** * Updates priorities for changed items * * @param kEvent $event - * @param Array $changes = Array (ID => Array ('parent' => ..., 'new' => ..., 'old' => ...), ...) + * @param Array $changes = Array (ID => Array ('constrain' => ..., 'new' => ..., 'old' => ...), ...) * @param Array $new_ids = Array (temp_id => live_id) * @param string $constrain + * @param string $joins */ - function updatePriorities(&$event, $changes, $new_ids, $constrain = '') + function updatePriorities(&$event, $changes, $new_ids, $constrain = '', $joins = '') { - if (!$changes) { + // TODO: no need pass external $constrain, since the one from $pair is used + + if ( !$changes ) { // no changes to process return Array (); } + list ($id, $pair) = each($changes); - if (!$id && !array_key_exists('parent', $pair)) { + if ( !$id && !isset($pair['constrain']) ) { // adding new item without constrain -> priority stays the same return Array ($id); } @@ -92,57 +98,69 @@ $id_field = $this->Application->getUnitOption($event->Prefix, 'IDField'); $table_name = $this->Application->getUnitOption($event->Prefix, 'TableName'); - $ids = array(); + $ids = Array (); $not_processed = array_keys($changes); foreach ($changes as $id => $pair) { array_push($ids, $id); - $constrain = isset($pair['parent']) ? 'ParentId = '.$pair['parent'].' AND ' : ''; - if ($pair['old'] == 'new') { + $constrain = isset($pair['constrain']) ? $this->normalizeConstrain($pair['constrain']) . ' AND ' : ''; + + if ( $pair['old'] == 'new' ) { // replace 0 with newly created item id (from $new_ids mapping) $not_processed[ array_search($id, $not_processed) ] = $new_ids[$id]; $id = $new_ids[$id]; - $sql = 'SELECT MIN(Priority) - FROM '.$table_name.' - WHERE '.$constrain.' '.$id_field.' NOT IN ('.implode(',', $not_processed).')'; + $sql = 'SELECT MIN(item_table.Priority) + FROM ' . $table_name . ' item_table + ' . $joins . ' + WHERE ' . $constrain . ' ' . $id_field . ' NOT IN (' . implode(',', $not_processed) . ')'; $min_priority = (int)$this->Conn->GetOne($sql) - 1; if ($pair['new'] < $min_priority) { $pair['new'] = $min_priority; } + $pair['old'] = $min_priority; } - if ($pair['new'] < $pair['old']) { - $set = ' SET Priority = Priority + 1'; - $where =' WHERE '.$constrain.' - Priority >= '.$pair['new'].' + if ( $pair['new'] < $pair['old'] ) { + $set = 'SET item_table.Priority = item_table.Priority + 1'; + $where =' WHERE ' . $constrain . ' + item_table.Priority >= ' . $pair['new'] . ' AND - Priority < '.$pair['old'].' + item_table.Priority < ' . $pair['old'] . ' AND - '.$id_field.' NOT IN ('.implode(',', $not_processed).')'; + ' . $id_field . ' NOT IN (' . implode(',', $not_processed) . ')'; } - elseif ($pair['new'] > $pair['old']) { - $set = ' SET Priority = Priority - 1'; - $where =' WHERE '.$constrain.' - Priority > '.$pair['old'].' + elseif ( $pair['new'] > $pair['old'] ) { + $set = 'SET item_table.Priority = item_table.Priority - 1'; + $where =' WHERE ' . $constrain . ' + item_table.Priority > ' . $pair['old'] . ' AND - Priority <= '.$pair['new'].' + item_table.Priority <= ' . $pair['new'] . ' AND - '.$id_field.' NOT IN ('.implode(',', $not_processed).')'; + ' . $id_field . ' NOT IN (' . implode(',', $not_processed) . ')'; } else { - $set = 'SET Priority = '.$pair['new']; - $where = ' WHERE '.$id_field.' = '.$id; + $set = 'SET item_table.Priority = ' . $pair['new']; + $where = ' WHERE ' . $id_field . ' = ' . $id; } - $ids = array_merge($ids, $this->Conn->GetCol('SELECT '.$id_field.' FROM '.$table_name.$where)); - $q = 'UPDATE '.$table_name.' '.$set.$where; + + $sql = 'SELECT item_table.' . $id_field . ' + FROM ' . $table_name . ' item_table + ' . $joins . ' + ' . $where; + $ids = array_merge($ids, $this->Conn->GetCol($sql)); + + $q = 'UPDATE ' . $table_name . ' item_table + ' . $joins . ' + ' . $set . $where; $this->Conn->Query($q); unset( $not_processed[array_search($id, $not_processed)] ); } + return $ids; } @@ -151,25 +169,67 @@ * * @param kEvent $event * @param string $constrain + * @param string $joins */ - function recalculatePriorities(&$event, $constrain = '') + function recalculatePriorities(&$event, $constrain = '', $joins = '') { $id_field = $this->Application->getUnitOption($event->Prefix, 'IDField'); $table_name = $this->Application->getUnitOption($event->Prefix, 'TableName'); + $constrain = $this->normalizeConstrain($constrain); - $sql = 'SELECT '.$id_field.' - FROM '.$table_name. - ($constrain ? ' WHERE '.$constrain : '').' - ORDER BY Priority DESC'; + $sql = 'SELECT ' . $id_field . ' + FROM ' . $table_name . ' item_table ' . + $joins . ' ' . + ($constrain ? ' WHERE ' . $constrain : '') . ' + ORDER BY item_table.Priority DESC'; $items = $this->Conn->GetCol($sql); foreach ($items as $item_number => $item_id) { - $sql = 'UPDATE '.$table_name.' - SET Priority = '.-($item_number + 1).' - WHERE '.$id_field.' = '.$item_id; + $sql = 'UPDATE ' . $table_name . ' + SET Priority = ' . -($item_number + 1) . ' + WHERE ' . $id_field . ' = ' . $item_id; $this->Conn->Query($sql); } + return $items; } + + /** + * Adds current table name into constrain if doesn't have it already (to prevent ambiguous columns during joins) + * + * @param string $constrain + * @return string + */ + function normalizeConstrain($constrain) + { + if ( strpos($constrain, '.') === false ) { + return 'item_table.' . $constrain; + } + + return $constrain; + } + + /** + * Peforms fake kDBItem::Update call, so any OnBefore/OnAfter events would be notified of priority change + * + * @param string $prefix + * @param Array $ids + */ + function massUpdateChanged($prefix, $ids) + { + $ids = array_unique($ids); + + $dummy =& $this->Application->recallObject($prefix . '.-dummy', null, Array ('skip_autoload' => true)); + /* @var $dummy kDBItem */ + + $sql = $dummy->GetSelectSQL() . ' + WHERE ' . $dummy->TableName . '.' . $dummy->IDField . ' IN (' . implode(',', $ids) . ')'; + $records = $this->Conn->Query($sql); + + foreach ($records as $record) { + $dummy->LoadFromHash($record); + $dummy->Update(); + } + } } \ No newline at end of file Index: units/priorites/priorites_config.php =================================================================== --- units/priorites/priorites_config.php (revision 0) +++ units/priorites/priorites_config.php (revision 0) @@ -0,0 +1,31 @@ + 'priority', + 'EventHandlerClass' => Array ('class' => 'PriorityEventHandler', 'file' => 'priority_eh.php', 'build_event' => 'OnBuild'), + + 'QueryString' => Array ( + 1 => 'prefix', + 2 => 'event', + ), + + 'Hooks' => Array ( + Array ( + 'Mode' => hAFTER, + 'Conditional' => false, + 'HookToPrefix' => 'adm', + 'HookToSpecial' => '*', + 'HookToEvent' => Array ('OnBeforeShutdown'), + 'DoPrefix' => 'priority', + 'DoSpecial' => '*', + 'DoEvent' => 'OnBeforeShutdown', + 'Conditional' => false, + ), + ), + + 'PermSection' => Array ('main' => 'custom'), + + 'ProcessPrefixes' => Array( + 'c', 'st' + ), + ); Index: units/priorites/priority_eh.php =================================================================== --- units/priorites/priority_eh.php (revision 0) +++ units/priorites/priority_eh.php (revision 0) @@ -0,0 +1,369 @@ + Array ('self' => true), + ); + + $this->permMapping = array_merge($this->permMapping, $permissions); + } + + function mapEvents() + { + parent::mapEvents(); + + $events_map = Array ( + 'OnMassMoveUp' => 'OnChangePriority', + 'OnMassMoveDown' => 'OnChangePriority', + ); + + $this->eventMethods = array_merge($this->eventMethods, $events_map); + } + + /** + * Enter description here... + * + * @param kEvent $event + */ + function OnAfterConfigRead(&$event) + { + $hooks = Array( + Array( + 'Mode' => hAFTER, + 'Conditional' => false, + 'HookToPrefix' => '', + 'HookToSpecial' => '*', + 'HookToEvent' => Array('OnAfterItemLoad', 'OnPreCreate', 'OnListBuild'), + 'DoPrefix' => 'priority', + 'DoSpecial' => '*', + 'DoEvent' => 'OnPreparePriorities', + 'Conditional' => false, + ), + Array( + 'Mode' => hBEFORE, + 'Conditional' => false, + 'HookToPrefix' => '', + 'HookToSpecial' => '*', + 'HookToEvent' => Array('OnPreSaveCreated'), + 'DoPrefix' => 'priority', + 'DoSpecial' => '*', + 'DoEvent' => 'OnPreparePriorities', + 'Conditional' => false, + ), + Array( + 'Mode' => hAFTER, + 'Conditional' => false, + 'HookToPrefix' => '', + 'HookToSpecial' => '*', + 'HookToEvent' => Array('OnPreSave', 'OnPreSaveCreated', 'OnSave', 'OnUpdate'), + 'DoPrefix' => 'priority', + 'DoSpecial' => '*', + 'DoEvent' => 'OnSavePriorityChanges', + 'Conditional' => false, + ), + Array( + 'Mode' => hAFTER, + 'Conditional' => false, + 'HookToPrefix' => '', + 'HookToSpecial' => '*', + 'HookToEvent' => Array('OnSave'), + 'DoPrefix' => 'priority', + 'DoSpecial' => '*', + 'DoEvent' => 'OnSaveItems', + 'Conditional' => false, + ), + Array( + 'Mode' => hBEFORE, + 'Conditional' => false, + 'HookToPrefix' => '', + 'HookToSpecial' => '*', + 'HookToEvent' => Array('OnBeforeItemCreate'), + 'DoPrefix' => 'priority', + 'DoSpecial' => '*', + 'DoEvent' => 'OnItemCreate', + 'Conditional' => false, + ), + Array( + 'Mode' => hBEFORE, + 'Conditional' => false, + 'HookToPrefix' => '', + 'HookToSpecial' => '*', + 'HookToEvent' => Array('OnAfterItemDelete'), + 'DoPrefix' => 'priority', + 'DoSpecial' => '*', + 'DoEvent' => 'OnItemDelete', + 'Conditional' => false, + ) + ); + + $prefixes = $this->Application->getUnitOption($event->Prefix, 'ProcessPrefixes'); + + foreach ($prefixes as $prefix) { + foreach ($hooks as $hook) { + if ( !is_array($hook['HookToEvent']) ) { + $hook['HookToEvent'] = Array($hook['HookToEvent']); + } + + foreach ($hook['HookToEvent'] as $hook_event) { + $this->Application->registerHook( + $prefix, + $hook['HookToSpecial'], + $hook_event, + $hook['Mode'], + $event->Prefix, + $hook['DoSpecial'], + $hook['DoEvent'], + $hook['Conditional'] + ); + } + } + } + } + + /** + * Should be hooked to OnAfterItemLoad, OnPreSaveCreated (why latter?) + * + * @param kEvent $event + */ + function OnPreparePriorities(&$event) + { + if ( !$this->Application->isAdminUser ) { + return ; + } + + $priority_helper =& $this->Application->recallObject('PriorityHelper'); + /* @var $priority_helper kPriorityHelper */ + + list ($constrain, $joins) = $this->getConstrainInfo($event); + $is_new = $event->MasterEvent->Name == 'OnPreCreate' || $event->MasterEvent->Name == 'OnPreSaveCreated'; + $priority_helper->preparePriorities($event->MasterEvent, $is_new, $constrain, $joins); + } + + /** + * Enter description here... + * + * @param kEvent $event + */ + function OnSavePriorityChanges(&$event) + { + if ($event->MasterEvent->status != erSUCCESS) { + // don't update priorities, when OnSave validation failed + return ; + } + + $object =& $event->MasterEvent->getObject(); + + $tmp = $this->Application->RecallVar('priority_changes'.$this->Application->GetVar('m_wid')); + $changes = $tmp ? unserialize($tmp) : array(); + + if (!isset($changes[$object->GetID()])) { + $changes[$object->GetId()]['old'] = $object->GetID() == 0 ? 'new' : $object->GetDBField('OldPriority'); + } + + if ($changes[$object->GetId()]['old'] == $object->GetDBField('Priority')) return ; + $changes[$object->GetId()]['new'] = $object->GetDBField('Priority'); + + list ($constrain, $joins) = $this->getConstrainInfo($event); + + if ($constrain) { + $changes[$object->GetId()]['constrain'] = $constrain; + } + + $this->Application->StoreVar('priority_changes'.$this->Application->GetVar('m_wid'), serialize($changes)); + } + + /** + * Enter description here... + * + * @param kEvent $event + */ + function OnItemDelete(&$event) + { + // just store the prefix in which the items were deleted + $del = $this->Application->RecallVar('priority_deleted' . $this->Application->GetVar('m_wid')); + $del = $del ? unserialize($del) : array(); + + list ($constrain, $joins) = $this->getConstrainInfo($event); + $cache_key = crc32($event->MasterEvent->Prefix . ':' . $constrain . ':' . $joins); + + if ( !isset($del[$cache_key]) ) { + $del[$cache_key] = Array ( + 'prefix' => $event->MasterEvent->Prefix, + 'constrain' => $constrain, + 'joins' => $joins, + ); + + $this->Application->StoreVar('priority_deleted' . $this->Application->GetVar('m_wid'), serialize($del)); + } + } + + /** + * Called before script shut-down and recalculate all deleted prefixes, to avoid recalculation on each deleted item + * + * @param kEvent $event + */ + function OnBeforeShutDown(&$event) + { + $del = $this->Application->RecallVar('priority_deleted'.$this->Application->GetVar('m_wid')); + $del = $del ? unserialize($del) : array(); + + $priority_helper =& $this->Application->recallObject('PriorityHelper'); + /* @var $priority_helper kPriorityHelper */ + + foreach ($del as $del_info) { + $dummy_event = new kEvent( array('prefix'=>$del_info['prefix'], 'name'=>'Dummy' ) ); + $ids = $priority_helper->recalculatePriorities($dummy_event, $del_info['constrain'], $del_info['joins']); + + if ($ids) { + $priority_helper->massUpdateChanged($del_info['prefix'], $ids); + } + } + + $this->Application->RemoveVar('priority_deleted'.$this->Application->GetVar('m_wid')); + } + + /** + * Enter description here... + * + * @param kEvent $event + */ + function OnSaveItems(&$event) + { + $tmp = $this->Application->RecallVar('priority_changes'.$this->Application->GetVar('m_wid')); + $changes = $tmp ? unserialize($tmp) : array(); + + $priority_helper =& $this->Application->recallObject('PriorityHelper'); + /* @var $priority_helper kPriorityHelper */ + + list ($constrain, $joins) = $this->getConstrainInfo($event); + $ids = $priority_helper->updatePriorities($event->MasterEvent, $changes, $event->MasterEvent->getEventParam('ids'), $constrain, $joins); + + if ($ids) { + $priority_helper->massUpdateChanged($event->MasterEvent->Prefix, $ids); + } + } + + function OnItemCreate(&$event) + { + $obj =& $event->MasterEvent->getObject(); + if ($obj->GetDBField('Priority') == 0) { + $priority_helper =& $this->Application->recallObject('PriorityHelper'); + /* @var $priority_helper kPriorityHelper */ + + list ($constrain, $joins) = $this->getConstrainInfo($event); + $priority_helper->preparePriorities($event->MasterEvent, true, $constrain, $joins); + } + } + + /** + * Processes OnMassMoveUp, OnMassMoveDown events + * + * @param kEvent $event + */ + function OnChangePriority(&$event) + { + $prefix = $this->Application->GetVar('priority_prefix'); + $dummy_event = new kEvent( array('prefix'=>$prefix, 'name'=>'Dummy' ) ); + + $ids = $this->StoreSelectedIDs($dummy_event); + + if ($ids) { + $id_field = $this->Application->getUnitOption($prefix, 'IDField'); + $table_name = $this->Application->getUnitOption($prefix, 'TableName'); + + $sql = 'SELECT Priority, '.$id_field.' + FROM '.$table_name.' + WHERE '.$id_field.' IN ('.implode(',', $ids).') ORDER BY Priority DESC'; + $priorities = $this->Conn->GetCol($sql, $id_field); + + $priority_helper =& $this->Application->recallObject('PriorityHelper'); + /* @var $priority_helper kPriorityHelper */ + + list ($constrain, $joins) = $this->getConstrainInfo($event); + + $sql = 'SELECT IFNULL(MIN(item_table.Priority), -1) + FROM '.$table_name . ' item_table + ' . $joins; + + if ( $constrain ) { + $sql .= ' WHERE ' . $priority_helper->normalizeConstrain($constrain); + } + + $min_priority = $this->Conn->GetOne($sql); + + foreach ($ids as $id) { + $new_priority = $priorities[$id] + ($event->Name == 'OnMassMoveUp' ? +1 : -1); + if ($new_priority > -1 || $new_priority < $min_priority) { + continue; + } + + $changes = Array ( + $id => Array ('old' => $priorities[$id], 'new' => $new_priority), + ); + + if ($constrain) { + $changes[$id]['constrain'] = $constrain; + } + + $sql = 'UPDATE '.$table_name.' + SET Priority = '.$new_priority.' + WHERE '.$id_field.' = '.$id; + $this->Conn->Query($sql); + + $ids = $priority_helper->updatePriorities($dummy_event, $changes, Array ($id => $id), $constrain, $joins); + + if ($ids) { + $priority_helper->massUpdateChanged($prefix, $ids); + } + } + } + + $this->clearSelectedIDs($dummy_event); + } + + /** + * Completely recalculates priorities in current category + * + * @param kEvent $event + */ + function OnRecalculatePriorities(&$event) + { + $priority_helper =& $this->Application->recallObject('PriorityHelper'); + /* @var $priority_helper kPriorityHelper */ + + $prefix = $this->Application->GetVar('priority_prefix'); + $dummy_event = new kEvent( array('prefix'=>$prefix, 'name'=>'Dummy' ) ); + + list ($constrain, $joins) = $this->getConstrainInfo($event); + $ids = $priority_helper->recalculatePriorities($dummy_event, $constrain, $joins); + + if ($ids) { + $priority_helper->massUpdateChanged($prefix, $ids); + } + } + + /** + * Returns constrain for current priority calculations + * + * @param kEvent $event + * @return Array + */ + function getConstrainInfo(&$event) + { + $constrain_event = new kEvent($event->MasterEvent->getPrefixSpecial() . ':OnGetConstrainInfo'); + $constrain_event->setEventParam('actual_event', $event->Name); + $constrain_event->setEventParam('original_event', $event->MasterEvent->Name); + $this->Application->HandleEvent($constrain_event); + + return $constrain_event->getEventParam('constrain_info'); + } +}