Page Menu
Home
In-Portal Phabricator
Search
Configure Global Search
Log In
Files
F1033767
in-portal
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Fri, Jun 20, 10:08 PM
Size
126 KB
Mime Type
text/x-diff
Expires
Sun, Jun 22, 10:08 PM (1 h, 59 m)
Engine
blob
Format
Raw Data
Handle
668091
Attached To
rINP In-Portal
in-portal
View Options
Index: branches/unlabeled/unlabeled-1.65.2/kernel/units/general/cat_event_handler.php
===================================================================
--- branches/unlabeled/unlabeled-1.65.2/kernel/units/general/cat_event_handler.php (nonexistent)
+++ branches/unlabeled/unlabeled-1.65.2/kernel/units/general/cat_event_handler.php (revision 6141)
@@ -0,0 +1,1893 @@
+<?php
+
+$application =& kApplication::Instance();
+$application->Factory->includeClassFile('kDBEventHandler');
+
+class kCatDBEventHandler extends InpDBEventHandler {
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ if ($event->Name == 'OnEdit' || $event->Name == 'OnSave') {
+ // check each id from selected individually and only if all are allowed proceed next
+ if ($event->Name == 'OnEdit') {
+ $selected_ids = implode(',', $this->StoreSelectedIDs($event));
+ }
+ else {
+ $selected_ids = $this->Application->RecallVar($event->getPrefixSpecial().'_selected_ids');
+ }
+
+ $id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
+ $table_name = $this->Application->getUnitOption($event->Prefix, 'TableName');
+ $sql = 'SELECT '.$id_field.', CreatedById, ci.CategoryId
+ FROM '.$table_name.' item_table
+ LEFT JOIN '.$this->Application->getUnitOption('ci', 'TableName').' ci ON ci.ItemResourceId = item_table.ResourceId
+ WHERE '.$id_field.' IN ('.$selected_ids.') AND (ci.PrimaryCat = 1)';
+ $items = $this->Conn->Query($sql, $id_field);
+
+ $perm_value = true;
+ $perm_helper =& $this->Application->recallObject('PermissionsHelper');
+ foreach ($items as $item_id => $item_data) {
+ if ($perm_helper->ModifyCheckPermission($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;
+ }
+
+ 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');
+ $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');
+ $except_types = $event->getEventParam('except');
+ $type_clauses = Array();
+
+ $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 (strpos($types, 'search') !== false || strpos($except_types, 'search') !== false) {
+ $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 (strpos($types, 'related') !== false || strpos($except_types, 'related') !== false) {
+
+ $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;
+ }
+
+ 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') ) {
+ $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).')');
+ }
+ 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->GetVar('u_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);
+
+ if ( !$this->Application->IsAdmin() )
+ {
+ $object->addFilter('status_filter', $object->TableName.'.Status = 1');
+ if ($this->Application->getUnitOption($event->Prefix, 'UsePendingEditing')) {
+ // 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 ($this->Application->getUnitOption($event->Prefix, 'UsePendingEditing')) {
+ $pending_ids = $this->Conn->GetCol(
+ 'SELECT OrgId FROM '.$object->TableName.'
+ WHERE Status = -2 AND OrgId IS NOT NULL');
+ if ($pending_ids) {
+ $object->addFilter('no_original_filter', '%1$s.'.$object->IDField.' NOT IN ('.implode(',', $pending_ids).')');
+ }
+ }
+ }
+
+ $types = $event->getEventParam('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);
+
+ $except_types_array = explode(',', $types);
+
+ 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->GetVar('u_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 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')
+ {
+ $this->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;
+ }
+
+ }
+
+ 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');
+ $product_table = $this->Application->getUnitOption('p', 'TableName');
+
+ $sql = ' SELECT * FROM '.$this->Application->getUnitOption('confs', 'TableName').'
+ WHERE ModuleName="In-Commerce"
+ 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 '.$product_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_object =& $this->Application->recallObject('u');
+ $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
+ {
+ $exploded = explode('.', $foreign_field); // format: table.field_name
+ $foreign_table = TABLE_PREFIX.$exploded[0];
+
+ $alias_counter++;
+ $alias = 't'.$alias_counter;
+
+ $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}', $product_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 '.$product_table.'.Status=1';
+
+ if($this->Application->GetVar('Action') == 'm_simple_subsearch') // subsearch, In-portal
+ {
+ if( $event->getEventParam('ResultIds') )
+ {
+ $where_clause .= ' AND '.$product_table.'.ResourceId IN ('.implode(',', $event->specificParams['ResultIds']).')';
+ }
+ }
+ if( $event->MasterEvent && $event->MasterEvent->Name == 'OnListBuild' ) // subsearch, k4
+ {
+ if( $event->MasterEvent->getEventParam('ResultIds') )
+ {
+ $where_clause .= ' AND '.$product_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)';
+ }
+ }
+ $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(' + ', $revelance_parts).') / '.$weight_sum.' * '.$rel_keywords;
+ $relevance_clause .= ' + (Hits + 1) / (MAX(Hits) + 1) * '.$rel_pop;
+ $relevance_clause .= ' + (CachedRating + 1) / (MAX(CachedRating) + 1) * '.$rel_rating;
+
+ // building final search query
+ if (!$this->Application->GetVar('INPORTAL_ON')) {
+ $this->Conn->Query('DROP TABLE IF EXISTS '.$search_table); // erase old search table if clean k4 event
+ }
+
+ if ($this->Conn->Query('SHOW TABLES LIKE "'.$search_table.'"')) {
+ $select_intro = 'INSERT INTO '.$search_table.' (Relevance, ItemId, ResourceId, ItemType, EdPick) ';
+ }
+ else {
+ $select_intro = 'CREATE TABLE '.$search_table.' AS ';
+ }
+
+ $sql = $select_intro.' SELECT '.$relevance_clause.' AS Relevance,
+ '.$product_table.'.ProductId AS ItemId,
+ '.$product_table.'.ResourceId,
+ 11 AS ItemType,
+ '.$product_table.'.EditorsPick AS EdPick
+ FROM '.$object->TableName.'
+ '.implode(' ', $join_clauses).'
+ WHERE '.$where_clause.'
+ GROUP BY '.$product_table.'.ProductId';
+
+ $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);
+ $user_object =& $this->Application->recallObject('u');
+ $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('<br />', $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 === */
+
+ /**
+ * Returns module folder
+ *
+ * @param kEvent $event
+ * @return string
+ */
+ function getModuleFolder(&$event)
+ {
+ return $this->Application->getUnitOption($event->Prefix, 'ModuleFolder');
+ }
+
+ /**
+ * 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 {
+ $this->StoreSelectedIDs($event);
+ $selected_ids = $this->getSelectedIDs($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);
+
+ $event->redirect = $this->getModuleFolder($event).'/export';
+
+ $redirect_params = Array( 'm_opener' => 'd',
+ $this->Prefix.'.export_event' => 'OnNew',
+ 'pass' => 'all,'.$this->Prefix.'.export');
+
+ $event->setRedirectParams($redirect_params);
+ }
+
+ /**
+ * Export form validation & processing
+ *
+ * @param kEvent $event
+ */
+ function OnExportBegin(&$event)
+ {
+ $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
+ if (!$items_info)
+ {
+ $items_info = unserialize( $this->Application->RecallVar($event->getPrefixSpecial().'_ItemsInfo') );
+ $this->Application->SetVar($event->getPrefixSpecial(true), $items_info);
+ }
+
+ list($item_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 !!!
+
+ $object->setID($item_id);
+ $this->setRequiredFields($event);
+
+ $export_object =& $this->Application->recallObject('CatItemExportHelper');
+
+ // save export/import options
+ if ($event->Special == 'export')
+ {
+ $export_ids = $this->Application->RecallVar($event->Prefix.'_export_ids');
+ $export_cats_ids = $this->Application->RecallVar($event->Prefix.'_export_cats_ids');
+
+ // used for multistep export
+ $field_values['export_ids'] = $export_ids ? explode(',', $export_ids) : false;
+ $field_values['export_cats_ids'] = $export_cats_ids ? explode(',', $export_cats_ids) : Array( $this->Application->GetVar('m_cat_id') );
+ }
+
+ $field_values['ExportColumns'] = $field_values['ExportColumns'] ? explode('|', substr($field_values['ExportColumns'], 1, -1) ) : Array();
+ $field_values['start_from'] = 0;
+ $export_object->saveOptions($event, $field_values);
+
+ if( $export_object->verifyOptions($event) )
+ {
+ $event->redirect = $this->getModuleFolder($event).'/'.$event->Special.'_progress';
+
+ }
+ else
+ {
+ // make uploaded file local & change source selection
+ $filename = getArrayValue($field_values, 'ImportFilename');
+ if ($filename) {
+ $this->updateImportFiles($event);
+ $object->SetDBField('ImportSource', 2);
+ $field_values['ImportSource'] = 2;
+ $object->SetDBField('ImportLocalFilename', $filename);
+ $field_values['ImportLocalFilename'] = $filename;
+ $export_object->saveOptions($event, $field_values);
+ }
+
+ $event->status = erFAIL;
+ $event->redirect = false;
+ }
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @param kEvent $event
+ */
+ function OnExportCancel(&$event)
+ {
+ $this->OnGoBack($event);
+ }
+
+ /**
+ * Sets correct available & export fields
+ *
+ * @param kEvent $event
+ */
+ function prepareExportColumns(&$event)
+ {
+ $object =& $event->getObject( Array('skip_autoload' => true) );
+
+ $available_columns = Array();
+
+ // category field (mixed)
+ $available_columns['__CATEGORY__CategoryPath'] = 'CategoryPath';
+
+ if ($event->Special == 'import') {
+ // category field (separated fields)
+ $max_level = $this->Application->ConfigValue('MaxImportCategoryLevels');
+ $i = 0;
+ while ($i < $max_level) {
+ $available_columns['__CATEGORY__Category'.($i + 1)] = 'Category'.($i + 1);
+ $i++;
+ }
+ }
+
+ // db fields
+ foreach ($object->Fields as $field_name => $field_options)
+ {
+ if (!$object->SkipField($field_name))
+ {
+ $available_columns[$field_name] = $field_name.(getArrayValue($field_options, 'required') ? '*' : '');
+ }
+ }
+
+ $available_columns = array_merge_recursive2($available_columns, $this->getCustomExportColumns($event));
+
+ // custom fields
+ foreach ($object->customFields as $custom_id => $custom_name)
+ {
+ $available_columns['__CUSTOM__'.$custom_name] = $custom_name;
+ }
+
+ // columns already in use
+ $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
+ if ($items_info)
+ {
+ list($item_id, $field_values) = each($items_info);
+ $export_keys = $field_values['ExportColumns'];
+ $export_keys = $export_keys ? explode('|', substr($export_keys, 1, -1) ) : Array();
+ }
+ else {
+ $export_keys = Array();
+ }
+
+ $export_columns = Array();
+ foreach ($export_keys as $field_key)
+ {
+ $field_name = $this->getExportField($field_key);
+ $export_columns[$field_key] = $field_name;
+ unset($available_columns[$field_key]);
+ }
+
+ $options = $object->GetFieldOptions('ExportColumns');
+ $options['options'] = $export_columns;
+ $object->SetFieldOptions('ExportColumns', $options);
+
+ $options = $object->GetFieldOptions('AvailableColumns');
+ $options['options'] = $available_columns;
+ $object->SetFieldOptions('AvailableColumns', $options);
+
+ $this->updateImportFiles($event);
+ }
+
+ /**
+ * Updates uploaded files list
+ *
+ * @param kEvent $event
+ */
+ function updateImportFiles(&$event)
+ {
+ if ($event->Special != 'import') {
+ return false;
+ }
+
+ $object =& $event->getObject();
+
+ $import_filenames = Array();
+
+ if ($folder_handle = opendir(EXPORT_PATH)) {
+ while (false !== ($file = readdir($folder_handle))) {
+ if (is_dir(EXPORT_PATH.'/'.$file) || substr($file, 0, 1) == '.' || strtolower($file) == 'cvs' || strtolower($file) == 'dummy' || filesize(EXPORT_PATH.'/'.$file) == 0) continue;
+
+ $file_size = formatSize( filesize(EXPORT_PATH.'/'.$file) );
+ $import_filenames[$file] = $file.' ('.$file_size.')';
+ }
+ closedir($folder_handle);
+ }
+
+ $options = $object->GetFieldOptions('ImportLocalFilename');
+ $options['options'] = $import_filenames;
+ $object->SetFieldOptions('ImportLocalFilename', $options);
+ }
+
+ /**
+ * 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);
+ }
+
+// ImportLocalFilename
+
+ function getExportField($field_key)
+ {
+ $prepends = Array('__CUSTOM__', '__CATEGORY__');
+ foreach ($prepends as $prepend)
+ {
+ if (substr($field_key, 0, strlen($prepend) ) == $prepend)
+ {
+ $field_key = substr($field_key, strlen($prepend), strlen($field_key) );
+ break;
+ }
+ }
+ return $field_key;
+ }
+
+ /**
+ * Prepares item for import/export operations
+ *
+ * @param kEvent $event
+ */
+ function OnNew(&$event)
+ {
+ parent::OnNew($event);
+
+ if ($event->Special != 'import' && $event->Special != 'export') return ;
+ $this->setRequiredFields($event);
+ $this->Application->StoreVar('ImportCategory', 0);
+ }
+
+ /**
+ * set required fields based on import or export params
+ *
+ * @param kEvent $event
+ */
+ function setRequiredFields(&$event)
+ {
+ $required_fields['common'] = Array('FieldsSeparatedBy', 'LineEndings', 'CategoryFormat');
+
+ $required_fields['export'] = Array('ExportFormat', 'ExportFilename','ExportColumns');
+ $required_fields['import'] = Array('FieldTitles', 'ImportSource', 'CheckDuplicatesMethod'); // ImportFilename, ImportLocalFilename
+
+ $object =& $event->getObject();
+ if ($event->Special == 'import')
+ {
+ $import_source = Array(1 => 'ImportFilename', 2 => 'ImportLocalFilename');
+ $used_field = $import_source[ $object->GetDBField('ImportSource') ];
+
+ $required_fields[$event->Special][] = $used_field;
+ $object->Fields[$used_field]['error_field'] = 'ImportSource';
+
+ if ($object->GetDBField('FieldTitles') == 2) $required_fields[$event->Special][] = 'ExportColumns'; // manual field titles
+ }
+
+ $required_fields = array_merge($required_fields['common'], $required_fields[$event->Special]);
+ foreach ($required_fields as $required_field) {
+ $object->setRequired($required_field, true);
+ }
+ }
+
+ /**
+ * 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 = explode(',', $this->Application->RecallVar($event->getPrefixSpecial().'_selected_ids'));
+ $selected_ids[ array_search($object->GetID(), $selected_ids) ] = $cloned_ids[0];
+ $this->Application->StoreVar($event->getPrefixSpecial().'_selected_ids', implode(',', $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->GetVar('u_id'));
+ }
+ }
+
+ /**
+ * Occures before original item of item in pending editing got deleted (for hooking only)
+ *
+ * @param kEvent $event
+ */
+ function OnBeforeDeleteOriginal(&$event)
+ {
+
+ }
+
+ /**
+ * 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;
+ }
+ }
+ }
+ }
+
+}
+
+?>
\ No newline at end of file
Property changes on: branches/unlabeled/unlabeled-1.65.2/kernel/units/general/cat_event_handler.php
___________________________________________________________________
Added: cvs2svn:cvs-rev
## -0,0 +1 ##
+1.65
\ No newline at end of property
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: branches/unlabeled/unlabeled-1.65.2/core/units/general/cat_event_handler.php
===================================================================
--- branches/unlabeled/unlabeled-1.65.2/core/units/general/cat_event_handler.php (nonexistent)
+++ branches/unlabeled/unlabeled-1.65.2/core/units/general/cat_event_handler.php (revision 6141)
@@ -0,0 +1,1893 @@
+<?php
+
+$application =& kApplication::Instance();
+$application->Factory->includeClassFile('kDBEventHandler');
+
+class kCatDBEventHandler extends InpDBEventHandler {
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ if ($event->Name == 'OnEdit' || $event->Name == 'OnSave') {
+ // check each id from selected individually and only if all are allowed proceed next
+ if ($event->Name == 'OnEdit') {
+ $selected_ids = implode(',', $this->StoreSelectedIDs($event));
+ }
+ else {
+ $selected_ids = $this->Application->RecallVar($event->getPrefixSpecial().'_selected_ids');
+ }
+
+ $id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
+ $table_name = $this->Application->getUnitOption($event->Prefix, 'TableName');
+ $sql = 'SELECT '.$id_field.', CreatedById, ci.CategoryId
+ FROM '.$table_name.' item_table
+ LEFT JOIN '.$this->Application->getUnitOption('ci', 'TableName').' ci ON ci.ItemResourceId = item_table.ResourceId
+ WHERE '.$id_field.' IN ('.$selected_ids.') AND (ci.PrimaryCat = 1)';
+ $items = $this->Conn->Query($sql, $id_field);
+
+ $perm_value = true;
+ $perm_helper =& $this->Application->recallObject('PermissionsHelper');
+ foreach ($items as $item_id => $item_data) {
+ if ($perm_helper->ModifyCheckPermission($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;
+ }
+
+ 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');
+ $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');
+ $except_types = $event->getEventParam('except');
+ $type_clauses = Array();
+
+ $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 (strpos($types, 'search') !== false || strpos($except_types, 'search') !== false) {
+ $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 (strpos($types, 'related') !== false || strpos($except_types, 'related') !== false) {
+
+ $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;
+ }
+
+ 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') ) {
+ $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).')');
+ }
+ 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->GetVar('u_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);
+
+ if ( !$this->Application->IsAdmin() )
+ {
+ $object->addFilter('status_filter', $object->TableName.'.Status = 1');
+ if ($this->Application->getUnitOption($event->Prefix, 'UsePendingEditing')) {
+ // 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 ($this->Application->getUnitOption($event->Prefix, 'UsePendingEditing')) {
+ $pending_ids = $this->Conn->GetCol(
+ 'SELECT OrgId FROM '.$object->TableName.'
+ WHERE Status = -2 AND OrgId IS NOT NULL');
+ if ($pending_ids) {
+ $object->addFilter('no_original_filter', '%1$s.'.$object->IDField.' NOT IN ('.implode(',', $pending_ids).')');
+ }
+ }
+ }
+
+ $types = $event->getEventParam('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);
+
+ $except_types_array = explode(',', $types);
+
+ 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->GetVar('u_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 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')
+ {
+ $this->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;
+ }
+
+ }
+
+ 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');
+ $product_table = $this->Application->getUnitOption('p', 'TableName');
+
+ $sql = ' SELECT * FROM '.$this->Application->getUnitOption('confs', 'TableName').'
+ WHERE ModuleName="In-Commerce"
+ 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 '.$product_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_object =& $this->Application->recallObject('u');
+ $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
+ {
+ $exploded = explode('.', $foreign_field); // format: table.field_name
+ $foreign_table = TABLE_PREFIX.$exploded[0];
+
+ $alias_counter++;
+ $alias = 't'.$alias_counter;
+
+ $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}', $product_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 '.$product_table.'.Status=1';
+
+ if($this->Application->GetVar('Action') == 'm_simple_subsearch') // subsearch, In-portal
+ {
+ if( $event->getEventParam('ResultIds') )
+ {
+ $where_clause .= ' AND '.$product_table.'.ResourceId IN ('.implode(',', $event->specificParams['ResultIds']).')';
+ }
+ }
+ if( $event->MasterEvent && $event->MasterEvent->Name == 'OnListBuild' ) // subsearch, k4
+ {
+ if( $event->MasterEvent->getEventParam('ResultIds') )
+ {
+ $where_clause .= ' AND '.$product_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)';
+ }
+ }
+ $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(' + ', $revelance_parts).') / '.$weight_sum.' * '.$rel_keywords;
+ $relevance_clause .= ' + (Hits + 1) / (MAX(Hits) + 1) * '.$rel_pop;
+ $relevance_clause .= ' + (CachedRating + 1) / (MAX(CachedRating) + 1) * '.$rel_rating;
+
+ // building final search query
+ if (!$this->Application->GetVar('INPORTAL_ON')) {
+ $this->Conn->Query('DROP TABLE IF EXISTS '.$search_table); // erase old search table if clean k4 event
+ }
+
+ if ($this->Conn->Query('SHOW TABLES LIKE "'.$search_table.'"')) {
+ $select_intro = 'INSERT INTO '.$search_table.' (Relevance, ItemId, ResourceId, ItemType, EdPick) ';
+ }
+ else {
+ $select_intro = 'CREATE TABLE '.$search_table.' AS ';
+ }
+
+ $sql = $select_intro.' SELECT '.$relevance_clause.' AS Relevance,
+ '.$product_table.'.ProductId AS ItemId,
+ '.$product_table.'.ResourceId,
+ 11 AS ItemType,
+ '.$product_table.'.EditorsPick AS EdPick
+ FROM '.$object->TableName.'
+ '.implode(' ', $join_clauses).'
+ WHERE '.$where_clause.'
+ GROUP BY '.$product_table.'.ProductId';
+
+ $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);
+ $user_object =& $this->Application->recallObject('u');
+ $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('<br />', $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 === */
+
+ /**
+ * Returns module folder
+ *
+ * @param kEvent $event
+ * @return string
+ */
+ function getModuleFolder(&$event)
+ {
+ return $this->Application->getUnitOption($event->Prefix, 'ModuleFolder');
+ }
+
+ /**
+ * 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 {
+ $this->StoreSelectedIDs($event);
+ $selected_ids = $this->getSelectedIDs($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);
+
+ $event->redirect = $this->getModuleFolder($event).'/export';
+
+ $redirect_params = Array( 'm_opener' => 'd',
+ $this->Prefix.'.export_event' => 'OnNew',
+ 'pass' => 'all,'.$this->Prefix.'.export');
+
+ $event->setRedirectParams($redirect_params);
+ }
+
+ /**
+ * Export form validation & processing
+ *
+ * @param kEvent $event
+ */
+ function OnExportBegin(&$event)
+ {
+ $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
+ if (!$items_info)
+ {
+ $items_info = unserialize( $this->Application->RecallVar($event->getPrefixSpecial().'_ItemsInfo') );
+ $this->Application->SetVar($event->getPrefixSpecial(true), $items_info);
+ }
+
+ list($item_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 !!!
+
+ $object->setID($item_id);
+ $this->setRequiredFields($event);
+
+ $export_object =& $this->Application->recallObject('CatItemExportHelper');
+
+ // save export/import options
+ if ($event->Special == 'export')
+ {
+ $export_ids = $this->Application->RecallVar($event->Prefix.'_export_ids');
+ $export_cats_ids = $this->Application->RecallVar($event->Prefix.'_export_cats_ids');
+
+ // used for multistep export
+ $field_values['export_ids'] = $export_ids ? explode(',', $export_ids) : false;
+ $field_values['export_cats_ids'] = $export_cats_ids ? explode(',', $export_cats_ids) : Array( $this->Application->GetVar('m_cat_id') );
+ }
+
+ $field_values['ExportColumns'] = $field_values['ExportColumns'] ? explode('|', substr($field_values['ExportColumns'], 1, -1) ) : Array();
+ $field_values['start_from'] = 0;
+ $export_object->saveOptions($event, $field_values);
+
+ if( $export_object->verifyOptions($event) )
+ {
+ $event->redirect = $this->getModuleFolder($event).'/'.$event->Special.'_progress';
+
+ }
+ else
+ {
+ // make uploaded file local & change source selection
+ $filename = getArrayValue($field_values, 'ImportFilename');
+ if ($filename) {
+ $this->updateImportFiles($event);
+ $object->SetDBField('ImportSource', 2);
+ $field_values['ImportSource'] = 2;
+ $object->SetDBField('ImportLocalFilename', $filename);
+ $field_values['ImportLocalFilename'] = $filename;
+ $export_object->saveOptions($event, $field_values);
+ }
+
+ $event->status = erFAIL;
+ $event->redirect = false;
+ }
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @param kEvent $event
+ */
+ function OnExportCancel(&$event)
+ {
+ $this->OnGoBack($event);
+ }
+
+ /**
+ * Sets correct available & export fields
+ *
+ * @param kEvent $event
+ */
+ function prepareExportColumns(&$event)
+ {
+ $object =& $event->getObject( Array('skip_autoload' => true) );
+
+ $available_columns = Array();
+
+ // category field (mixed)
+ $available_columns['__CATEGORY__CategoryPath'] = 'CategoryPath';
+
+ if ($event->Special == 'import') {
+ // category field (separated fields)
+ $max_level = $this->Application->ConfigValue('MaxImportCategoryLevels');
+ $i = 0;
+ while ($i < $max_level) {
+ $available_columns['__CATEGORY__Category'.($i + 1)] = 'Category'.($i + 1);
+ $i++;
+ }
+ }
+
+ // db fields
+ foreach ($object->Fields as $field_name => $field_options)
+ {
+ if (!$object->SkipField($field_name))
+ {
+ $available_columns[$field_name] = $field_name.(getArrayValue($field_options, 'required') ? '*' : '');
+ }
+ }
+
+ $available_columns = array_merge_recursive2($available_columns, $this->getCustomExportColumns($event));
+
+ // custom fields
+ foreach ($object->customFields as $custom_id => $custom_name)
+ {
+ $available_columns['__CUSTOM__'.$custom_name] = $custom_name;
+ }
+
+ // columns already in use
+ $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
+ if ($items_info)
+ {
+ list($item_id, $field_values) = each($items_info);
+ $export_keys = $field_values['ExportColumns'];
+ $export_keys = $export_keys ? explode('|', substr($export_keys, 1, -1) ) : Array();
+ }
+ else {
+ $export_keys = Array();
+ }
+
+ $export_columns = Array();
+ foreach ($export_keys as $field_key)
+ {
+ $field_name = $this->getExportField($field_key);
+ $export_columns[$field_key] = $field_name;
+ unset($available_columns[$field_key]);
+ }
+
+ $options = $object->GetFieldOptions('ExportColumns');
+ $options['options'] = $export_columns;
+ $object->SetFieldOptions('ExportColumns', $options);
+
+ $options = $object->GetFieldOptions('AvailableColumns');
+ $options['options'] = $available_columns;
+ $object->SetFieldOptions('AvailableColumns', $options);
+
+ $this->updateImportFiles($event);
+ }
+
+ /**
+ * Updates uploaded files list
+ *
+ * @param kEvent $event
+ */
+ function updateImportFiles(&$event)
+ {
+ if ($event->Special != 'import') {
+ return false;
+ }
+
+ $object =& $event->getObject();
+
+ $import_filenames = Array();
+
+ if ($folder_handle = opendir(EXPORT_PATH)) {
+ while (false !== ($file = readdir($folder_handle))) {
+ if (is_dir(EXPORT_PATH.'/'.$file) || substr($file, 0, 1) == '.' || strtolower($file) == 'cvs' || strtolower($file) == 'dummy' || filesize(EXPORT_PATH.'/'.$file) == 0) continue;
+
+ $file_size = formatSize( filesize(EXPORT_PATH.'/'.$file) );
+ $import_filenames[$file] = $file.' ('.$file_size.')';
+ }
+ closedir($folder_handle);
+ }
+
+ $options = $object->GetFieldOptions('ImportLocalFilename');
+ $options['options'] = $import_filenames;
+ $object->SetFieldOptions('ImportLocalFilename', $options);
+ }
+
+ /**
+ * 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);
+ }
+
+// ImportLocalFilename
+
+ function getExportField($field_key)
+ {
+ $prepends = Array('__CUSTOM__', '__CATEGORY__');
+ foreach ($prepends as $prepend)
+ {
+ if (substr($field_key, 0, strlen($prepend) ) == $prepend)
+ {
+ $field_key = substr($field_key, strlen($prepend), strlen($field_key) );
+ break;
+ }
+ }
+ return $field_key;
+ }
+
+ /**
+ * Prepares item for import/export operations
+ *
+ * @param kEvent $event
+ */
+ function OnNew(&$event)
+ {
+ parent::OnNew($event);
+
+ if ($event->Special != 'import' && $event->Special != 'export') return ;
+ $this->setRequiredFields($event);
+ $this->Application->StoreVar('ImportCategory', 0);
+ }
+
+ /**
+ * set required fields based on import or export params
+ *
+ * @param kEvent $event
+ */
+ function setRequiredFields(&$event)
+ {
+ $required_fields['common'] = Array('FieldsSeparatedBy', 'LineEndings', 'CategoryFormat');
+
+ $required_fields['export'] = Array('ExportFormat', 'ExportFilename','ExportColumns');
+ $required_fields['import'] = Array('FieldTitles', 'ImportSource', 'CheckDuplicatesMethod'); // ImportFilename, ImportLocalFilename
+
+ $object =& $event->getObject();
+ if ($event->Special == 'import')
+ {
+ $import_source = Array(1 => 'ImportFilename', 2 => 'ImportLocalFilename');
+ $used_field = $import_source[ $object->GetDBField('ImportSource') ];
+
+ $required_fields[$event->Special][] = $used_field;
+ $object->Fields[$used_field]['error_field'] = 'ImportSource';
+
+ if ($object->GetDBField('FieldTitles') == 2) $required_fields[$event->Special][] = 'ExportColumns'; // manual field titles
+ }
+
+ $required_fields = array_merge($required_fields['common'], $required_fields[$event->Special]);
+ foreach ($required_fields as $required_field) {
+ $object->setRequired($required_field, true);
+ }
+ }
+
+ /**
+ * 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 = explode(',', $this->Application->RecallVar($event->getPrefixSpecial().'_selected_ids'));
+ $selected_ids[ array_search($object->GetID(), $selected_ids) ] = $cloned_ids[0];
+ $this->Application->StoreVar($event->getPrefixSpecial().'_selected_ids', implode(',', $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->GetVar('u_id'));
+ }
+ }
+
+ /**
+ * Occures before original item of item in pending editing got deleted (for hooking only)
+ *
+ * @param kEvent $event
+ */
+ function OnBeforeDeleteOriginal(&$event)
+ {
+
+ }
+
+ /**
+ * 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;
+ }
+ }
+ }
+ }
+
+}
+
+?>
\ No newline at end of file
Property changes on: branches/unlabeled/unlabeled-1.65.2/core/units/general/cat_event_handler.php
___________________________________________________________________
Added: cvs2svn:cvs-rev
## -0,0 +1 ##
+1.65
\ No newline at end of property
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Event Timeline
Log In to Comment