Array ('self' => true), 'OnProcessReciprocalLinks' => Array ('self' => true), 'OnSetGrouping' => Array ('self' => 'view'), 'OnStoreSelected' => Array ('self' => 'view'), 'OnMerge' => Array ('self' => 'edit'), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Apply any custom changes to list's sql query * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(kEvent $event) { parent::SetCustomQuery($event); /** @var kDBList $object */ $object = $event->getObject(); if ( !$this->Application->isAdminUser ) { $object->addFilter('expire_filter', '(Expire > ' . adodb_mktime() . ' OR Expire IS NULL)'); } if ( substr($event->Special, 0, 10) == 'duplicates' ) { $object->removeFilter('category_filter'); /** @var LinkHelper $link_helper */ $link_helper = $this->Application->recallObject('LinkHelper'); $grouping = $link_helper->getGrouping($event->getPrefixSpecial()); switch ($event->Special) { case 'duplicates': foreach ($grouping as $group_field) { $object->AddGroupByField($object->TableName . '.' . $group_field); } $object->addFilter('has_dupes_filter', 'DupeCount > 1', kDBList::AGGREGATE_FILTER); break; case 'duplicates-sub': /** @var kDBItem $main_object */ $main_object = $this->Application->recallObject($event->Prefix . '.duplicates'); foreach ($grouping as $field_index => $group_field) { $object->addFilter('dupe_filter_' . $field_index, '%1$s.`' . $group_field . '` = ' . $this->Conn->qstr($main_object->GetDBField($group_field))); } break; } $object->addFilter('primary_filter', TABLE_PREFIX . 'CategoryItems.PrimaryCat = 1'); } } /** * Set groping fields for link duplicate checker * * @param kEvent $event */ function OnSetGrouping($event) { $this->Application->LinkVar($event->getPrefixSpecial(true).'_dupe_fields', $event->getPrefixSpecial().'_dupe_fields'); } /** * Merge duplicate links together (only categories) & delete duplicates * * @param kEvent $event */ function OnMerge($event) { /** @var LinkHelper $link_helper */ $link_helper = $this->Application->recallObject('LinkHelper'); $grouping = $link_helper->getGrouping($event->getPrefixSpecial()); $ids = $this->StoreSelectedIDs($event); if ( !$ids ) { return; } // check, that user has not selected multiple links from same group $primary_links = Array (); $id_field = $this->Application->getUnitOption($event->Prefix, 'IDField'); $table_name = $this->Application->getUnitOption($event->Prefix, 'TableName'); $sql = 'SELECT * FROM ' . $table_name . ' WHERE ' . $id_field . ' IN (' . implode(',', $ids) . ')'; $links = $this->Conn->Query($sql, $id_field); $groping_error = false; foreach ($links as $link_data) { $group_key = ''; foreach ($grouping as $grouping_field) { $group_key .= 'main_table.`' . $grouping_field . '` = ' . $this->Conn->qstr($link_data[$grouping_field]) . ' AND '; } $group_key = substr($group_key, 0, -5); if ( isset($primary_links[$group_key]) ) { $groping_error = true; break; } else { $primary_links[$group_key] = $link_data['ResourceId']; } } if ( !$groping_error ) { /** @var kTempTablesHandler $temp_handler */ $temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler'); $categories_sql = ' SELECT main_table.ResourceId, ci.CategoryId, main_table.' . $id_field . ' FROM ' . $table_name . ' main_table LEFT JOIN ' . TABLE_PREFIX . 'CategoryItems ci ON main_table.ResourceId = ci.ItemResourceId WHERE %s'; foreach ($primary_links as $group_key => $primary_resource_id) { $categories = Array (); $group_links = Array (); $group_categories = $this->Conn->Query(sprintf($categories_sql, $group_key)); foreach ($group_categories as $category_data) { $group_links[$category_data['ResourceId']] = $category_data[$id_field]; $categories[$category_data['ResourceId'] == $primary_resource_id ? 'remove' : 'add'][] = $category_data['CategoryId']; } unset($group_links[$primary_resource_id]); $categories = array_unique(array_diff($categories['add'], $categories['remove'])); if ( $categories ) { // add link to other link categories $values_sql = ''; foreach ($categories as $category_id) { $values_sql .= '(' . $category_id . ',' . $primary_resource_id . ',0),'; } $values_sql = substr($values_sql, 0, -1); $insert_sql = 'INSERT INTO ' . TABLE_PREFIX . 'CategoryItems (CategoryId,ItemResourceId,PrimaryCat) VALUES ' . $values_sql; $this->Conn->Query($insert_sql); } // delete all links from group except primary $temp_handler->DeleteItems($event->Prefix, $event->Special, array_values($group_links)); } } else { $event->status = kEvent::erFAIL; $event->redirect = false; $this->Application->SetVar($event->getPrefixSpecial().'_error', 1); } } /** * Stores ids, that were selected in duplicate checker * * @param kEvent $event * @return void * @access protected */ protected function OnStoreSelected(kEvent $event) { $this->StoreSelectedIDs($event); $event->SetRedirectParam('pass', 'm,' . $event->getPrefixSpecial()); } /** * Allows to enhance link after creation * * @param kEvent $event * @return void * @access protected */ protected function OnCreate(kEvent $event) { parent::OnCreate($event); if ( $event->status != kEvent::erSUCCESS ) { return; } /** @var kDBItem $object */ $object = $event->getObject(); // replace 0 id in post with actual created id (used in enhancement process) $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); kUtil::array_rename_key($items_info, 0, $object->GetID()); $this->Application->SetVar($event->getPrefixSpecial(true), $items_info); // listing was created -> enhance it right away $enhancement_event = new kEvent('ls:OnRequestEnhancement'); $this->Application->HandleEvent($enhancement_event); if ( ($enhancement_event->status == kEvent::erSUCCESS) && strlen($enhancement_event->redirect) ) { $event->SetRedirectParam('next_template', $event->redirect); $event->redirect = $enhancement_event->redirect; } } /** * Adds free listing option to listing type selection * * @param kEvent $event * @return void * @access protected */ protected function OnAfterConfigRead(kEvent $event) { parent::OnAfterConfigRead($event); if (defined('IS_INSTALL') && IS_INSTALL) { return ; } $free_listings = $this->Application->ConfigValue('Link_AllowFreeListings'); $virtual_fields = $this->Application->getUnitOption($event->Prefix, 'VirtualFields'); $virtual_fields['ListingTypeId']['options'] = $free_listings ? Array (0 => 'lu_free_listing') : Array (); $language_id = $this->Application->GetVar('m_lang'); $duplicate_options = array_flip($virtual_fields['DuplicateCheckFields']['options']); $duplicate_options['NAME'] = 'l' . $language_id . '_Name'; $virtual_fields['DuplicateCheckFields']['options'] = array_flip($duplicate_options); $default = $virtual_fields['DuplicateCheckFields']['default']; $virtual_fields['DuplicateCheckFields']['default'] = str_replace('|Name|', '|l' . $language_id . '_Name|', $default); $this->Application->setUnitOption($event->Prefix, 'VirtualFields', $virtual_fields); if (!$this->Application->isAdminUser) { // for now only on Front-End $this->Application->setUnitOption($event->Prefix, 'PopulateMlFields', true); } } /** * contact us form submitted on link details page * * @param kEvent $event */ function OnContactFormSubmit($event) { $fields = Array ( 'ContactFormFullName', 'ContactFormEmail', 'ContactFormSubject', 'ContactFormBody', 'ContactFormCaptcha' ); // reset errors var $this->Application->SetVar('ContactForm_HasErrors', ''); // 1. validate form fields $required_fields = $this->Application->GetVar('FormRequiredFields'); foreach ($fields as $field_name) { $field_value = trim($this->Application->GetVar($field_name)); if (in_array($field_name, $required_fields)) { // custom captcha validation if ($field_name == 'ContactFormCaptcha') { if (!strlen($field_value) || ($field_value != $this->Application->RecallVar($event->Prefix . '_captcha_code'))) { $this->Application->SetVar('error_'.$field_name, 1); /** @var kCaptchaHelper $captcha_helper */ $captcha_helper = $this->Application->recallObject('CaptchaHelper'); $this->Application->StoreVar($event->Prefix . '_captcha_code', $captcha_helper->GenerateCaptchaCode()); $event->status = kEvent::erFAIL; $event->redirect = false; } } // email validation elseif (!strlen($field_value) || ($field_name == 'ContactFormEmail' && !preg_match('/'.REGEX_EMAIL_USER.'@'.REGEX_EMAIL_DOMAIN.'/', $field_value))) { $this->Application->SetVar('error_'.$field_name, 1); $event->status = kEvent::erFAIL; $event->redirect = false; } } } if ($event->status != kEvent::erSUCCESS) { // set errors var $this->Application->SetVar('ContactForm_HasErrors', 1); return ; } $object = $event->getObject(); // get link object /** @var kDBItem $object */ $send_params = Array( 'from_name' => $this->Application->GetVar('ContactFormFullName'), 'from_email' => $this->Application->GetVar('ContactFormEmail'), 'from_subject' => $this->Application->GetVar('ContactFormSubject'), 'message' => $this->Application->GetVar('ContactFormBody'), 'to_linkname' => $object->GetField('Name'), ); $email_sent = $this->Application->emailUser('LINK.CONTACTFORM', $object->GetDBField('CreatedById'), $send_params); if ( $email_sent ) { $event->redirect = $this->Application->GetVar('success_template'); $redirect_params = Array ( 'opener' => 's', 'pass' => 'all', 'thankyou_header' => $this->Application->GetVar('success_label_header'), 'thankyou_text' => $this->Application->GetVar('success_label_body') ); $event->setRedirectParams($redirect_params); $this->Application->emailAdmin('LINK.CONTACTFORM', null, $send_params); } else { $this->Application->SetVar('error_ContactFormEmail', 1); $event->status = kEvent::erFAIL; $event->redirect = false; } } /** * Makes reciprocal check on link, when it is created * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(kEvent $event) { parent::OnBeforeItemCreate($event); $this->_checkLink($event); } /** * Makes reciprocal check on link, when it is updated * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(kEvent $event) { parent::OnBeforeItemUpdate($event); $this->_checkLink($event); } /** * Makes reciprocal check on link & saves results * * @param kEvent $event */ function _checkLink($event) { if (!$this->Application->ConfigValue('ReciprocalLinkChecking')) { return ; } /** @var kDBItem $object */ $object = $event->getObject(); if ($object->GetDBField('Url') != $object->GetOriginalField('Url')) { // check only when url was changed /** @var LinkHelper $link_helper */ $link_helper = $this->Application->recallObject('LinkHelper'); $link_checked = $link_helper->CheckReciprocalURL($object->GetDBField('Url')); $object->SetDBField('ReciprocalLinkFound', $link_checked ? LINK_IS_RECIPROCAL : LINK_IS_NOT_RECIPROCAL); if (!$link_checked) { $this->Application->emailAdmin('LINK.RECIPROCAL.CHECK.FAILED'); } } } /** * Update links status by their reciprocal status * * @param kEvent $event */ function OnProcessReciprocalLinks($event) { if ( !$this->Application->ConfigValue('ReciprocalLinkChecking') ) { return; } /** @var kCatDBItem $object */ $object = $event->getObject( Array('skip_autoload' => true) ); /** @var LinkHelper $link_helper */ $link_helper = $this->Application->recallObject('LinkHelper'); $id_field = $this->Application->getUnitOption($event->Prefix, 'IDField'); $table_name = $this->Application->getUnitOption($event->Prefix, 'TableName'); // 1. verify all links, that were not verified previously $sql = 'SELECT ' . $id_field . ' FROM ' . $table_name . ' WHERE (ReciprocalLinkFound = 0)'; $not_checked_links = $this->Conn->GetCol($sql); foreach ($not_checked_links as $link_id) { $object->Load($link_id); $link_checked = $link_helper->CheckReciprocalURL($object->GetDBField('Url')); $object->SetDBField('ReciprocalLinkFound', $link_checked ? LINK_IS_RECIPROCAL : LINK_IS_NOT_RECIPROCAL); $object->Update(); if ( $link_checked ) { $object->ApproveChanges(); } else { $object->DeclineChanges(); $this->Application->emailAdmin('LINK.RECIPROCAL.CHECK.FAILED'); } } // 2. approve all links, that have succeeded in reciprocal check (during adding/changing on front-end) $sql = 'SELECT ' . $id_field . ' FROM ' . $table_name . ' WHERE (ReciprocalLinkFound = ' . LINK_IS_RECIPROCAL . ') AND (Status <> ' . STATUS_ACTIVE . ')'; $verified_links = $this->Conn->GetCol($sql); foreach ($verified_links as $link_id) { $object->Load($link_id); $object->ApproveChanges(); } // 3. decline all links, that failed in reciprocal check (during adding/changing on front-end) $sql = 'SELECT ' . $id_field . ' FROM ' . $table_name . ' WHERE (ReciprocalLinkFound = ' . LINK_IS_NOT_RECIPROCAL . ') AND (Status <> ' . STATUS_DISABLED . ')'; $not_verified_links = $this->Conn->GetCol($sql); foreach ($not_verified_links as $link_id) { $object->Load($link_id); $object->DeclineChanges(); } } /** * Allows to load duplicate link by special id * * @param kEvent $event * @return int * @access public */ public function getPassedID(kEvent $event) { $id = parent::getPassedID($event); if ( ($event->Special == 'duplicates') && !is_numeric($id) ) { $load_keys = unserialize(base64_decode($id)); // can't return $load_keys as $id, because "kCatDBItem::GetKeyClause" will ignore them foreach ($load_keys as $field => $value) { $load_keys[$field] = $field . ' = ' . $this->Conn->qstr($value); } $sql = 'SELECT ' . $this->Application->getUnitOption($event->Prefix, 'IDField') . ' FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . ' WHERE (' . implode(') AND (', $load_keys) . ')'; $id = $this->Conn->GetOne($sql); } return $id; } /** * Returns events, that require item-based (not just event-name based) permission check * * @return Array */ function _getMassPermissionEvents() { $events = parent::_getMassPermissionEvents(); $events[] = 'OnMerge'; return $events; } /** * [HOOK] Allows to add cloned subitem to given prefix * * @param kEvent $event * @return void * @access protected */ protected function OnCloneSubItem(kEvent $event) { parent::OnCloneSubItem($event); if ( $event->MasterEvent->Prefix == 'rev' ) { $clones = $this->Application->getUnitOption($event->MasterEvent->Prefix, 'Clones'); $subitem_prefix = $event->Prefix . '-' . $event->MasterEvent->Prefix; $clones[$subitem_prefix]['ConfigMapping'] = Array ( 'PerPage' => 'Perpage_LinkReviews', 'ShortListPerPage' => 'Perpage_LinkReviews_Short', 'DefaultSorting1Field' => 'Link_ReviewsSort', 'DefaultSorting2Field' => 'Link_ReviewsSort2', 'DefaultSorting1Dir' => 'Link_ReviewsOrder', 'DefaultSorting2Dir' => 'Link_ReviewsOrder2', 'ReviewDelayInterval' => 'link_ReviewDelay_Interval', 'ReviewDelayValue' => 'link_ReviewDelay_Value', ); $this->Application->setUnitOption($event->MasterEvent->Prefix, 'Clones', $clones); } } /** * Occurs before original item of item in pending editing got deleted (for hooking only) * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeDeleteOriginal(kEvent $event) { parent::OnBeforeDeleteOriginal($event); /** @var kDBItem $object */ $object = $event->getObject(); $link_id = $event->getEventParam('original_id'); $new_resource_id = $object->GetDBField('ResourceId'); $sql = 'SELECT ResourceId FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . ' WHERE ' . $this->Application->getUnitOption($event->Prefix, 'IDField') . '=' . $link_id; $old_resource_id = $this->Conn->GetOne($sql); $this->Application->SetVar('original_resource_id', $old_resource_id); $this->changeResourceId('rel', 'TargetId', $old_resource_id, $new_resource_id); } /** * Occurs after original item of item in pending editing got deleted * * @param kEvent $event * @return void * @access protected */ protected function OnAfterDeleteOriginal(kEvent $event) { parent::OnAfterDeleteOriginal($event); /** @var kDBItem $object */ $object = $event->getObject(); $old_resource_id = $this->Application->GetVar('original_resource_id'); $new_resource_id = $object->GetDBField('ResourceId'); $this->changeResourceId('ls', 'ItemResourceId', $old_resource_id, $new_resource_id); } /** * Changes item resource id in one field * * @param string $prefix * @param string $field * @param string $old_resource_id * @param string $new_resource_id * @return void * @access protected */ protected function changeResourceId($prefix, $field, $old_resource_id, $new_resource_id) { $fields_hash = Array ($field => $new_resource_id); $table_name = $this->Application->getUnitOption($prefix, 'TableName'); $this->Conn->doUpdate($fields_hash, $table_name, $field . ' = ' . $old_resource_id); } }