Array ('self' => 'advanced:reset',), 'OnRestartValidation' => Array ('self' => 'advanced:restart',), 'OnContinueValidation' => Array ('self' => 'advanced:continue',), 'OnValidateSelected' => Array ('self' => 'advanced:validate',), 'OnValidateProgress' => Array ('self' => 'advanced:validate|advanced:continue|advanced:restart|advanced:reset',), 'OnCancelValidation' => Array ('self' => 'advanced:validate|advanced:continue|advanced:restart|advanced:reset',), 'OnCronValidation' => Array ('self' => 'advanced:validate|advanced:continue|advanced:restart|advanced:reset',), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Define alternative event processing method names * * @return void * @see kEventHandler::$eventMethods * @access protected */ protected function mapEvents() { parent::mapEvents(); $events_map = Array ( 'OnApproveLinks' => 'iterateItems', 'OnDeclineLinks' => 'iterateItems', ); $this->eventMethods = array_merge($this->eventMethods, $events_map); } /** * Checks user permission to execute given $event * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(kEvent $event) { $check_events = Array ('OnApproveLinks', 'OnDeclineLinks', 'OnDeleteLinks'); if ( in_array($event->Name, $check_events) ) { $ids = $this->_getSelectedIds($event); $perm_value = true; if ( $ids ) { /** @var kPermissionsHelper $perm_helper */ $perm_helper = $this->Application->recallObject('PermissionsHelper'); $items = $perm_helper->GetCategoryItemData('l', $ids); $check_method = $event->Name == 'OnDeleteLinks' ? 'DeleteCheckPermission' : 'ModifyCheckPermission'; foreach ($items as $item_id => $item_data) { if ( $perm_helper->$check_method($item_data['CreatedById'], $item_data['CategoryId'], 'l') == 0 ) { // one of items selected has no permission $perm_value = false; break; } } if ( !$perm_value ) { $event->status = kEvent::erPERM_FAIL; } } return $perm_value; } return parent::CheckPermission($event); } /** * Adds calculates fields for category name * * @param kDBItem|kDBList $object * @param kEvent $event * @return void * @access protected */ protected function prepareObject(&$object, kEvent $event) { parent::prepareObject($object, $event); $object->addCalculatedField('CachedNavbar', 'c.l' . $this->Application->GetVar('m_lang') . '_CachedNavbar'); } /** * Allows to show only invalid links * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(kEvent $event) { parent::SetCustomQuery($event); /** @var kDBList $object */ $object = $event->getObject(); $object->addFilter('primary_category_filter', 'ci.PrimaryCat = 1'); if ( $event->Special == 'invalid' ) { $object->addFilter('status_filter', '%1$s.ValidationStatus = ' . LINK_VALIDATION_INVALID); } } /** * Restarts link validation process * * @param kEvent $event */ function OnRestartValidation($event) { $this->_resetValidation($event); $this->OnContinueValidation($event); } /** * Restarts link validation process * * @param kEvent $event */ function _resetValidation($event) { // 1. delete previous validation results $sql = 'SELECT ' . $this->Application->getUnitOption($event->Prefix, 'IDField') . ' FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName'); $ids = $this->Conn->GetCol($sql); if ($ids) { /** @var kTempTablesHandler $temp_handler */ $temp_handler = $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler'); $temp_handler->DeleteItems($event->Prefix, $event->Special, $ids); } } /** * Validates only selected links * * @param kEvent $event */ function OnValidateSelected($event) { $link_ids = $this->_getSelectedIds($event); if (!$link_ids) { return ; } $validation_data = Array ( 'processed' => 0, 'total' => count($link_ids), 'items' => $link_ids, ); $this->Application->StoreVar($event->Prefix . '_status', serialize($validation_data)); $event->redirect = $this->Application->GetVar('progress_template'); } /** * Validates only links, that were not previously validated * * @param kEvent $event */ function OnContinueValidation($event) { $have_data = $this->_prepareValidation($event); if ($have_data) { $event->redirect = $this->Application->GetVar('progress_template'); } } /** * Performs validation * * @param kEvent $event * @param bool $from_ajax */ function _validate($event, $from_ajax = true) { $validation_data = unserialize( $this->Application->RecallVar($event->Prefix . '_status') ); $i = 0; $link_ids = $validation_data['items']; $per_page = count($link_ids) >= LINK_VALIDATION_PER_PAGE ? LINK_VALIDATION_PER_PAGE : count($link_ids); while ($i < $per_page) { $this->_validateLink($link_ids[$i]); $i++; } // remove processed links from array array_splice($link_ids, 0, LINK_VALIDATION_PER_PAGE); // store validation progress $validation_data['processed'] += $i; $validation_data['items'] = $link_ids; if ($validation_data['processed'] >= $validation_data['total']) { // finished $this->Application->emailAdmin('LINK.VALIDATION.RESULTS'); $this->Application->RemoveVar($event->Prefix . '_status'); return true; } // show progress, proceed to next step $this->Application->StoreVar($event->Prefix . '_status', serialize($validation_data)); if ($from_ajax) { echo $validation_data['processed'] / $validation_data['total'] * 100; $event->status = kEvent::erSTOP; } return false; } /** * Performs validation of links (called from AjaxProgressBar) * * @param kEvent $event */ function OnValidateProgress($event) { $done = $this->_validate($event, true); if ($done) { $this->Application->Redirect( $this->Application->GetVar('finish_template') ); } } /** * Returns categories, that are located inside recycle bin category * * @return Array */ function _getRecycleBinCategories() { $recycle_bin = $this->Application->ConfigValue('RecycleBinFolder'); if (!is_numeric($recycle_bin)) { return Array (); } $recycle_categories = $this->Application->RecallVar('recycle_categories'); if ($recycle_categories === false) { $tree_indexes = $this->Application->getTreeIndex($recycle_bin); $sql = 'SELECT ' . $this->Application->getUnitOption('c', 'IDField') . ' FROM ' . $this->Application->getUnitOption('c', 'TableName') . ' WHERE TreeLeft BETWEEN ' . $tree_indexes['TreeLeft'] . ' AND ' . $tree_indexes['TreeRight']; $recycle_categories = serialize( $this->Conn->GetCol($sql) ); // store recycle bin categories in session to prevent query below happening on each link validation step $this->Application->StoreVar('recycle_categories', $recycle_categories); } return unserialize($recycle_categories); } /** * Checks, that link is located in one of RecycleBin subcategories * * @param unknown_type $resource_id * @return unknown */ function _inRecycleBin($resource_id) { static $recycle_bin = null; if (!isset($recycle_bin)) { $recycle_bin = $this->_getRecycleBinCategories(); } if (!$recycle_bin) { // Recycle Bin not used in system -> link is 100% not there return false; } $sql = 'SELECT CategoryId FROM ' . $this->Application->getUnitOption('l-ci', 'TableName') . ' WHERE ItemResourceId = ' . $resource_id . ' AND PrimaryCat = 1'; return in_array( $this->Conn->GetOne($sql), $recycle_bin); } function _validateLink($link_id) { /** @var kCurlHelper $curl_helper */ $curl_helper = $this->Application->recallObject('CurlHelper'); $sql = 'SELECT Url, ResourceId FROM ' . $this->Application->getUnitOption('l', 'TableName') . ' WHERE ' . $this->Application->getUnitOption('l', 'IDField') . ' = ' . $link_id; $link_data = $this->Conn->GetRow($sql); if (!preg_match('/^(http|https):\/\/(.*)/U', $link_data['Url']) || $this->_inRecycleBin($link_data['ResourceId'])) { return ; } $curl_helper->timeout = LINK_VALIDATION_TIMEOUT; $result = $curl_helper->Send($link_data['Url']); if ($result === false || $curl_helper->lastErrorMsg != '') { $curl_helper->lastHTTPCode = 500; } /** @var kDBItem $link_validation */ $link_validation = $this->Application->recallObject($this->Prefix . '.-item', null, Array ('skip_autoload' => true)); $link_validation->Load($link_id, 'LinkId'); $now = adodb_mktime(); $fields_hash = Array ( 'LinkId' => $link_id, 'ValidationTime_date' => $now, 'ValidationTime_time' => $now, 'ValidationCode' => $curl_helper->lastHTTPCode, 'ValidationStatus' => $curl_helper->lastHTTPCode < 400 ? LINK_VALIDATION_VALID : LINK_VALIDATION_INVALID, ); $link_validation->SetDBFieldsFromHash($fields_hash); return $link_validation->isLoaded() ? $link_validation->Update() : $link_validation->Create(); } /** * Cancels validation (from validation progress bar) * * @param kEvent $event */ function OnCancelValidation($event) { $this->Application->RemoveVar($event->Prefix . '_status'); } /** * Resets validation status for selected * * @param kEvent $event */ function OnResetValidationStatus($event) { $ids = $this->_getSelectedIds($event, true); if (!$ids) { return ; } /** @var kTempTablesHandler $temp_handler */ $temp_handler = $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler'); $temp_handler->DeleteItems($event->Prefix, $event->Special, $ids); } /** * Returns ids, that user has checked in grid * * @param kEvent $event * @param bool $transform convert link ids to link validation ids * @return Array */ function _getSelectedIds($event, $transform = false) { $ids = Array(); $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); if ($items_info) { foreach ($items_info as $id => $field_values) { if ( getArrayValue($field_values, 'ForeignLinkId') ) { // we are not gathering ids by unit idfield here! array_push($ids, $id); } } } if ($transform && $ids) { $sql = 'SELECT ' . $this->Application->getUnitOption($event->Prefix, 'IDField') . ' FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . ' WHERE LinkId IN (' . implode(',', $ids) . ')'; $ids = $this->Conn->GetCol($sql); } return $ids; } /** * Approves/declines selected links * * @param kEvent $event * @return void * @access protected */ protected function iterateItems(kEvent $event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return; } $ids = $this->_getSelectedIds($event); if ( !$ids ) { return; } /** @var kCatDBItem $object */ $object = $this->Application->recallObject('l.-item', null, Array ('skip_autoload' => true)); foreach ($ids as $id) { $object->Load($id); switch ( $event->Name ) { case 'OnApproveLinks': $object->ApproveChanges(); break; case 'OnDeclineLinks': $object->DeclineChanges(); break; } } } /** * Deletes selected links * * @param kEvent $event */ function OnDeleteLinks($event) { if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { $event->status = kEvent::erFAIL; return; } $ids = $this->_getSelectedIds($event); if (!$ids) { return ; } /** @var kTempTablesHandler $temp_handler */ $temp_handler = $this->Application->recallObject('l_TempHandler', 'kTempTablesHandler'); $temp_handler->DeleteItems('l', '', $ids); } /** * [HOOK] Allows to edit links, used in selected link validation records * * @param kEvent $event */ function OnPrepareLinkEditing($event) { // hook to OnAfterConfigRead instead of OnEdit, because fake ids should be available in CheckPermission if ($this->Application->GetVar('l_event') != 'OnEdit') { return ; } $ids = $this->_getSelectedIds($event); $id_field = $this->Application->getUnitOption('l', 'IDField'); $items_info = Array (); foreach ($ids as $id) { $items_info[$id][$id_field] = 'on'; } $this->Application->SetVar('l', $items_info); } /** * Gets all links, that are not yet validated and prepare data * * @param kEvent $event * * @return bool */ function _prepareValidation($event) { // 2. get ids of all links and put them into validation queue $id_field = $this->Application->getUnitOption('l', 'IDField'); $sql = 'SELECT ' . $id_field . ' FROM ' . $this->Application->getUnitOption('l', 'TableName') . ' WHERE LinkId NOT IN (SELECT LinkId FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . ')'; $link_ids = $this->Conn->GetCol($sql); if ($link_ids) { $validation_data = Array ( 'processed' => 0, 'total' => count($link_ids), 'items' => $link_ids, ); $this->Application->StoreVar($event->Prefix . '_status', serialize($validation_data)); // 4K links will be 78KB serialized return true; } return false; } /** * [SCHEDULED TASK] Performs link validation through cron * * @param kEvent $event */ function OnCronValidation($event) { $this->_resetValidation($event); // remove this for continuing to non validated before links $have_data = $this->_prepareValidation($event); if ($have_data) { do { $done = $this->_validate($event, false); } while (!$done); } } /** * Makes calculated fields to go to multilingual link fields * * @param kEvent $event * @return void * @access protected */ protected function OnAfterConfigRead(kEvent $event) { parent::OnAfterConfigRead($event); $calculated_fields = $this->Application->getUnitOption($event->Prefix, 'CalculatedFields'); $calculated_fields['']['LinkName'] = 'l.l' . $this->Application->GetVar('m_lang') . '_Name'; $this->Application->setUnitOption($event->Prefix, 'CalculatedFields', $calculated_fields); } }