Factory->includeClassFile('kDBEventHandler'); class kCatDBEventHandler extends kDBEventHandler { /** * Allows to override standart permission mapping * */ function mapPermissions() { parent::mapPermissions(); $permissions = Array( 'OnExport' => Array('self' => 'view|advanced:export'), 'OnExportBegin' => Array('self' => 'view|advanced:export'), 'OnSaveSettings' => Array('self' => 'add|edit|advanced:import'), 'OnBeforeDeleteOriginal' => Array('self' => 'edit|advanced:approve'), 'OnCancelAction' => Array('self' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Load item if id is available * * @param kEvent $event */ function LoadItem(&$event) { $object =& $event->getObject(); $id = $this->getPassedID($event); if ($object->Load($id)) { $actions =& $this->Application->recallObject('kActions'); $actions->Set($event->Prefix_Special.'_id', $object->GetID() ); $use_pending_editing = $this->Application->getUnitOption($event->Prefix, 'UsePendingEditing'); if ($use_pending_editing && $event->Special != 'original') { $this->Application->SetVar($event->Prefix.'.original_id', $object->GetDBField('OrgId')); } } else { $object->setID($id); } } /** * Checks permissions of user * * @param kEvent $event */ function CheckPermission(&$event) { if (!$this->Application->IsAdmin()) { if ($event->Name == 'OnSetSortingDirect') { // allow sorting on front event without view permission return true; } } if ($event->Name == 'OnExport') { // save category_id before doing export $this->Application->LinkVar('m_cat_id'); } if ($event->Name == 'OnNew' && preg_match('/(.*)\/import$/', $this->Application->GetVar('t'), $rets)) { // redirect to item import template, where permission (import) category will be chosen) $root_category = $this->Application->findModule('Path', $rets[1].'/', 'RootCat'); $this->Application->StoreVar('m_cat_id', $root_category); } $check_events = Array ('OnEdit', 'OnSave', 'OnMassDelete'); if (in_array($event->Name, $check_events)) { // check each id from selected individually and only if all are allowed proceed next if ($event->Name == 'OnSave') { $selected_ids = implode(',', $this->getSelectedIDs($event, true)); if (!$selected_ids) { $selected_ids = 0; // when saving newly created item (OnPreCreate -> OnPreSave -> OnSave) } } else { $selected_ids = implode(',', $this->StoreSelectedIDs($event)); } $perm_value = true; if (strlen($selected_ids)) { $perm_helper =& $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $items = $perm_helper->GetCategoryItemData($event->Prefix, $selected_ids); $check_method = ($event->Name == 'OnMassDelete') ? 'DeleteCheckPermission' : 'ModifyCheckPermission'; foreach ($items as $item_id => $item_data) { if ($perm_helper->$check_method($item_data['CreatedById'], $item_data['CategoryId'], $event->Prefix) == 0) { // one of items selected has no permission $perm_value = false; break; } } if (!$perm_value) { $event->status = erPERM_FAIL; } } else { trigger_error('IDs not passed to '.$event->getPrefixSpecial().':CheckPermission', E_USER_WARNING); } return $perm_value; } return parent::CheckPermission($event); } /** * Add selected items to clipboard with mode = COPY (CLONE) * * @param kEvent $event */ function OnCopy(&$event) { $this->Application->RemoveVar('clipboard'); $clipboard_helper =& $this->Application->recallObject('ClipboardHelper'); $clipboard_helper->setClipboard($event, 'copy', $this->StoreSelectedIDs($event)); } /** * Add selected items to clipboard with mode = CUT * * @param kEvent $event */ function OnCut(&$event) { $this->Application->RemoveVar('clipboard'); $clipboard_helper =& $this->Application->recallObject('ClipboardHelper'); $clipboard_helper->setClipboard($event, 'cut', $this->StoreSelectedIDs($event)); } /** * Performs category item paste * * @param kEvent $event */ function OnPaste(&$event) { if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { return; } $clipboard_data = $event->getEventParam('clipboard_data'); if (!$clipboard_data['cut'] && !$clipboard_data['copy']) { return false; } if ($clipboard_data['copy']) { $temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler'); $this->Application->SetVar('ResetCatBeforeClone', 1); $temp->CloneItems($event->Prefix, $event->Special, $clipboard_data['copy']); } if ($clipboard_data['cut']) { $object =& $this->Application->recallObject($event->getPrefixSpecial().'.item', $event->Prefix, Array('skip_autoload' => true)); foreach ($clipboard_data['cut'] as $id) { $object->Load($id); $object->MoveToCat(); } } } /** * Return type clauses for list bulding on front * * @param kEvent $event * @return Array */ function getTypeClauses(&$event) { $types = $event->getEventParam('types'); $types = $types ? explode(',', $types) : Array (); $except_types = $event->getEventParam('except'); $except_types = $except_types ? explode(',', $except_types) : Array (); $type_clauses = Array(); $user_id = $this->Application->RecallVar('user_id'); $type_clauses['my_items']['include'] = '%1$s.CreatedById = '.$user_id; $type_clauses['my_items']['except'] = '%1$s.CreatedById <> '.$user_id; $type_clauses['my_items']['having_filter'] = false; $type_clauses['pick']['include'] = '%1$s.EditorsPick = 1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1'; $type_clauses['pick']['except'] = '%1$s.EditorsPick! = 1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1'; $type_clauses['pick']['having_filter'] = false; $type_clauses['hot']['include'] = '`IsHot` = 1 AND PrimaryCat = 1'; $type_clauses['hot']['except'] = '`IsHot`! = 1 AND PrimaryCat = 1'; $type_clauses['hot']['having_filter'] = true; $type_clauses['pop']['include'] = '`IsPop` = 1 AND PrimaryCat = 1'; $type_clauses['pop']['except'] = '`IsPop`! = 1 AND PrimaryCat = 1'; $type_clauses['pop']['having_filter'] = true; $type_clauses['new']['include'] = '`IsNew` = 1 AND PrimaryCat = 1'; $type_clauses['new']['except'] = '`IsNew`! = 1 AND PrimaryCat = 1'; $type_clauses['new']['having_filter'] = true; $type_clauses['displayed']['include'] = ''; $displayed = $this->Application->GetVar($event->Prefix.'_displayed_ids'); if ($displayed) { $id_field = $this->Application->getUnitOption($event->Prefix, 'IDField'); $type_clauses['displayed']['except'] = '%1$s.'.$id_field.' NOT IN ('.$displayed.')'; } else { $type_clauses['displayed']['except'] = ''; } $type_clauses['displayed']['having_filter'] = false; if (in_array('search', $types) || in_array('search', $except_types)) { $event_mapping = Array( 'simple' => 'OnSimpleSearch', 'subsearch' => 'OnSubSearch', 'advanced' => 'OnAdvancedSearch'); if($this->Application->GetVar('INPORTAL_ON') && $this->Application->GetVar('Action') == 'm_simple_subsearch') { $type = 'subsearch'; } else { $type = $this->Application->GetVar('search_type') ? $this->Application->GetVar('search_type') : 'simple'; } if($keywords = $event->getEventParam('keyword_string')) // processing keyword_string param of ListProducts tag { $this->Application->SetVar('keywords', $keywords); $type = 'simple'; } $search_event = $event_mapping[$type]; $this->$search_event($event); $search_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search'; $sql = 'SHOW TABLES LIKE "'.$search_table.'"'; if ( $this->Conn->Query($sql) ) { $search_res_ids = $this->Conn->GetCol('SELECT ResourceId FROM '.$search_table); } if ($search_res_ids) { $type_clauses['search']['include'] = '%1$s.ResourceId IN ('.implode(',', $search_res_ids).') AND PrimaryCat = 1'; $type_clauses['search']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $search_res_ids).') AND PrimaryCat = 1'; } else { $type_clauses['search']['include'] = '0'; $type_clauses['search']['except'] = '1'; } $type_clauses['search']['having_filter'] = false; } if (in_array('related', $types) || in_array('related', $except_types)) { $related_to = $event->getEventParam('related_to'); if (!$related_to) { $related_prefix = $event->Prefix; } else { $sql = 'SELECT Prefix FROM '.TABLE_PREFIX.'ItemTypes WHERE ItemName = '.$this->Conn->qstr($related_to); $related_prefix = $this->Conn->GetOne($sql); } $rel_table = $this->Application->getUnitOption('rel', 'TableName'); $item_type = $this->Application->getUnitOption($event->Prefix, 'ItemType'); $p_item =& $this->Application->recallObject($related_prefix.'.current', null, Array('skip_autoload' => true)); $p_item->Load( $this->Application->GetVar($related_prefix.'_id') ); $p_resource_id = $p_item->GetDBField('ResourceId'); $sql = 'SELECT SourceId, TargetId FROM '.$rel_table.' WHERE (Enabled = 1) AND ( (Type = 0 AND SourceId = '.$p_resource_id.' AND TargetType = '.$item_type.') OR (Type = 1 AND ( (SourceId = '.$p_resource_id.' AND TargetType = '.$item_type.') OR (TargetId = '.$p_resource_id.' AND SourceType = '.$item_type.') ) ) )'; $related_ids_array = $this->Conn->Query($sql); $related_ids = Array(); foreach ($related_ids_array as $key => $record) { $related_ids[] = $record[ $record['SourceId'] == $p_resource_id ? 'TargetId' : 'SourceId' ]; } if (count($related_ids) > 0) { $type_clauses['related']['include'] = '%1$s.ResourceId IN ('.implode(',', $related_ids).') AND PrimaryCat = 1'; $type_clauses['related']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $related_ids).') AND PrimaryCat = 1'; } else { $type_clauses['related']['include'] = '0'; $type_clauses['related']['except'] = '1'; } $type_clauses['related']['having_filter'] = false; } if (in_array('favorites', $types) || in_array('favorites', $except_types)) { $sql = 'SELECT ResourceId FROM '.$this->Application->getUnitOption('fav', 'TableName').' WHERE PortalUserId = '.$this->Application->RecallVar('user_id'); $favorite_ids = $this->Conn->GetCol($sql); if ($favorite_ids) { $type_clauses['favorites']['include'] = '%1$s.ResourceId IN ('.implode(',', $favorite_ids).') AND PrimaryCat = 1'; $type_clauses['favorites']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $favorite_ids).') AND PrimaryCat = 1'; } else { $type_clauses['favorites']['include'] = 0; $type_clauses['favorites']['except'] = 1; } $type_clauses['favorites']['having_filter'] = false; } return $type_clauses; } /** * Apply filters to list * * @param kEvent $event */ function SetCustomQuery(&$event) { parent::SetCustomQuery($event); $object =& $event->getObject(); // add category filter if needed if ($event->Special != 'showall') { if ($event->getEventParam('parent_cat_id') !== false) { $parent_cat_id = $event->getEventParam('parent_cat_id'); } else { $parent_cat_id = $this->Application->GetVar('c_id'); if (!$parent_cat_id) { $parent_cat_id = $this->Application->GetVar('m_cat_id'); } if (!$parent_cat_id) { $parent_cat_id = 0; } } if ((string) $parent_cat_id != 'any') { if ($event->getEventParam('recursive')) { $current_path = $this->Conn->GetOne('SELECT ParentPath FROM '.TABLE_PREFIX.'Category WHERE CategoryId='.$parent_cat_id); $subcats = $this->Conn->GetCol('SELECT CategoryId FROM '.TABLE_PREFIX.'Category WHERE ParentPath LIKE "'.$current_path.'%" '); $object->addFilter('category_filter', TABLE_PREFIX.'CategoryItems.CategoryId IN ('.implode(', ', $subcats).')'); $object->addFilter('primary_filter', 'PrimaryCat = 1'); } else { $object->addFilter('category_filter', TABLE_PREFIX.'CategoryItems.CategoryId = '.$parent_cat_id ); } } } else { $object->addFilter('primary_filter', 'PrimaryCat = 1'); } // add permission filter if ($this->Application->RecallVar('user_id') == -1) { // for "root" CATEGORY.VIEW permission is checked for items lists too $view_perm = 1; } else { // for any real user itemlist view permission is checked instead of CATEGORY.VIEW $sql = 'SELECT PermissionConfigId FROM '.TABLE_PREFIX.'PermissionConfig WHERE PermissionName = "'.$this->Application->getUnitOption($event->Prefix, 'PermItemPrefix').'.VIEW"'; $view_perm = $this->Conn->GetOne($sql); $groups = explode( ',', $this->Application->RecallVar('UserGroups') ); foreach($groups as $group) { $view_filters[] = 'FIND_IN_SET('.$group.', perm.acl)'; } $view_filter = implode(' OR ', $view_filters); $object->addFilter('perm_filter2', $view_filter); } $object->addFilter('perm_filter', 'perm.PermId = '.$view_perm); $types = $event->getEventParam('types'); $this->applyItemStatusFilter($object, $types); $except_types = $event->getEventParam('except'); $type_clauses = $this->getTypeClauses($event); // convert prepared type clauses into list filters $includes_or_filter =& $this->Application->makeClass('kMultipleFilter'); $includes_or_filter->setType(FLT_TYPE_OR); $excepts_and_filter =& $this->Application->makeClass('kMultipleFilter'); $excepts_and_filter->setType(FLT_TYPE_AND); $includes_or_filter_h =& $this->Application->makeClass('kMultipleFilter'); $includes_or_filter_h->setType(FLT_TYPE_OR); $excepts_and_filter_h =& $this->Application->makeClass('kMultipleFilter'); $excepts_and_filter_h->setType(FLT_TYPE_AND); if ($types) { $types_array = explode(',', $types); for ($i = 0; $i < sizeof($types_array); $i++) { $type = trim($types_array[$i]); if (isset($type_clauses[$type])) { if ($type_clauses[$type]['having_filter']) { $includes_or_filter_h->removeFilter('filter_'.$type); $includes_or_filter_h->addFilter('filter_'.$type, $type_clauses[$type]['include']); }else { $includes_or_filter->removeFilter('filter_'.$type); $includes_or_filter->addFilter('filter_'.$type, $type_clauses[$type]['include']); } } } } if ($except_types) { $except_types_array = explode(',', $except_types); for ($i = 0; $i < sizeof($except_types_array); $i++) { $type = trim($except_types_array[$i]); if (isset($type_clauses[$type])) { if ($type_clauses[$type]['having_filter']) { $excepts_and_filter_h->removeFilter('filter_'.$type); $excepts_and_filter_h->addFilter('filter_'.$type, $type_clauses[$type]['except']); }else { $excepts_and_filter->removeFilter('filter_'.$type); $excepts_and_filter->addFilter('filter_'.$type, $type_clauses[$type]['except']); } } } } /*if ( !$this->Application->IsAdmin() ) { $object->addFilter('expire_filter', '%1$s.Expire IS NULL OR %1$s.Expire > UNIX_TIMESTAMP()'); }*/ /*$list_type = $event->getEventParam('ListType'); switch($list_type) { case 'favorites': $fav_table = $this->Application->getUnitOption('fav','TableName'); $user_id =& $this->Application->RecallVar('user_id'); $sql = 'SELECT DISTINCT f.ResourceId FROM '.$fav_table.' f LEFT JOIN '.$object->TableName.' p ON p.ResourceId = f.ResourceId WHERE f.PortalUserId = '.$user_id; $ids = $this->Conn->GetCol($sql); if(!$ids) $ids = Array(-1); $object->addFilter('category_filter', TABLE_PREFIX.'CategoryItems.PrimaryCat = 1'); $object->addFilter('favorites_filter', '%1$s.`ResourceId` IN ('.implode(',',$ids).')'); break; case 'search': $search_results_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search'; $sql = ' SELECT DISTINCT ResourceId FROM '.$search_results_table.' WHERE ItemType=11'; $ids = $this->Conn->GetCol($sql); if(!$ids) $ids = Array(-1); $object->addFilter('search_filter', '%1$s.`ResourceId` IN ('.implode(',',$ids).')'); break; } */ $object->addFilter('includes_filter', $includes_or_filter); $object->addFilter('excepts_filter', $excepts_and_filter); $object->addFilter('includes_filter_h', $includes_or_filter_h, HAVING_FILTER); $object->addFilter('excepts_filter_h', $excepts_and_filter_h, HAVING_FILTER); } /** * Adds filter that filters out items with non-required statuses * * @param kDBList $object * @param string $types */ function applyItemStatusFilter(&$object, $types) { // Link1 (before modifications) [Status = 1, OrgId = NULL], Link2 (after modifications) [Status = -2, OrgId = Link1_ID] $pending_editing = $this->Application->getUnitOption($object->Prefix, 'UsePendingEditing'); if ( !$this->Application->IsAdmin() ) { $types = explode(',', $types); if (in_array('my_items', $types)) { $allow_statuses = Array (STATUS_ACTIVE, STATUS_PENDING, STATUS_PENDING_EDITING); $object->addFilter('status_filter', '%1$s.Status IN ('.implode(',', $allow_statuses).')'); if ($pending_editing) { $user_id = $this->Application->RecallVar('user_id'); $this->applyPendingEditingFilter($object, $user_id); } } else { $object->addFilter('status_filter', '%1$s.Status = 1'); if ($pending_editing) { // if category item uses pending editing abilities, then in no cases show pending copies on front $object->addFilter('original_filter', '%1$s.OrgId = 0 OR %1$s.OrgId IS NULL'); } } } else { if ($pending_editing) { $this->applyPendingEditingFilter($object); } } } /** * Adds filter, that removes live items if they have pending editing copies * * @param kDBList $object * @param int $user_id */ function applyPendingEditingFilter(&$object, $user_id = null) { $sql = 'SELECT OrgId FROM '.$object->TableName.' WHERE Status = '.STATUS_PENDING_EDITING.' AND OrgId IS NOT NULL'; if (isset($user_id)) { $sql .= ' AND CreatedById = '.$user_id; } $pending_ids = $this->Conn->GetCol($sql); if ($pending_ids) { $object->addFilter('no_original_filter', '%1$s.'.$object->IDField.' NOT IN ('.implode(',', $pending_ids).')'); } } /** * Adds calculates fields for item statuses * * @param kCatDBItem $object * @param kEvent $event */ function prepareObject(&$object, &$event) { $this->prepareItemStatuses($event); $object->addCalculatedField('CachedNavbar', 'l'.$this->Application->GetVar('m_lang').'_CachedNavbar'); if ($event->Special == 'export' || $event->Special == 'import') { $export_helper =& $this->Application->recallObject('CatItemExportHelper'); $export_helper->prepareExportColumns($event); } } /** * Creates calculated fields for all item statuses based on config settings * * @param kEvent $event */ function prepareItemStatuses(&$event) { $object =& $event->getObject( Array('skip_autoload' => true) ); $property_map = $this->Application->getUnitOption($event->Prefix, 'ItemPropertyMappings'); if (!$property_map) { return ; } // new items $object->addCalculatedField('IsNew', ' IF(%1$s.NewItem = 2, IF(%1$s.CreatedOn >= (UNIX_TIMESTAMP() - '. $this->Application->ConfigValue($property_map['NewDays']). '*3600*24), 1, 0), %1$s.NewItem )'); // hot items (cache updated every hour) $sql = 'SELECT Data FROM '.TABLE_PREFIX.'Cache WHERE (VarName = "'.$property_map['HotLimit'].'") AND (Cached >'.(adodb_mktime() - 3600).')'; $hot_limit = $this->Conn->GetOne($sql); if ($hot_limit === false) { $hot_limit = $this->CalculateHotLimit($event); } $object->addCalculatedField('IsHot', ' IF(%1$s.HotItem = 2, IF(%1$s.'.$property_map['ClickField'].' >= '.$hot_limit.', 1, 0), %1$s.HotItem )'); // popular items $object->addCalculatedField('IsPop', ' IF(%1$s.PopItem = 2, IF(%1$s.CachedVotesQty >= '. $this->Application->ConfigValue($property_map['MinPopVotes']). ' AND %1$s.CachedRating >= '. $this->Application->ConfigValue($property_map['MinPopRating']). ', 1, 0), %1$s.PopItem)'); } function CalculateHotLimit(&$event) { $property_map = $this->Application->getUnitOption($event->Prefix, 'ItemPropertyMappings'); if (!$property_map) { return; } $click_field = $property_map['ClickField']; $last_hot = $this->Application->ConfigValue($property_map['MaxHotNumber']) - 1; $sql = 'SELECT '.$click_field.' FROM '.$this->Application->getUnitOption($event->Prefix, 'TableName').' ORDER BY '.$click_field.' DESC LIMIT '.$last_hot.', 1'; $res = $this->Conn->GetCol($sql); $hot_limit = (double)array_shift($res); $this->Conn->Query('REPLACE INTO '.TABLE_PREFIX.'Cache (VarName, Data, Cached) VALUES ("'.$property_map['HotLimit'].'", "'.$hot_limit.'", '.adodb_mktime().')'); return $hot_limit; return 0; } /** * Enter description here... * * @param kEvent $event */ function OnBeforeItemUpdate(&$event) { $property_map = $this->Application->getUnitOption($event->Prefix, 'ItemPropertyMappings'); if (!$property_map) { return; } $click_field = $property_map['ClickField']; $object =& $event->getObject(); if( $this->Application->IsAdmin() && ($this->Application->GetVar($click_field.'_original') !== false) && floor($this->Application->GetVar($click_field.'_original')) != $object->GetDBField($click_field) ) { $sql = 'SELECT MAX('.$click_field.') FROM '.$this->Application->getUnitOption($event->Prefix, 'TableName').' WHERE FLOOR('.$click_field.') = '.$object->GetDBField($click_field); $hits = ( $res = $this->Conn->GetOne($sql) ) ? $res + 0.000001 : $object->GetDBField($click_field); $object->SetDBField($click_field, $hits); } } /** * Load price from temp table if product mode is temp table * * @param kEvent $event */ function OnAfterItemLoad(&$event) { $special = substr($event->Special, -6); $object =& $event->getObject(); if ($special == 'import' || $special == 'export') { $image_data = $object->getPrimaryImageData(); if ($image_data) { $thumbnail_image = $image_data[$image_data['LocalThumb'] ? 'ThumbPath' : 'ThumbUrl']; if ($image_data['SameImages']) { $full_image = ''; } else { $full_image = $image_data[$image_data['LocalImage'] ? 'LocalPath' : 'Url']; } $object->SetDBField('ThumbnailImage', $thumbnail_image); $object->SetDBField('FullImage', $full_image); $object->SetDBField('ImageAlt', $image_data['AltName']); } } //substituiting pending status value for pending editing if ($object->HasField('OrgId') && $object->GetDBField('OrgId') > 0 && $object->GetDBField('Status') == -2) { $options = $object->Fields['Status']['options']; foreach ($options as $key => $val) { if ($key == 2) $key = -2; $new_options[$key] = $val; } $object->Fields['Status']['options'] = $new_options; } /*elseif (!$this->Application->IsAdmin() && $object->GetDBField('Status') != 1 && $object->Prefix != 'cms') { header('HTTP/1.0 404 Not Found'); while (ob_get_level()) { ob_end_clean(); } $this->Application->HTML = $this->Application->ParseBlock(array('name'=>$this->Application->ConfigValue('ErrorTemplate'))); $this->Application->Done(); exit(); }*/ } function OnAfterItemUpdate(&$event) { $this->CalculateHotLimit($event); if ( substr($event->Special, -6) == 'import') { $this->setCustomExportColumns($event); } } /** * sets values for import process * * @param kEvent $event */ function OnAfterItemCreate(&$event) { if ( substr($event->Special, -6) == 'import') { $this->setCustomExportColumns($event); } } /** * Make record to search log * * @param string $keywords * @param int $search_type 0 - simple search, 1 - advanced search */ function saveToSearchLog($keywords, $search_type = 0) { $sql = 'UPDATE '.TABLE_PREFIX.'SearchLog SET Indices = Indices + 1 WHERE Keyword = '.$this->Conn->qstr($keywords).' AND SearchType = '.$search_type; // 0 - simple search, 1 - advanced search $this->Conn->Query($sql); if ($this->Conn->getAffectedRows() == 0) { $fields_hash = Array('Keyword' => $keywords, 'Indices' => 1, 'SearchType' => $search_type); $this->Conn->doInsert($fields_hash, TABLE_PREFIX.'SearchLog'); } } /** * Makes simple search for products * based on keywords string * * @param kEvent $event * @todo Change all hardcoded Products table & In-Commerce module usage to dynamic usage from item config !!! */ function OnSimpleSearch(&$event) { if($this->Application->GetVar('INPORTAL_ON') && !($this->Application->GetVar('Action') == 'm_simple_search')) { return; } $event->redirect = false; $search_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search'; $keywords = unhtmlentities( trim($this->Application->GetVar('keywords')) ); $query_object =& $this->Application->recallObject('HTTPQuery'); $sql = 'SHOW TABLES LIKE "'.$search_table.'"'; if(!isset($query_object->Get['keywords']) && !isset($query_object->Post['keywords']) && $this->Conn->Query($sql)) { return; // used when navigating by pages or changing sorting in search results } if(!$keywords || strlen($keywords) < $this->Application->ConfigValue('Search_MinKeyword_Length')) { $this->Conn->Query('DROP TABLE IF EXISTS '.$search_table); $this->Application->SetVar('keywords_too_short', 1); return; // if no or too short keyword entered, doing nothing } $this->Application->StoreVar('keywords', $keywords); if (!$this->Application->GetVar('INPORTAL_ON')) { // don't save search log, because in-portal already saved it $this->saveToSearchLog($keywords, 0); // 0 - simple search, 1 - advanced search } $keywords = strtr($keywords, Array('%' => '\\%', '_' => '\\_')); $event->setPseudoClass('_List'); $object =& $event->getObject(); $this->Application->SetVar($event->getPrefixSpecial().'_Page', 1); $lang = $this->Application->GetVar('m_lang'); $items_table = $this->Application->getUnitOption($event->Prefix, 'TableName'); $module_name = $this->Application->findModule('Var', $event->Prefix, 'Name'); $sql = ' SELECT * FROM '.$this->Application->getUnitOption('confs', 'TableName').' WHERE ModuleName="'.$module_name.'" AND SimpleSearch=1'; $search_config = $this->Conn->Query($sql, 'FieldName'); $field_list = array_keys($search_config); $join_clauses = Array(); // field processing $weight_sum = 0; $alias_counter = 0; $custom_fields = $this->Application->getUnitOption($event->Prefix, 'CustomFields'); if ($custom_fields) { $custom_table = $this->Application->getUnitOption($event->Prefix.'-cdata', 'TableName'); $join_clauses[] = ' LEFT JOIN '.$custom_table.' custom_data ON '.$items_table.'.ResourceId = custom_data.ResourceId'; } // what field in search config becomes what field in sql (key - new field, value - old field (from searchconfig table)) $search_config_map = Array(); foreach ($field_list as $key => $field) { $options = $object->getFieldOptions($field); $local_table = TABLE_PREFIX.$search_config[$field]['TableName']; $weight_sum += $search_config[$field]['Priority']; // counting weight sum; used when making relevance clause // processing multilingual fields if (getArrayValue($options, 'formatter') == 'kMultiLanguage') { $field_list[$key.'_primary'] = 'l'.$this->Application->GetDefaultLanguageId().'_'.$field; $field_list[$key] = 'l'.$lang.'_'.$field; if(!isset($search_config[$field]['ForeignField'])) { $field_list[$key.'_primary'] = $local_table.'.'.$field_list[$key.'_primary']; $search_config_map[ $field_list[$key.'_primary'] ] = $field; } } // processing fields from other tables if($foreign_field = $search_config[$field]['ForeignField']) { $exploded = explode(':', $foreign_field, 2); if($exploded[0] == 'CALC') { unset($field_list[$key]); continue; // ignoring having type clauses in simple search /*$user_groups = $this->Application->RecallVar('UserGroups'); $having_list[$key] = str_replace('{PREFIX}', TABLE_PREFIX, $exploded[1]); $join_clause = str_replace('{PREFIX}', TABLE_PREFIX, $search_config[$field]['JoinClause']); $join_clause = str_replace('{USER_GROUPS}', $user_groups, $join_clause); $join_clause = ' LEFT JOIN '.$join_clause; $join_clauses[] = $join_clause;*/ } else { $multi_lingual = false; if ($exploded[0] == 'MULTI') { $multi_lingual = true; $foreign_field = $exploded[1]; } $exploded = explode('.', $foreign_field); // format: table.field_name $foreign_table = TABLE_PREFIX.$exploded[0]; $alias_counter++; $alias = 't'.$alias_counter; if ($multi_lingual) { $field_list[$key] = $alias.'.'.'l'.$lang.'_'.$exploded[1]; $field_list[$key.'_primary'] = 'l'.$this->Application->GetDefaultLanguageId().'_'.$field; $search_config_map[ $field_list[$key] ] = $field; $search_config_map[ $field_list[$key.'_primary'] ] = $field; } else { $field_list[$key] = $alias.'.'.$exploded[1]; $search_config_map[ $field_list[$key] ] = $field; } $join_clause = str_replace('{ForeignTable}', $alias, $search_config[$field]['JoinClause']); $join_clause = str_replace('{LocalTable}', $items_table, $join_clause); $join_clauses[] = ' LEFT JOIN '.$foreign_table.' '.$alias.' ON '.$join_clause; } } else { // processing fields from local table if ($search_config[$field]['CustomFieldId']) { $local_table = 'custom_data'; $field_list[$key] = 'l'.$lang.'_cust_'.array_search($field_list[$key], $custom_fields); } $field_list[$key] = $local_table.'.'.$field_list[$key]; $search_config_map[ $field_list[$key] ] = $field; } } // keyword string processing $search_helper =& $this->Application->recallObject('SearchHelper'); $where_clause = $search_helper->buildWhereClause($keywords, $field_list); $where_clause = $where_clause.' AND '.$items_table.'.Status=1'; if($this->Application->GetVar('Action') == 'm_simple_subsearch') // subsearch, In-portal { if( $event->getEventParam('ResultIds') ) { $where_clause .= ' AND '.$items_table.'.ResourceId IN ('.implode(',', $event->specificParams['ResultIds']).')'; } } if( $event->MasterEvent && $event->MasterEvent->Name == 'OnListBuild' ) // subsearch, k4 { if( $event->MasterEvent->getEventParam('ResultIds') ) { $where_clause .= ' AND '.$items_table.'.ResourceId IN ('.implode(',', $event->MasterEvent->getEventParam('ResultIds')).')'; } } // making relevance clause $positive_words = $search_helper->getPositiveKeywords($keywords); $this->Application->StoreVar('highlight_keywords', serialize($positive_words)); $revelance_parts = Array(); reset($search_config); foreach ($field_list as $field) { $config_elem = $search_config[ $search_config_map[$field] ]; $weight = $config_elem['Priority']; $revelance_parts[] = 'IF('.$field.' LIKE "%'.implode(' ', $positive_words).'%", '.$weight_sum.', 0)'; foreach ($positive_words as $keyword) { $revelance_parts[] = 'IF('.$field.' LIKE "%'.$keyword.'%", '.$weight.', 0)'; } } $conf_postfix = $this->Application->getUnitOption($event->Prefix, 'SearchConfigPostfix'); $rel_keywords = $this->Application->ConfigValue('SearchRel_Keyword_'.$conf_postfix) / 100; $rel_pop = $this->Application->ConfigValue('SearchRel_Pop_'.$conf_postfix) / 100; $rel_rating = $this->Application->ConfigValue('SearchRel_Rating_'.$conf_postfix) / 100; $relevance_clause = '('.implode(' + ', $revelance_parts).') / '.$weight_sum.' * '.$rel_keywords; if ($rel_pop && isset($object->Fields['Hits'])) { $relevance_clause .= ' + (Hits + 1) / (MAX(Hits) + 1) * '.$rel_pop; } if ($rel_rating && isset($object->Fields['CachedRating'])) { $relevance_clause .= ' + (CachedRating + 1) / (MAX(CachedRating) + 1) * '.$rel_rating; } // building final search query if (!$this->Application->GetVar('do_not_drop_search_table') && !$this->Application->GetVar('INPORTAL_ON')) { $this->Conn->Query('DROP TABLE IF EXISTS '.$search_table); // erase old search table if clean k4 event $this->Application->SetVar('do_not_drop_search_table', true); } $search_table_exists = $this->Conn->Query('SHOW TABLES LIKE "'.$search_table.'"'); if ($search_table_exists) { $select_intro = 'INSERT INTO '.$search_table.' (Relevance, ItemId, ResourceId, ItemType, EdPick) '; } else { $select_intro = 'CREATE TABLE '.$search_table.' AS '; } $edpick_clause = $this->Application->getUnitOption($event->Prefix.'.EditorsPick', 'Fields') ? $items_table.'.EditorsPick' : '0'; $sql = $select_intro.' SELECT '.$relevance_clause.' AS Relevance, '.$items_table.'.'.$this->Application->getUnitOption($event->Prefix, 'IDField').' AS ItemId, '.$items_table.'.ResourceId, '.$this->Application->getUnitOption($event->Prefix, 'ItemType').' AS ItemType, '.$edpick_clause.' AS EdPick FROM '.$object->TableName.' '.implode(' ', $join_clauses).' WHERE '.$where_clause.' GROUP BY '.$items_table.'.'.$this->Application->getUnitOption($event->Prefix, 'IDField'); $res = $this->Conn->Query($sql); } /** * Enter description here... * * @param kEvent $event */ function OnSubSearch(&$event) { $search_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search'; $sql = 'SHOW TABLES LIKE "'.$search_table.'"'; if($this->Conn->Query($sql)) { $sql = 'SELECT DISTINCT ResourceId FROM '.$search_table; $ids = $this->Conn->GetCol($sql); } $event->setEventParam('ResultIds', $ids); $event->CallSubEvent('OnSimpleSearch'); } /** * Enter description here... * * @param kEvent $event * @todo Change all hardcoded Products table & In-Commerce module usage to dynamic usage from item config !!! */ function OnAdvancedSearch(&$event) { $query_object =& $this->Application->recallObject('HTTPQuery'); if(!isset($query_object->Post['andor'])) { return; // used when navigating by pages or changing sorting in search results } $this->Application->RemoveVar('keywords'); $this->Application->RemoveVar('Search_Keywords'); $sql = ' SELECT * FROM '.$this->Application->getUnitOption('confs', 'TableName').' WHERE ModuleName="In-Commerce" AND AdvancedSearch=1'; $search_config = $this->Conn->Query($sql); $lang = $this->Application->GetVar('m_lang'); $object =& $event->getObject(); $object->SetPage(1); $product_table = $this->Application->getUnitOption('p', 'TableName'); $search_keywords = $this->Application->GetVar('value'); // will not be changed $keywords = $this->Application->GetVar('value'); // will be changed down there $verbs = $this->Application->GetVar('verb'); $glues = $this->Application->GetVar('andor'); $and_conditions = Array(); $or_conditions = Array(); $and_having_conditions = Array(); $or_having_conditions = Array(); $join_clauses = Array(); $highlight_keywords = Array(); $relevance_parts = Array(); $condition_patterns = Array( 'any' => '%s LIKE %s', 'contains' => '%s LIKE %s', 'notcontains' => '(NOT (%1$s LIKE %2$s) OR %1$s IS NULL)', 'is' => '%s = %s', 'isnot' => '(%1$s != %2$s OR %1$s IS NULL)'); $alias_counter = 0; $custom_fields = $this->Application->getUnitOption($event->Prefix, 'CustomFields'); if ($custom_fields) { $custom_table = $this->Application->getUnitOption($event->Prefix.'-cdata', 'TableName'); $join_clauses[] = ' LEFT JOIN '.$custom_table.' custom_data ON '.$product_table.'.ResourceId = custom_data.ResourceId'; } $search_log = ''; $weight_sum = 0; // processing fields and preparing conditions foreach($search_config as $record) { $field = $record['FieldName']; $join_clause = ''; $condition_mode = 'WHERE'; // field processing $options = $object->getFieldOptions($field); $local_table = TABLE_PREFIX.$record['TableName']; $weight_sum += $record['Priority']; // counting weight sum; used when making relevance clause // processing multilingual fields if (getArrayValue($options, 'formatter') == 'kMultiLanguage') { $field_name = 'l'.$lang.'_'.$field; } else { $field_name = $field; } // processing fields from other tables if ($foreign_field = $record['ForeignField']) { $exploded = explode(':', $foreign_field, 2); if($exploded[0] == 'CALC') { $user_groups = $this->Application->RecallVar('UserGroups'); $field_name = str_replace('{PREFIX}', TABLE_PREFIX, $exploded[1]); $join_clause = str_replace('{PREFIX}', TABLE_PREFIX, $record['JoinClause']); $join_clause = str_replace('{USER_GROUPS}', $user_groups, $join_clause); $join_clause = ' LEFT JOIN '.$join_clause; $condition_mode = 'HAVING'; } else { $exploded = explode('.', $foreign_field); $foreign_table = TABLE_PREFIX.$exploded[0]; if($record['CustomFieldId']) { $exploded[1] = 'l'.$lang.'_'.$exploded[1]; } $alias_counter++; $alias = 't'.$alias_counter; $field_name = $alias.'.'.$exploded[1]; $join_clause = str_replace('{ForeignTable}', $alias, $record['JoinClause']); $join_clause = str_replace('{LocalTable}', $product_table, $join_clause); if($record['CustomFieldId']) { $join_clause .= ' AND '.$alias.'.CustomFieldId='.$record['CustomFieldId']; } $join_clause = ' LEFT JOIN '.$foreign_table.' '.$alias.' ON '.$join_clause; } } else { // processing fields from local table if ($record['CustomFieldId']) { $local_table = 'custom_data'; $field_name = 'l'.$lang.'_cust_'.array_search($field_name, $custom_fields); } $field_name = $local_table.'.'.$field_name; } $condition = ''; switch($record['FieldType']) { case 'text': $keywords[$field] = unhtmlentities( $keywords[$field] ); if(strlen($keywords[$field]) >= $this->Application->ConfigValue('Search_MinKeyword_Length')) { $highlight_keywords[] = $keywords[$field]; if( in_array($verbs[$field], Array('any', 'contains', 'notcontains')) ) { $keywords[$field] = '%'.strtr($keywords[$field], Array('%' => '\\%', '_' => '\\_')).'%'; } $condition = sprintf( $condition_patterns[$verbs[$field]], $field_name, $this->Conn->qstr( $keywords[$field] )); } break; case 'boolean': if($keywords[$field] != -1) { $property_mappings = $this->Application->getUnitOption($event->Prefix, 'ItemPropertyMappings'); switch($field) { case 'HotItem': $hot_limit_var = getArrayValue($property_mappings, 'HotLimit'); if($hot_limit_var) { $sql = 'SELECT Data FROM '.TABLE_PREFIX.'Cache WHERE VarName="'.$hot_limit_var.'"'; $hot_limit = (int)$this->Conn->GetOne($sql); $condition = 'IF('.$product_table.'.HotItem = 2, IF('.$product_table.'.Hits >= '. $hot_limit. ', 1, 0), '.$product_table.'.HotItem) = '.$keywords[$field]; } break; case 'PopItem': $votes2pop_var = getArrayValue($property_mappings, 'VotesToPop'); $rating2pop_var = getArrayValue($property_mappings, 'RatingToPop'); if($votes2pop_var && $rating2pop_var) { $condition = 'IF('.$product_table.'.PopItem = 2, IF('.$product_table.'.CachedVotesQty >= '. $this->Application->ConfigValue($votes2pop_var). ' AND '.$product_table.'.CachedRating >= '. $this->Application->ConfigValue($rating2pop_var). ', 1, 0), '.$product_table.'.PopItem) = '.$keywords[$field]; } break; case 'NewItem': $new_days_var = getArrayValue($property_mappings, 'NewDays'); if($new_days_var) { $condition = 'IF('.$product_table.'.NewItem = 2, IF('.$product_table.'.CreatedOn >= (UNIX_TIMESTAMP() - '. $this->Application->ConfigValue($new_days_var). '*3600*24), 1, 0), '.$product_table.'.NewItem) = '.$keywords[$field]; } break; case 'EditorsPick': $condition = $product_table.'.EditorsPick = '.$keywords[$field]; break; } } break; case 'range': $range_conditions = Array(); if($keywords[$field.'_from'] && !preg_match("/[^0-9]/i", $keywords[$field.'_from'])) { $range_conditions[] = $field_name.' >= '.$keywords[$field.'_from']; } if($keywords[$field.'_to'] && !preg_match("/[^0-9]/i", $keywords[$field.'_to'])) { $range_conditions[] = $field_name.' <= '.$keywords[$field.'_to']; } if($range_conditions) { $condition = implode(' AND ', $range_conditions); } break; case 'date': if($keywords[$field]) { if( in_array($keywords[$field], Array('today', 'yesterday')) ) { $current_time = getdate(); $day_begin = adodb_mktime(0, 0, 0, $current_time['mon'], $current_time['mday'], $current_time['year']); $time_mapping = Array('today' => $day_begin, 'yesterday' => ($day_begin - 86400)); $min_time = $time_mapping[$keywords[$field]]; } else { $time_mapping = Array( 'last_week' => 604800, 'last_month' => 2628000, 'last_3_months' => 7884000, 'last_6_months' => 15768000, 'last_year' => 31536000 ); $min_time = adodb_mktime() - $time_mapping[$keywords[$field]]; } $condition = $field_name.' > '.$min_time; } break; } if($condition) { if($join_clause) { $join_clauses[] = $join_clause; } $relevance_parts[] = 'IF('.$condition.', '.$record['Priority'].', 0)'; if($glues[$field] == 1) // and { if($condition_mode == 'WHERE') { $and_conditions[] = $condition; } else { $and_having_conditions[] = $condition; } } else // or { if($condition_mode == 'WHERE') { $or_conditions[] = $condition; } else { $or_having_conditions[] = $condition; } } // create search log record $search_log_data = Array('search_config' => $record, 'verb' => getArrayValue($verbs, $field), 'value' => ($record['FieldType'] == 'range') ? $search_keywords[$field.'_from'].'|'.$search_keywords[$field.'_to'] : $search_keywords[$field]); $search_log[] = $this->Application->Phrase('la_Field').' "'.$this->getHuman('Field', $search_log_data).'" '.$this->getHuman('Verb', $search_log_data).' '.$this->Application->Phrase('la_Value').' '.$this->getHuman('Value', $search_log_data).' '.$this->Application->Phrase($glues[$field] == 1 ? 'lu_And' : 'lu_Or'); } } $search_log = implode('
', $search_log); $search_log = preg_replace('/(.*) '.preg_quote($this->Application->Phrase('lu_and'), '/').'|'.preg_quote($this->Application->Phrase('lu_or'), '/').'$/is', '\\1', $search_log); $this->saveToSearchLog($search_log, 1); // advanced search $this->Application->StoreVar('highlight_keywords', serialize($highlight_keywords)); // making relevance clause if($relevance_parts) { $rel_keywords = $this->Application->ConfigValue('SearchRel_Keyword_products') / 100; $rel_pop = $this->Application->ConfigValue('SearchRel_Pop_products') / 100; $rel_rating = $this->Application->ConfigValue('SearchRel_Rating_products') / 100; $relevance_clause = '('.implode(' + ', $relevance_parts).') / '.$weight_sum.' * '.$rel_keywords; $relevance_clause .= ' + (Hits + 1) / (MAX(Hits) + 1) * '.$rel_pop; $relevance_clause .= ' + (CachedRating + 1) / (MAX(CachedRating) + 1) * '.$rel_rating; } else { $relevance_clause = '0'; } // building having clause if($or_having_conditions) { $and_having_conditions[] = '('.implode(' OR ', $or_having_conditions).')'; } $having_clause = implode(' AND ', $and_having_conditions); $having_clause = $having_clause ? ' HAVING '.$having_clause : ''; // building where clause if($or_conditions) { $and_conditions[] = '('.implode(' OR ', $or_conditions).')'; } // $and_conditions[] = $product_table.'.Status = 1'; $where_clause = implode(' AND ', $and_conditions); if(!$where_clause) { if($having_clause) { $where_clause = '1'; } else { $where_clause = '0'; $this->Application->SetVar('adv_search_error', 1); } } $where_clause .= ' AND '.$product_table.'.Status = 1'; // building final search query $search_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search'; $this->Conn->Query('DROP TABLE IF EXISTS '.$search_table); $sql = ' CREATE TABLE '.$search_table.' SELECT '.$relevance_clause.' AS Relevance, '.$product_table.'.ProductId AS ItemId, '.$product_table.'.ResourceId AS ResourceId, 11 AS ItemType, '.$product_table.'.EditorsPick AS EdPick FROM '.$product_table.' '.implode(' ', $join_clauses).' WHERE '.$where_clause.' GROUP BY '.$product_table.'.ProductId'. $having_clause; $res = $this->Conn->Query($sql); } function getHuman($type, $search_data) { $type = ucfirst(strtolower($type)); extract($search_data); switch ($type) { case 'Field': return $this->Application->Phrase($search_config['DisplayName']); break; case 'Verb': return $verb ? $this->Application->Phrase('lu_advsearch_'.$verb) : ''; break; case 'Value': switch ($search_config['FieldType']) { case 'date': $values = Array(0 => 'lu_comm_Any', 'today' => 'lu_comm_Today', 'yesterday' => 'lu_comm_Yesterday', 'last_week' => 'lu_comm_LastWeek', 'last_month' => 'lu_comm_LastMonth', 'last_3_months' => 'lu_comm_Last3Months', 'last_6_months' => 'lu_comm_Last6Months', 'last_year' => 'lu_comm_LastYear'); $ret = $this->Application->Phrase($values[$value]); break; case 'range': $value = explode('|', $value); return $this->Application->Phrase('lu_comm_From').' "'.$value[0].'" '.$this->Application->Phrase('lu_comm_To').' "'.$value[1].'"'; break; case 'boolean': $values = Array(1 => 'lu_comm_Yes', 0 => 'lu_comm_No', -1 => 'lu_comm_Both'); $ret = $this->Application->Phrase($values[$value]); break; case 'text': $ret = $value; break; } return '"'.$ret.'"'; break; } } /** * Set's correct page for list * based on data provided with event * * @param kEvent $event * @access private * @see OnListBuild */ function SetPagination(&$event) { // get PerPage (forced -> session -> config -> 10) $per_page = $this->getPerPage($event); $object =& $event->getObject(); $object->SetPerPage($per_page); $this->Application->StoreVarDefault($event->getPrefixSpecial().'_Page', 1); $page = $this->Application->GetVar($event->getPrefixSpecial().'_Page'); if (!$page) { $page = $this->Application->GetVar($event->getPrefixSpecial(true).'_Page'); } if (!$page) { if( $this->Application->RewriteURLs() ) { $page = $this->Application->GetVar($event->Prefix.'_Page'); if (!$page) { $page = $this->Application->RecallVar($event->Prefix.'_Page'); } if($page) $this->Application->StoreVar($event->getPrefixSpecial().'_Page', $page); } else { $page = $this->Application->RecallVar($event->getPrefixSpecial().'_Page'); } } else { $this->Application->StoreVar($event->getPrefixSpecial().'_Page', $page); } if( !$event->getEventParam('skip_counting') ) { $pages = $object->GetTotalPages(); if($page > $pages) { $this->Application->StoreVar($event->getPrefixSpecial().'_Page', 1); $page = 1; } } /*$cur_per_page = $per_page; $per_page = $event->getEventParam('per_page'); if ($per_page == 'list_next') { $cur_page = $page; $object =& $this->Application->recallObject($event->Prefix); $object->SetPerPage(1); $cur_item_index = $object->CurrentIndex; $page = ($cur_page-1) * $cur_per_page + $cur_item_index + 1; $object->SetPerPage(1); }*/ $object->SetPage($page); } /* === RELATED TO IMPORT/EXPORT: BEGIN === */ /** * Shows export dialog * * @param kEvent $event */ function OnExport(&$event) { // use old fasion (in-portal) grid $selector_name = $this->Application->getUnitOption($event->Prefix, 'CatalogSelectorName'); if ($selector_name) { $selected_ids = $this->Application->GetVar($selector_name); } else { $selected_ids = $this->StoreSelectedIDs($event); if (implode(',', $selected_ids) == '') { // K4 fix when no ids found bad selected ids array is formed $selected_ids = false; } } $selected_cats_ids = $this->Application->GetVar('export_categories'); $this->Application->StoreVar($event->Prefix.'_export_ids', $selected_ids ? implode(',', $selected_ids) : '' ); $this->Application->StoreVar($event->Prefix.'_export_cats_ids', $selected_cats_ids); $export_helper =& $this->Application->recallObject('CatItemExportHelper'); $event->redirect = $export_helper->getModuleFolder($event).'/export'; $redirect_params = Array( 'm_opener' => 'd', $this->Prefix.'.export_event' => 'OnNew', 'pass' => 'all,'.$this->Prefix.'.export'); $event->setRedirectParams($redirect_params); } /** * Performs each export step & displays progress percent * * @param kEvent $event */ function OnExportProgress(&$event) { $export_object =& $this->Application->recallObject('CatItemExportHelper'); /* @var $export_object kCatDBItemExportHelper */ $event = new kEvent($event->getPrefixSpecial().':OnDummy'); $action_method = 'perform'.ucfirst($event->Special); $field_values = $export_object->$action_method($event); // finish code is done from JS now if ($field_values['start_from'] == $field_values['total_records']) { if ($event->Special == 'import') { $this->Application->StoreVar('PermCache_UpdateRequired', 1); $this->Application->Redirect('in-portal/categories/cache_updater', Array('m_opener' => 'r', 'pass' => 'm', 'continue' => 1, 'no_amp' => 1)); } elseif ($event->Special == 'export') { $template = $this->Application->getUnitOption($event->Prefix, 'ModuleFolder').'/'.$event->Special.'_finish'; $this->Application->Redirect($template, Array('pass' => 'all')); } } $export_options = $export_object->loadOptions($event); echo $export_options['start_from'] * 100 / $export_options['total_records']; $event->status = erSTOP; } /** * Returns specific to each item type columns only * * @param kEvent $event * @return Array */ function getCustomExportColumns(&$event) { return Array( '__VIRTUAL__ThumbnailImage' => 'ThumbnailImage', '__VIRTUAL__FullImage' => 'FullImage', '__VIRTUAL__ImageAlt' => 'ImageAlt'); } /** * Sets non standart virtual fields (e.g. to other tables) * * @param kEvent $event */ function setCustomExportColumns(&$event) { $this->restorePrimaryImage($event); } /** * Create/Update primary image record in info found in imported data * * @param kEvent $event */ function restorePrimaryImage(&$event) { $object =& $event->getObject(); $has_image_info = $object->GetDBField('ImageAlt') && ($object->GetDBField('ThumbnailImage') || $object->GetDBField('FullImage')); if (!$has_image_info) { return false; } $image_data = $object->getPrimaryImageData(); $image =& $this->Application->recallObject('img', null, Array('skip_autoload' => true)); if ($image_data) { $image->Load($image_data['ImageId']); } else { $image->Clear(); $image->SetDBField('Name', 'main'); $image->SetDBField('DefaultImg', 1); $image->SetDBField('ResourceId', $object->GetDBField('ResourceId')); } $image->SetDBField('AltName', $object->GetDBField('ImageAlt')); if ($object->GetDBField('ThumbnailImage')) { $thumbnail_field = $this->isURL( $object->GetDBField('ThumbnailImage') ) ? 'ThumbUrl' : 'ThumbPath'; $image->SetDBField($thumbnail_field, $object->GetDBField('ThumbnailImage') ); $image->SetDBField('LocalThumb', $thumbnail_field == 'ThumbPath' ? 1 : 0); } if (!$object->GetDBField('FullImage')) { $image->SetDBField('SameImages', 1); } else { $image->SetDBField('SameImages', 0); $full_field = $this->isURL( $object->GetDBField('FullImage') ) ? 'Url' : 'LocalPath'; $image->SetDBField($full_field, $object->GetDBField('FullImage') ); $image->SetDBField('LocalImage', $full_field == 'LocalPath' ? 1 : 0); } if ($image->isLoaded()) { $image->Update(); } else { $image->Create(); } } function isURL($path) { return preg_match('#(http|https)://(.*)#', $path); } /** * Prepares item for import/export operations * * @param kEvent $event */ function OnNew(&$event) { parent::OnNew($event); if ($event->Special == 'import' || $event->Special == 'export') { $export_helper =& $this->Application->recallObject('CatItemExportHelper'); $export_helper->setRequiredFields($event); $this->Application->StoreVar('ImportCategory', 0); } } /** * Process items selected in item_selector * * @param kEvent $event */ function OnProcessSelected(&$event) { $selected_ids = $this->Application->GetVar('selected_ids'); $dst_field = $this->Application->RecallVar('dst_field'); if ($dst_field == 'ItemCategory') { // Item Edit -> Categories Tab -> New Categories $object =& $event->getObject(); $category_ids = explode(',', $selected_ids['c']); foreach ($category_ids as $category_id) { $object->assignToCategory($category_id); } } if ($dst_field == 'ImportCategory') { // Tools -> Import -> Item Import -> Select Import Category $this->Application->StoreVar('ImportCategory', $selected_ids['c']); // $this->Application->StoreVar($event->getPrefixSpecial().'_ForceNotValid', 1); // not to loose import/export values on form refresh $this->Application->SetVar($event->getPrefixSpecial().'_id', 0); $this->Application->SetVar($event->getPrefixSpecial().'_event', 'OnExportBegin'); $passed = $this->Application->GetVar('passed'); $this->Application->SetVar('passed', $passed.','.$event->getPrefixSpecial()); $event->setEventParam('pass_events', true); } $this->finalizePopup($event); } /** * Saves Import/Export settings to session * * @param kEvent $event */ function OnSaveSettings(&$event) { $event->redirect = false; $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); if ($items_info) { list($id, $field_values) = each($items_info); $object =& $event->getObject( Array('skip_autoload' => true) ); $object->SetFieldsFromHash($field_values); $field_values['ImportFilename'] = $object->GetDBField('ImportFilename'); //if upload formatter has renamed the file during moving !!! $field_values['ImportSource'] = 2; $field_values['ImportLocalFilename'] = $object->GetDBField('ImportFilename'); $items_info[$id] = $field_values; $this->Application->StoreVar($event->getPrefixSpecial().'_ItemsInfo', serialize($items_info)); } } function OnCancelAction(&$event) { $event->redirect_params = Array('pass' => 'all,'.$event->GetPrefixSpecial()); $event->redirect = $this->Application->GetVar('cancel_template'); } /* === RELATED TO IMPORT/EXPORT: END === */ /** * Stores item's owner login into separate field together with id * * @param kEvent $event * @param string $id_field * @param string $cached_field */ function cacheItemOwner(&$event, $id_field, $cached_field) { $object =& $event->getObject(); $user_id = $object->GetDBField($id_field); $options = $object->GetFieldOptions($id_field); if (isset($options['options'][$user_id])) { $object->SetDBField($cached_field, $options['options'][$user_id]); } else { $id_field = $this->Application->getUnitOption('u', 'IDField'); $table_name = $this->Application->getUnitOption('u', 'TableName'); $sql = 'SELECT Login FROM '.$table_name.' WHERE '.$id_field.' = '.$user_id; $object->SetDBField($cached_field, $this->Conn->GetOne($sql)); } } /** * Saves item beeing edited into temp table * * @param kEvent $event */ function OnPreSave(&$event) { parent::OnPreSave($event); $use_pending_editing = $this->Application->getUnitOption($event->Prefix, 'UsePendingEditing'); if ($event->status == erSUCCESS && $use_pending_editing) { // decision: clone or not clone $object =& $event->getObject(); if ($object->GetID() == 0 || $object->GetDBField('OrgId') > 0) { // new items or cloned items shouldn't be cloned again return true; } $perm_helper =& $this->Application->recallObject('PermissionsHelper'); if ($perm_helper->ModifyCheckPermission($object->GetDBField('CreatedById'), $object->GetDBField('CategoryId'), $event->Prefix) == 2) { // 1. clone original item $temp_handler =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler'); $cloned_ids = $temp_handler->CloneItems($event->Prefix, $event->Special, Array($object->GetID()), null, null, null, true); $ci_table = $this->Application->GetTempName(TABLE_PREFIX.'CategoryItems'); // 2. delete record from CategoryItems (about cloned item) that was automatically created during call of Create method of kCatDBItem $sql = 'SELECT ResourceId FROM '.$object->TableName.' WHERE '.$object->IDField.' = '.$cloned_ids[0]; $clone_resource_id = $this->Conn->GetOne($sql); $sql = 'DELETE FROM '.$ci_table.' WHERE ItemResourceId = '.$clone_resource_id.' AND PrimaryCat = 1'; $this->Conn->Query($sql); // 3. copy main item categoryitems to cloned item $sql = ' INSERT INTO '.$ci_table.' (CategoryId, ItemResourceId, PrimaryCat, ItemPrefix, Filename) SELECT CategoryId, '.$clone_resource_id.' AS ItemResourceId, PrimaryCat, ItemPrefix, Filename FROM '.$ci_table.' WHERE ItemResourceId = '.$object->GetDBField('ResourceId'); $this->Conn->Query($sql); // 4. put cloned id to OrgId field of item being cloned $sql = 'UPDATE '.$object->TableName.' SET OrgId = '.$object->GetID().' WHERE '.$object->IDField.' = '.$cloned_ids[0]; $this->Conn->Query($sql); // 5. substitute id of item being cloned with clone id $this->Application->SetVar($event->getPrefixSpecial().'_id', $cloned_ids[0]); $selected_ids = $this->getSelectedIDs($event, true); $selected_ids[ array_search($object->GetID(), $selected_ids) ] = $cloned_ids[0]; $this->StoreSelectedIDs($event, $selected_ids); // 6. delete original item from temp table $temp_handler->DeleteItems($event->Prefix, $event->Special, Array($object->GetID())); } } } /** * Sets default expiration based on module setting * * @param kEvent $event */ function OnPreCreate(&$event) { parent::OnPreCreate($event); if ($event->status == erSUCCESS) { $object =& $event->getObject(); $object->SetDBField('CreatedById', $this->Application->RecallVar('user_id')); } } /** * Occures before original item of item in pending editing got deleted (for hooking only) * * @param kEvent $event */ function OnBeforeDeleteOriginal(&$event) { } /** * Occures before an item is cloneded * Id of ORIGINAL item is passed as event' 'id' param * Do not call object' Update method in this event, just set needed fields! * * @param kEvent $event */ function OnBeforeClone(&$event) { if ($this->Application->GetVar('ResetCatBeforeClone')) { $object =& $event->getObject(); $object->SetDBField('CategoryId', null); } } /** * Set status for new category item based on user permission in category * * @param kEvent $event */ function OnBeforeItemCreate(&$event) { if ($this->Application->IsAdmin()) { return true; } $use_pending_editing = $this->Application->getUnitOption($event->Prefix, 'UsePendingEditing'); if ($use_pending_editing) { $object =& $event->getObject(); /* @var $object kDBItem */ $perm_helper =& $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $primary_category = $object->GetDBField('CategoryId') > 0 ? $object->GetDBField('CategoryId') : $this->Application->GetVar('m_cat_id'); $item_status = $perm_helper->AddCheckPermission($primary_category, $event->Prefix); if ($item_status == STATUS_DISABLED) { $event->status = erFAIL; return false; } else { $object->SetDBField('Status', $item_status); } } } /** * Creates category item & redirects to confirmation template (front-end only) * * @param kEvent $event */ function OnCreate(&$event) { parent::OnCreate($event); $this->SetFrontRedirectTemplate($event, 'suggest'); } /** * Creates category item & redirects to confirmation template (front-end only) * * @param kEvent $event */ function OnUpdate(&$event) { $use_pending = $this->Application->getUnitOption($event->Prefix, 'UsePendingEditing'); if ($this->Application->IsAdmin() || !$use_pending) { parent::OnUpdate($event); $this->SetFrontRedirectTemplate($event, 'modify'); return ; } $object =& $event->getObject(Array('skip_autoload' => true)); /* @var $object kCatDBItem */ $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); if ($items_info) { $perm_helper =& $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $temp_handler =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler'); /* @var $temp_handler kTempTablesHandler */ foreach ($items_info as $id => $field_values) { $object->Load($id); $edit_perm = $perm_helper->ModifyCheckPermission($object->GetDBField('CreatedById'), $object->GetDBField('CategoryId'), $event->Prefix); if ($use_pending && !$object->GetDBField('OrgId') && ($edit_perm == STATUS_PENDING)) { // pending editing enabled + not pending copy -> get/create pending copy & save changes to it $original_id = $object->GetID(); $original_resource_id = $object->GetDBField('ResourceId'); $object->Load($original_id, 'OrgId'); if (!$object->isLoaded()) { // 1. user has no pending copy of live item -> clone live item $cloned_ids = $temp_handler->CloneItems($event->Prefix, $event->Special, Array($original_id), null, null, null, true); $object->Load($cloned_ids[0]); $object->SetFieldsFromHash($field_values); // 1a. delete record from CategoryItems (about cloned item) that was automatically created during call of Create method of kCatDBItem $ci_table = $this->Application->getUnitOption('ci', 'TableName'); $sql = 'DELETE FROM '.$ci_table.' WHERE ItemResourceId = '.$object->GetDBField('ResourceId').' AND PrimaryCat = 1'; $this->Conn->Query($sql); // 1b. copy main item categoryitems to cloned item $sql = 'INSERT INTO '.$ci_table.' (CategoryId, ItemResourceId, PrimaryCat, ItemPrefix, Filename) SELECT CategoryId, '.$object->GetDBField('ResourceId').' AS ItemResourceId, PrimaryCat, ItemPrefix, Filename FROM '.$ci_table.' WHERE ItemResourceId = '.$original_resource_id; $this->Conn->Query($sql); // 1c. put cloned id to OrgId field of item being cloned $object->SetDBField('Status', STATUS_PENDING_EDITING); $object->SetDBField('OrgId', $original_id); } else { // 2. user has pending copy of live item -> just update field values $object->SetFieldsFromHash($field_values); } // update id in request (used for redirect in mod-rewrite mode) $this->Application->SetVar($event->getPrefixSpecial().'_id', $object->GetID()); } else { // 3. already editing pending copy -> just update field values $object->SetFieldsFromHash($field_values); } if ($object->Update()) { $event->status = erSUCCESS; } else { $event->status = erFAIL; $event->redirect = false; break; } } } $this->SetFrontRedirectTemplate($event, 'modify'); } /** * Sets next template to one required for front-end after adding/modifying item * * @param kEvent $event * @param string $template_key - {suggest,modify} */ function SetFrontRedirectTemplate(&$event, $template_key) { if ($this->Application->IsAdmin() || $event->status != erSUCCESS) { return ; } // prepare redirect template $object =& $event->getObject(); $is_active = ($object->GetDBField('Status') == STATUS_ACTIVE); $next_template = $is_active ? 'confirm_template' : 'pending_confirm_template'; $event->redirect = $this->Application->GetVar($template_key.'_'.$next_template); $event->SetRedirectParam('opener', 's'); // send email events $perm_prefix = $this->Application->getUnitOption($event->Prefix, 'PermItemPrefix'); switch ($event->Name) { case 'OnCreate': $event_suffix = $is_active ? 'ADD' : 'ADD.PENDING'; $this->Application->EmailEventAdmin($perm_prefix.'.'.$event_suffix); // there are no ADD.PENDING event for admin :( $this->Application->EmailEventUser($perm_prefix.'.'.$event_suffix, $object->GetDBField('CreatedById')); break; case 'OnUpdate': $event_suffix = $is_active ? 'MODIFY' : 'MODIFY.PENDING'; $this->Application->EmailEventAdmin($perm_prefix.'.'.$event_suffix); // there are no ADD.PENDING event for admin :( $this->Application->EmailEventUser($perm_prefix.'.'.$event_suffix, $object->GetDBField('ModifiedById')); break; } } /** * Apply same processing to each item beeing selected in grid * * @param kEvent $event * @access private */ function iterateItems(&$event) { if ($event->Name != 'OnMassApprove' && $event->Name != 'OnMassDecline') { return parent::iterateItems($event); } if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { return; } $object =& $event->getObject( Array('skip_autoload' => true) ); $ids = $this->StoreSelectedIDs($event); if ($ids) { foreach ($ids as $id) { $object->Load($id); switch ($event->Name) { case 'OnMassApprove': $ret = $object->ApproveChanges(); break; case 'OnMassDecline': $ret = $object->DeclineChanges(); break; } if (!$ret) { $event->status = erFAIL; $event->redirect = false; break; } } } } /** * Deletes items & preserves clean env * * @param kEvent $event */ function OnMassDelete(&$event) { parent::OnMassDelete($event); if ($event->status == erSUCCESS && !$this->Application->IsAdmin()) { $event->SetRedirectParam('pass', 'm'); $event->SetRedirectParam('m_cat_id', 0); } } /** * Deletes items & preserves clean env * * @param kEvent $event */ function OnDelete(&$event) { parent::OnDelete($event); if ($event->status == erSUCCESS && !$this->Application->IsAdmin()) { $event->SetRedirectParam('pass', 'm'); $event->SetRedirectParam('m_cat_id', 0); } } /** * Checks, that currently loaded item is allowed for viewing (non permission-based) * * @param kEvent $event * @return bool */ function checkItemStatus(&$event) { $object =& $event->getObject(); if (!$object->isLoaded()) { return true; } $status = $object->GetDBField('Status'); $user_id = $this->Application->RecallVar('user_id'); if (($status == -2 || $status == STATUS_PENDING) && ($object->GetDBField('CreatedById') == $user_id)) { return true; } return $status == STATUS_ACTIVE; } /** * Set sorting directly to session * * @param kEvent $event */ function OnSetSortingDirect(&$event) { $combined = $this->Application->GetVar($event->Prefix.'_CombinedSorting'); if ($combined) { list($field, $dir) = explode('|', $combined); $this->Application->StoreVar($event->Prefix.'_Sort1', $field); $this->Application->StoreVar($event->Prefix.'_Sort1_Dir', $dir); return ; } $field_pos = $this->Application->GetVar($event->Prefix.'_SortPos'); $this->Application->LinkVar($event->Prefix.'_Sort'.$field_pos, $event->Prefix.'_Sort'.$field_pos); $this->Application->LinkVar($event->Prefix.'_Sort'.$field_pos.'_Dir', $event->Prefix.'_Sort'.$field_pos.'_Dir'); } /** * Set's correct sorting for list * based on data provided with event * * @param kEvent $event * @access private * @see OnListBuild */ function SetSorting(&$event) { if ($this->Application->IsAdmin()) { parent::SetSorting($event); return ; } $event->setPseudoClass('_List'); $object =& $event->getObject(); $cur_sort1 = $this->Application->RecallVar($event->Prefix.'_Sort1'); $cur_sort1_dir = $this->Application->RecallVar($event->Prefix.'_Sort1_Dir'); $cur_sort2 = $this->Application->RecallVar($event->Prefix.'_Sort2'); $cur_sort2_dir = $this->Application->RecallVar($event->Prefix.'_Sort2_Dir'); $sorting_configs = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping'); $list_sortings = $this->Application->getUnitOption($event->Prefix, 'ListSortings'); $sorting_prefix = getArrayValue($list_sortings, $event->Special) ? $event->Special : ''; $tag_sort_by = $event->getEventParam('sort_by'); if ($tag_sort_by) { if ($tag_sort_by == 'random') { $by = 'RAND()'; $dir = ''; } else { list($by, $dir) = explode(',', $tag_sort_by); } $object->AddOrderField($by, $dir); } if ($sorting_configs && isset ($sorting_configs['DefaultSorting1Field'])){ $list_sortings[$sorting_prefix]['Sorting'] = Array( $this->Application->ConfigValue($sorting_configs['DefaultSorting1Field']) => $this->Application->ConfigValue($sorting_configs['DefaultSorting1Dir']), $this->Application->ConfigValue($sorting_configs['DefaultSorting2Field']) => $this->Application->ConfigValue($sorting_configs['DefaultSorting2Dir']), ); } // Use default if not specified if ( !$cur_sort1 || !$cur_sort1_dir) { if ( $sorting = getArrayValue($list_sortings, $sorting_prefix, 'Sorting') ) { reset($sorting); $cur_sort1 = key($sorting); $cur_sort1_dir = current($sorting); if (next($sorting)) { $cur_sort2 = key($sorting); $cur_sort2_dir = current($sorting); } } } if ( $forced_sorting = getArrayValue($list_sortings, $sorting_prefix, 'ForcedSorting') ) { foreach ($forced_sorting as $field => $dir) { $object->AddOrderField($field, $dir); } } if($cur_sort1 != '' && $cur_sort1_dir != '') { $object->AddOrderField($cur_sort1, $cur_sort1_dir); } if($cur_sort2 != '' && $cur_sort2_dir != '') { $object->AddOrderField($cur_sort2, $cur_sort2_dir); } } /** * Returns current per-page setting for list * * @param kEvent $event * @return int */ function getPerPage(&$event) { if ($this->Application->IsAdmin()) { return parent::getPerPage($event); } $special = $event->Special; $event->Init($event->Prefix); $per_page = parent::getPerPage($event); $event->Init($event->Prefix, $special); return $per_page; } } ?>