Index: trunk/kernel/admin_templates/incs/script.js
===================================================================
diff -u -N -r3299 -r3543
--- trunk/kernel/admin_templates/incs/script.js (.../script.js) (revision 3299)
+++ trunk/kernel/admin_templates/incs/script.js (.../script.js) (revision 3543)
@@ -464,11 +464,261 @@
{
if ( $kf.elements[i].id.match($cb_mask) )
{
-// alert('found:' + $kf.elements[i].id);
if ($kf.elements[i].checked) $tmp += '|'+$kf.elements[i].value;
}
}
if($tmp.length > 0) $tmp += '|';
document.getElementById($hidden_id).value = $tmp.replace(/,$/, '');
-// alert('field: '+$hidden_id+' = '+document.getElementById($hidden_id).value );
-}
\ No newline at end of file
+}
+
+// related to lists operations (moving)
+
+
+
+ function move_selected($from_list, $to_list)
+ {
+ if (typeof($from_list) != 'object') $from_list = document.getElementById($from_list);
+ if (typeof($to_list) != 'object') $to_list = document.getElementById($to_list);
+
+ if (has_selected_options($from_list))
+ {
+ var $from_array = select_to_array($from_list);
+ var $to_array = select_to_array($to_list);
+ var $new_from = Array();
+ var $cur = null;
+
+ for (var $i = 0; $i < $from_array.length; $i++)
+ {
+ $cur = $from_array[$i];
+ if ($cur[2]) // If selected - add to To array
+ {
+ $to_array[$to_array.length] = $cur;
+ }
+ else //Else - keep in new From
+ {
+ $new_from[$new_from.length] = $cur;
+ }
+ }
+
+ $from_list = array_to_select($new_from, $from_list);
+ $to_list = array_to_select($to_array, $to_list);
+ }
+ else
+ {
+ alert('Please select items to perform moving!');
+ }
+ }
+
+ function select_to_array($aSelect)
+ {
+ var $an_array = new Array();
+ var $cur = null;
+
+ for (var $i = 0; $i < $aSelect.length; $i++)
+ {
+ $cur = $aSelect.options[$i];
+ $an_array[$an_array.length] = new Array($cur.text, $cur.value, $cur.selected);
+ }
+ return $an_array;
+ }
+
+ function array_to_select($anArray, $aSelect)
+ {
+ var $initial_length = $aSelect.length;
+ for (var $i = $initial_length - 1; $i >= 0; $i--)
+ {
+ $aSelect.options[$i] = null;
+ }
+
+ for (var $i = 0; $i < $anArray.length; $i++)
+ {
+ $cur = $anArray[$i];
+ $aSelect.options[$aSelect.length] = new Option($cur[0], $cur[1]);
+ }
+ }
+
+ function select_compare($a, $b)
+ {
+ if ($a[0] < $b[0])
+ return -1;
+ if ($a[0] > $b[0])
+ return 1;
+ return 0;
+ }
+
+ function select_to_string($aSelect)
+ {
+ var $result = '';
+ var $cur = null;
+
+ if (typeof($aSelect) != 'object') $aSelect = document.getElementById($aSelect);
+
+ for (var $i = 0; $i < $aSelect.length; $i++)
+ {
+ $result += $aSelect.options[$i].value + '|';
+ }
+
+ return $result.length ? '|' + $result : '';
+ }
+
+ function selected_to_string($aSelect)
+ {
+ var $result = '';
+ var $cur = null;
+
+ if (typeof($aSelect) != 'object') $aSelect = document.getElementById($aSelect);
+
+ for (var $i = 0; $i < $aSelect.length; $i++)
+ {
+ $cur = $aSelect.options[$i];
+ if ($cur.selected && $cur.value != '')
+ {
+ $result += $cur.value + '|';
+ }
+ }
+
+ return $result.length ? '|' + $result : '';
+ }
+
+ function string_to_selected($str, $aSelect)
+ {
+ var $cur = null;
+ for (var $i = 0; i < $aSelect.length; $i++)
+ {
+ $cur = $aSelect.options[$i];
+ $aSelect.options[$i].selected = $str.match(',' + $cur.value + ',') ? true : false;
+ }
+ }
+
+ function set_selected($selected_options, $aSelect)
+ {
+ if (!$selected_options.length) return false;
+
+ for (var $i = 0; $i < $aSelect.length; $i++)
+ {
+ for (var $k = 0; $k < $selected_options.length; $k++)
+ {
+ if ($aSelect.options[$i].value == $selected_options[$k])
+ {
+ $aSelect.options[$i].selected = true;
+ }
+ }
+ }
+ }
+
+ function get_selected_count($theList)
+ {
+ var $count = 0;
+ var $cur = null;
+ for (var $i = 0; $i < $theList.length; $i++)
+ {
+ $cur = $theList.options[$i];
+ if ($cur.selected) $count++;
+ }
+ return $count;
+ }
+
+ function get_selected_index($aSelect, $typeIndex)
+ {
+ var $index = 0;
+ for (var $i = 0; $i < $aSelect.length; $i++)
+ {
+ if ($aSelect.options[$i].selected)
+ {
+ $index = $i;
+ if ($typeIndex == 'firstSelected') break;
+ }
+ }
+ return $index;
+ }
+
+ function has_selected_options($theList)
+ {
+ var $ret = false;
+ var $cur = null;
+
+ for (var $i = 0; $i < $theList.length; $i++)
+ {
+ $cur = $theList.options[$i];
+ if ($cur.selected) $ret = true;
+ }
+ return $ret;
+ }
+
+ function select_sort($aSelect)
+ {
+ if (typeof($aSelect) != 'object') $aSelect = document.getElementById($aSelect);
+
+ var $to_array = select_to_array($aSelect);
+ $to_array.sort(select_compare);
+ array_to_select($to_array, $aSelect);
+ }
+
+ function move_options_up($aSelect, $interval)
+ {
+ if (typeof($aSelect) != 'object') $aSelect = document.getElementById($aSelect);
+
+ if (has_selected_options($aSelect))
+ {
+ var $selected_options = Array();
+ var $first_selected = get_selected_index($aSelect, 'firstSelected');
+
+ for (var $i = 0; $i < $aSelect.length; $i++)
+ {
+ if ($aSelect.options[$i].selected && ($first_selected > 0) )
+ {
+ swap_options($aSelect, $i, $i - $interval);
+ $selected_options[$selected_options.length] = $aSelect.options[$i - $interval].value;
+ }
+ else if ($first_selected == 0)
+ {
+ alert('Begin of list');
+ break;
+ }
+ }
+ set_selected($selected_options, $aSelect);
+ }
+ else
+ {
+ alert('Check items from moving');
+ }
+ }
+
+ function move_options_down($aSelect, $interval)
+ {
+ if (typeof($aSelect) != 'object') $aSelect = document.getElementById($aSelect);
+
+ if (has_selected_options($aSelect))
+ {
+ var $last_selected = get_selected_index($aSelect, 'lastSelected');
+ var $selected_options = Array();
+
+ for (var $i = $aSelect.length - 1; $i >= 0; $i--)
+ {
+ if ($aSelect.options[$i].selected && ($aSelect.length - ($last_selected + 1) > 0))
+ {
+ swap_options($aSelect, $i, $i + $interval);
+ $selected_options[$selected_options.length] = $aSelect.options[$i + $interval].value;
+ }
+ else if ($last_selected + 1 == $aSelect.length)
+ {
+ alert('End of list');
+ break;
+ }
+ }
+ set_selected($selected_options, $aSelect);
+ }
+ else
+ {
+ alert('Check items from moving');
+ }
+ }
+
+ function swap_options($aSelect, $src_num, $dst_num)
+ {
+ var $src_option = new Option($aSelect.options[$src_num].innerHTML, $aSelect.options[$src_num].value);
+ var $dst_option = new Option($aSelect.options[$dst_num].innerHTML, $aSelect.options[$dst_num].value);
+
+ $aSelect.options[$src_num] = $dst_option;
+ $aSelect.options[$dst_num] = $src_option;
+ }
\ No newline at end of file
Index: trunk/core/kernel/db/dbitem.php
===================================================================
diff -u -N -r3282 -r3543
--- trunk/core/kernel/db/dbitem.php (.../dbitem.php) (revision 3282)
+++ trunk/core/kernel/db/dbitem.php (.../dbitem.php) (revision 3543)
@@ -135,12 +135,14 @@
*/
function SetError($field, $pseudo, $error_label = '')
{
- $error_msg = $this->Application->Phrase($error_label);
- if( !($error_msg && getArrayValue($this->ErrorMsgs,$pseudo)) )
+ $error_field = isset($this->Fields[$field]['error_field']) ? $this->Fields[$field]['error_field'] : $field;
+ $this->FieldErrors[$error_field]['pseudo'] = $pseudo;
+
+ $error_msg = $error_label ? $this->Application->Phrase($error_label) : '';
+ if ($error_label && !getArrayValue($this->ErrorMsgs, $pseudo))
{
$this->ErrorMsgs[$pseudo] = $error_msg;
}
- $this->FieldErrors[$field]['pseudo'] = $pseudo;
}
/**
Index: trunk/admin/include/mainscript.js
===================================================================
diff -u -N -r3145 -r3543
--- trunk/admin/include/mainscript.js (.../mainscript.js) (revision 3145)
+++ trunk/admin/include/mainscript.js (.../mainscript.js) (revision 3543)
@@ -153,6 +153,31 @@
if(\$window.parent) \$window = \$window.parent;
\$window.document.title = (main_title.length ? main_title + ' - ' : '') + \$title;
}
+
+// sets hidden field value
+// if the field does not exist - creates it
+function set_hidden_field(\$field_id, \$value)
+{
+// alert('form: '+\$form_prefix+'_form');
+
+ var \$kf = document.getElementById(\$form_prefix+'_form');
+ var \$field = \$kf.elements[\$field_id];
+ if(\$field)
+ {
+ \$field.value = \$value;
+ return true;
+ }
+
+ \$field = document.createElement('INPUT');
+ \$field.type = 'hidden';
+ \$field.name = \$field_id;
+ \$field.id = \$field_id;
+ \$field.value = \$value;
+
+ \$kf.appendChild(\$field);
+ return false;
+}
+
var env = "$env2";
Index: trunk/core/kernel/event_manager.php
===================================================================
diff -u -N -r3374 -r3543
--- trunk/core/kernel/event_manager.php (.../event_manager.php) (revision 3374)
+++ trunk/core/kernel/event_manager.php (.../event_manager.php) (revision 3543)
@@ -23,7 +23,7 @@
*
* @var Array
*/
- var $queryMaps=Array();
+ var $queryMaps = Array();
/**
* Build events registred for
@@ -85,9 +85,9 @@
*/
function setQueryMaps($new_query_maps)
{
- $this->queryMaps=$new_query_maps;
+ $this->queryMaps = $new_query_maps;
}
-
+
/**
* Registers new regular event
*
Index: trunk/kernel/module_help/visits_list.txt
===================================================================
diff -u -N -r2976 -r3543
--- trunk/kernel/module_help/visits_list.txt (.../visits_list.txt) (revision 2976)
+++ trunk/kernel/module_help/visits_list.txt (.../visits_list.txt) (revision 3543)
@@ -1,24 +1,24 @@
-Visits (In-portal Platform)
-
-This section displays the log of all visits to your website. The visit is recorded when a user comes to your site. If the user logs in during his or her visit, the username will be updated in the visit record. If the user’s session expires during the visit, a new visit record will be created when he or she comes back to the site.
-The grid on this page displays the following columns:
-
-
Visit Date – Date and Time of the visit’s start
-
IP Address – the IP address of the visitor, as identified by the web server
-
Referrer – the URL the visitor came from, as reported by visitor’s browser
-
Username – the username of the visitor if he or she logs in during the visit
-
-
-Visits (with In-commerce installed)
-
-This section displays the log of all visits to your website. The visit is recorded when user comes to your site. If the user logs in during his or her visit, the username will be updated in the visit record. If the user’s session expires during the visit, a new visit record will be created when he or she comes back to the site.
-The grid on this page displays the following columns:
-
-
Visit Date – Date and Time of the visit’s start
-
IP Address – the IP address of the visitor, as identified by the web server
-
Referrer – the URL the visitor came from, as reported by visitor’s browser
-
Username – the username of the visitor if he or she logs in during the visit
-
Affiliate User – the username of affiliate who has referred the visitor
-
Order Total – the total amount of the orders made during this visit
-
Affiliate Commission – amount of affiliate commission received for the orders made during the visit
+Visits (In-portal Platform)
+
+This section displays the log of all visits to your website. The visit is recorded when a user comes to your site. If the user logs in during his or her visit, the username will be updated in the visit record. If the user’s session expires during the visit, a new visit record will be created when he or she comes back to the site.
+The grid on this page displays the following columns:
+
+
Visit Date – Date and Time of the visit’s start
+
IP Address – the IP address of the visitor, as identified by the web server
+
Referrer – the URL the visitor came from, as reported by visitor’s browser
+
Username – the username of the visitor if he or she logs in during the visit
+
+
+Visits (with In-commerce installed)
+
+This section displays the log of all visits to your website. The visit is recorded when user comes to your site. If the user logs in during his or her visit, the username will be updated in the visit record. If the user’s session expires during the visit, a new visit record will be created when he or she comes back to the site.
+The grid on this page displays the following columns:
+
+
Visit Date – Date and Time of the visit’s start
+
IP Address – the IP address of the visitor, as identified by the web server
+
Referrer – the URL the visitor came from, as reported by visitor’s browser
+
Username – the username of the visitor if he or she logs in during the visit
+
Affiliate User – the username of affiliate who has referred the visitor
+
Order Total – the total amount of the orders made during this visit
+
Affiliate Commission – amount of affiliate commission received for the orders made during the visit
\ No newline at end of file
Index: trunk/kernel/units/general/cat_dbitem_export.php
===================================================================
diff -u -N
--- trunk/kernel/units/general/cat_dbitem_export.php (revision 0)
+++ trunk/kernel/units/general/cat_dbitem_export.php (revision 3543)
@@ -0,0 +1,848 @@
+cache, $type, $key);
+ }
+
+ /**
+ * Adds value to be cached
+ *
+ * @param string $type
+ * @param int $key
+ * @param mixed $value
+ */
+ function addToCache($type, $key, $value)
+ {
+// if (!isset($this->cache[$type])) $this->cache[$type] = Array();
+ $this->cache[$type][$key] = $value;
+ }
+
+ /**
+ * Fill required fields with dummy values
+ *
+ * @param kEvent $event
+ */
+ function fillRequiredFields(&$event)
+ {
+ $object =& $event->getObject();
+
+ $fields = array_keys($object->Fields);
+ foreach ($fields as $field_name)
+ {
+ $field_options =& $object->Fields[$field_name];
+ if (isset($object->VirtualFields[$field_name]) || !getArrayValue($field_options, 'required') ) continue;
+
+ $formatter_class = getArrayValue($field_options, 'formatter');
+ if ($formatter_class) // not tested
+ {
+ $formatter =& $this->Application->recallObject($formatter_class);
+ $sample_value = $formatter->GetSample($field_name, $field_options, $object);
+ }
+ $object->SetDBField($field_name, isset($sample_value) && $sample_value ? $sample_value : 'dummy');
+ }
+ }
+
+ /**
+ * Verifies that all user entered export params are correct
+ *
+ * @param kEvent $event
+ */
+ function verifyOptions(&$event)
+ {
+ if ($this->Application->RecallVar($event->getPrefixSpecial().'_ForceNotValid'))
+ {
+ $this->Application->StoreVar($event->getPrefixSpecial().'_ForceNotValid', 0);
+ return false;
+ }
+
+ $this->fillRequiredFields($event);
+
+ $object =& $event->getObject();
+ $cross_unique_fields = Array('FieldsSeparatedBy', 'FieldsEnclosedBy');
+ if (($object->GetDBField('CategoryFormat') == 1) || ($event->Special == 'import')) // in one field
+ {
+ $object->setRequired('CategorySeparator', true);
+ $cross_unique_fields[] = 'CategorySeparator';
+ }
+
+ $ret = $object->Validate();
+
+ // check if cross unique fields has no same values
+ foreach ($cross_unique_fields as $field_index => $field_name)
+ {
+ if (getArrayValue($object->FieldErrors, $field_name, 'pseudo') == 'required') continue;
+
+ $check_fields = $cross_unique_fields;
+ unset($check_fields[$field_index]);
+
+ foreach ($check_fields as $check_field)
+ {
+ if ($object->GetDBField($field_name) == $object->GetDBField($check_field))
+ {
+ $object->SetError($check_field, 'unique');
+ }
+ }
+ }
+
+ if ($event->Special == 'import')
+ {
+ $this->exportOptions = unserialize($this->Application->RecallVar($event->getPrefixSpecial().'_options'));
+
+ $automatic_fields = ($object->GetDBField('FieldTitles') == 1);
+ $object->setRequired('ExportColumns', !$automatic_fields);
+ $category_prefix = '__CATEGORY__';
+ if ( $automatic_fields && ($this->exportOptions['SkipFirstRow']) ) {
+ $this->openFile($event);
+ $this->exportOptions['ExportColumns'] = $this->readRecord();
+ $this->closeFile();
+
+ // remove additional (non-parseble columns)
+ foreach ($this->exportOptions['ExportColumns'] as $field_index => $field_name) {
+ if (!$this->validateField($field_name, $object)) {
+ unset($this->exportOptions['ExportColumns'][$field_index]);
+ }
+ }
+ $category_prefix = '';
+ }
+
+ // 1. check, that we have column definitions
+ if (!$this->exportOptions['ExportColumns']) {
+ $object->setError('ExportColumns', 'required');
+ $ret = false;
+ }
+
+ // 2. check, that we have only mixed category field or only separated category fields
+ $category_found['mixed'] = false;
+ $category_found['separated'] = false;
+
+ foreach ($this->exportOptions['ExportColumns'] as $import_field) {
+ if (preg_match('/^'.$category_prefix.'Category(Path|[0-9]+)/', $import_field, $rets)) {
+ $category_found[$rets[1] == 'Path' ? 'mixed' : 'separated'] = true;
+ }
+ }
+ if ($category_found['mixed'] && $category_found['separated']) {
+ $object->SetError('ExportColumns', 'unique_category', 'la_error_unique_category_field');
+ $ret = false;
+ }
+
+ // 3. check, that duplicates check fields are selected & present in imported fields
+ if ($this->exportOptions['ReplaceDuplicates']) {
+ if ($this->exportOptions['CheckDuplicatesMethod'] == 1) {
+ $check_fields = Array($object->IDField);
+ }
+ else {
+ $check_fields = $this->exportOptions['DuplicateCheckFields'] ? explode('|', substr($this->exportOptions['DuplicateCheckFields'], 1, -1)) : Array();
+ }
+
+ if (!$check_fields) {
+ $object->setError('CheckDuplicatesMethod', 'required');
+ $ret = false;
+ }
+ else {
+ foreach ($check_fields as $check_field) {
+ if (!in_array($check_field, $this->exportOptions['ExportColumns'])) {
+ $object->setError('ExportColumns', 'required');
+ $ret = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Returns filename to read import data from
+ *
+ * @return string
+ */
+ function getImportFilename()
+ {
+ if ($this->exportOptions['ImportSource'] == 1)
+ {
+ $ret = $this->exportOptions['ImportFilename']['name'];
+ }
+ else {
+ $ret = $this->exportOptions['ImportLocalFilename'];
+ }
+ return EXPORT_PATH.'/'.$ret;
+ }
+
+ /**
+ * Returns filename to write export data to
+ *
+ * @return string
+ */
+ function getExportFilename()
+ {
+ return EXPORT_PATH.'/'.$this->exportOptions['ExportFilename'].'.'.$this->getFileExtension();
+ }
+
+ /**
+ * Opens file required for export/import operations
+ *
+ * @param kEvent $event
+ */
+ function openFile(&$event)
+ {
+ if ($event->Special == 'export') {
+ $write_mode = ($this->exportOptions['start_from'] == 0) ? 'w' : 'a';
+ $this->filePointer = fopen($this->getExportFilename(), $write_mode);
+ }
+ else {
+ $this->filePointer = fopen($this->getImportFilename(), 'r');
+ }
+ }
+
+ /**
+ * Closes opened file
+ *
+ */
+ function closeFile()
+ {
+ fclose($this->filePointer);
+ }
+
+ function getExportSQL($count_only = false)
+ {
+ if ($this->exportOptions['export_ids'] === false)
+ {
+ // get links from current category & all it's subcategories
+ $sql = 'SELECT item_table.*, ci.CategoryId
+ FROM '.$this->curItem->TableName.' item_table
+ LEFT JOIN '.TABLE_PREFIX.'CategoryItems ci ON ci.ItemResourceId = item_table.ResourceId
+ LEFT JOIN '.TABLE_PREFIX.'Category c ON c.CategoryId = ci.CategoryId
+ WHERE ';
+
+ if ($this->exportOptions['export_cats_ids'][0] == 0)
+ {
+ $sql .= '1';
+ }
+ else {
+ foreach ($this->exportOptions['export_cats_ids'] as $category_id) {
+ $sql .= '(c.ParentPath LIKE "%|'.$category_id.'|%") OR ';
+ }
+ $sql = preg_replace('/(.*) OR $/', '\\1', $sql);
+ }
+
+ $sql .= ' ORDER BY ci.PrimaryCat DESC'; // NEW
+ }
+ else {
+ // get only selected links
+ $sql = 'SELECT item_table.*, '.$this->exportOptions['export_cats_ids'][0].' AS CategoryId
+ FROM '.$this->curItem->TableName.' item_table
+ WHERE '.$this->curItem->IDField.' IN ('.implode(',', $this->exportOptions['export_ids']).')';
+ }
+
+ if (!$count_only)
+ {
+ $sql .= ' LIMIT '.$this->exportOptions['start_from'].','.EXPORT_STEP;
+ }
+ else {
+ $sql = preg_replace("/^.*SELECT(.*?)FROM(?!_)/is", "SELECT COUNT(*) AS count FROM ", $sql);
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @param kEvent $event
+ */
+ function performExport(&$event)
+ {
+ $this->exportOptions = unserialize($this->Application->RecallVar($event->getPrefixSpecial().'_options'));
+ $this->exportFields = $this->exportOptions['ExportColumns'];
+ $this->curItem =& $event->getObject( Array('skip_autoload' => true) );
+
+ $this->openFile($event);
+
+ if ($this->exportOptions['start_from'] == 0) // first export step
+ {
+ if ($this->exportOptions['IsBaseCategory'] ) {
+ $sql = 'SELECT CachedNavbar
+ FROM '.TABLE_PREFIX.'Category
+ WHERE CategoryId = '.$this->Application->GetVar('m_cat_id');
+ $this->exportOptions['BaseLevel'] = substr_count($this->Conn->GetOne($sql), '>') + 1; // level to cut from other categories
+ }
+
+ // 1. export field titles if required
+ if ($this->exportOptions['IncludeFieldTitles'])
+ {
+ $data_array = Array();
+ foreach ($this->exportFields as $export_field)
+ {
+ $data_array = array_merge($data_array, $this->getFieldCaption($export_field));
+ }
+ $this->writeRecord($data_array);
+ }
+ $this->exportOptions['total_records'] = $this->Conn->GetOne( $this->getExportSQL(true) );
+ $this->exportOptions['has_custom_fields'] = $this->scanCustomFields();
+ }
+
+ $this->hasCustomFields = $this->exportOptions['has_custom_fields'];
+
+ // 2. export data
+ $records = $this->Conn->Query( $this->getExportSQL() );
+ $records_exported = 0;
+ foreach ($records as $record_info) {
+ $this->curItem->SetDBFieldsFromHash($record_info);
+ if ($this->hasCustomFields)
+ {
+ $this->loadItemCustomFields();
+ }
+
+ $data_array = Array();
+ foreach ($this->exportFields as $export_field)
+ {
+ $data_array = array_merge($data_array, $this->getFieldValue($export_field) );
+ }
+ $this->writeRecord($data_array);
+ $records_exported++;
+ }
+ $this->closeFile();
+
+ $this->exportOptions['start_from'] += $records_exported;
+ $this->Application->StoreVar($event->getPrefixSpecial().'_options', serialize($this->exportOptions) );
+
+ return $this->exportOptions;
+ }
+
+ function getItemFields()
+ {
+ // just in case dummy user selected automtic mode & moved columns too :(
+ return array_merge($this->curItem->Fields['AvailableColumns']['options'], $this->curItem->Fields['ExportColumns']['options']);
+ }
+
+ /**
+ * Checks if field really belongs to importable field list
+ *
+ * @param string $field_name
+ * @param kCatDBItem $object
+ * @return bool
+ */
+ function validateField($field_name, &$object)
+ {
+ // 1. convert custom field
+ $field_name = preg_replace('/^Custom_(.*)/', '__CUSTOM__\\1', $field_name);
+
+ // 2. convert category field (mixed version & serparated version)
+ $field_name = preg_replace('/^Category(Path|[0-9]+)/', '__CATEGORY__Category\\1', $field_name);
+
+ $valid_fields = $object->getPossibleExportColumns();
+ return isset($valid_fields[$field_name]);
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @param kEvent $event
+ */
+ function performImport(&$event)
+ {
+ if (!$this->exportOptions) {
+ // load import options in case if not previously loaded in verification function
+ $this->exportOptions = unserialize($this->Application->RecallVar($event->getPrefixSpecial().'_options'));
+ }
+
+ $this->curItem =& $event->getObject( Array('skip_autoload' => true) );
+
+ $backup_category_id = $this->Application->GetVar('m_cat_id');
+ $this->Application->SetVar('m_cat_id', (int)$this->Application->RecallVar('ImportCategory') );
+
+ $this->openFile($event);
+
+ $bytes_imported = 0;
+ if ($this->exportOptions['start_from'] == 0) // first export step
+ {
+ // 1st time run
+ if ($this->exportOptions['SkipFirstRow']) {
+ $this->readRecord();
+ $this->exportOptions['start_from'] = ftell($this->filePointer);
+ $bytes_imported = ftell($this->filePointer);
+ }
+
+ $current_category_id = $this->Application->GetVar('m_cat_id');
+ if ($current_category_id > 0) {
+ $sql = 'SELECT ParentPath FROM '.TABLE_PREFIX.'Category WHERE CategoryId = '.$current_category_id;
+ $this->exportOptions['ImportCategoryPath'] = $this->Conn->GetOne($sql);
+ }
+ else {
+ $this->exportOptions['ImportCategoryPath'] = '';
+ }
+ $this->exportOptions['total_records'] = filesize($this->getImportFilename());
+ }
+ else {
+ $this->cache['new_ids'] = $this->exportOptions['new_ids_hash'];
+ }
+ $this->exportFields = $this->exportOptions['ExportColumns'];
+ $this->addToCache('category_parent_path', $this->Application->GetVar('m_cat_id'), $this->exportOptions['ImportCategoryPath']);
+
+ // 2. import data
+ $this->dummyCategory =& $this->Application->recallObject('c.-tmpitem', 'c', Array('skip_autoload' => true));
+ fseek($this->filePointer, $this->exportOptions['start_from']);
+
+ while (($bytes_imported < IMPORT_CHUNK) && !feof($this->filePointer)) {
+ $this->customValues = Array();
+ $data = $this->readRecord();
+ if ($data) {
+ foreach ($data as $field_index => $field_value) {
+ $this->setFieldValue($field_index, $field_value);
+ }
+ $this->curItem->setID( $this->curItem->GetDBField($this->curItem->IDField) );
+ $this->processCurrentItem($event);
+ }
+ $bytes_imported = ftell($this->filePointer) - $this->exportOptions['start_from'];
+ }
+
+ $this->closeFile();
+ $this->Application->SetVar('m_cat_id', $backup_category_id);
+
+ $this->exportOptions['start_from'] += $bytes_imported;
+ $this->exportOptions['new_ids_hash'] = getArrayValue($this->cache, 'new_ids');
+ $this->Application->StoreVar($event->getPrefixSpecial().'_options', serialize($this->exportOptions) );
+
+ return $this->exportOptions;
+ }
+
+ function setFieldValue($field_index, $value)
+ {
+ $field_name = $this->exportFields[$field_index];
+
+ if (substr($field_name, 0, 7) == 'Custom_') {
+ $field_name = substr($field_name, 7);
+ $this->customValues[$field_name] = $value;
+ }
+ elseif ($field_name == 'CategoryPath') {
+ $this->curItem->CategoryPath = explode($this->exportOptions['CategorySeparator'], $value);
+ }
+ elseif (substr($field_name, 0, 8) == 'Category') {
+ $this->curItem->CategoryPath[ (int)substr($field_name, 8) ] = $value;
+ }
+ else {
+ $this->curItem->SetField($field_name, $value);
+ }
+ }
+
+ /**
+ * Returns temporary items for import procedures
+ *
+ * @param kEvent $event
+ * @return kCatDBItem
+ */
+ function &getTempItem(&$event)
+ {
+ return $this->Application->recallObject($event->Prefix.'.-tmpitem', $event->Prefix, Array('skip_autoload' => true));
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @param kEvent $event
+ */
+ function processCurrentItem(&$event)
+ {
+ $tmp_item =& $this->getTempItem($event);
+
+ // create/update categories
+ $backup_category_id = $this->Application->GetVar('m_cat_id');
+
+ foreach ($this->curItem->CategoryPath as $category_name) {
+ if (!$category_name) continue;
+ $category_id = $this->getFromCache('category_names', $category_name);
+ if ($category_id === false) {
+ // get parent category path to search only in it
+ $current_category_id = $this->Application->GetVar('m_cat_id');
+ $parent_path = $this->getParentPath($current_category_id);
+
+ // get category id from database by name
+ $sql = 'SELECT CategoryId
+ FROM '.TABLE_PREFIX.'Category
+ WHERE (Name = '.$this->Conn->qstr($category_name).') AND (ParentPath LIKE "'.$parent_path.'%")';
+ $category_id = $this->Conn->GetOne($sql);
+
+ if ($category_id === false) {
+ // category not in db -> create
+ $category_fields = Array( 'Name' => $category_name, 'Description' => $category_name,
+ 'Status' => STATUS_ACTIVE, 'ParentId' => $current_category_id,
+ );
+ $this->dummyCategory->SetDBFieldsFromHash($category_fields);
+ if ($this->dummyCategory->Create()) {
+ $category_id = $this->dummyCategory->GetID();
+ $this->addToCache('category_parent_path', $category_id, $this->dummyCategory->GetDBField('ParentPath'));
+ $this->addToCache('category_names', $category_name, $category_id);
+ }
+ }
+ else {
+ $this->addToCache('category_names', $category_name, $category_id);
+ }
+ }
+
+ if ($category_id) {
+ $this->Application->SetVar('m_cat_id', $category_id);
+ }
+ }
+
+ // create main record
+ $save_method = 'Create';
+ if ($this->exportOptions['ReplaceDuplicates']) {
+ if ($this->exportOptions['CheckDuplicatesMethod'] == 1) {
+ $load_keys = Array($this->curItem->IDField => $this->curItem->GetID());
+ }
+ else {
+ $key_fields = explode('|', substr($this->exportOptions['DuplicateCheckFields']) );
+ foreach ($key_fields as $key_field) {
+ $load_keys[$key_field] = $this->curItem->GetDBField($key_field);
+ }
+ }
+
+ $where_clause = '';
+ foreach ($load_keys as $field_name => $field_value) {
+ $where_clause .= '(item_table.`'.$field_name.'` = '.$this->Conn->qstr($field_value).') AND ';
+ }
+ $where_clause = preg_replace('/(.*) AND $/', '\\1', $where_clause);
+
+ $item_id = $this->getFromCache('new_ids', $where_clause);
+ if (!$item_id) {
+ $parent_path = $this->getParentPath($category_id);
+ $sql = 'SELECT '.$this->curItem->IDField.'
+ FROM '.$this->curItem->TableName.' item_table
+ LEFT JOIN '.TABLE_PREFIX.'CategoryItems ci ON ci.ItemResourceId = item_table.ResourceId
+ LEFT JOIN '.TABLE_PREFIX.'Category c ON c.CategoryId = ci.CategoryId
+ WHERE (c.ParentPath LIKE "'.$parent_path.'%") AND '.$where_clause;
+ $item_id = $this->Conn->GetOne($sql);
+ }
+ $save_method = $tmp_item->Load($item_id) ? 'Update' : 'Create';
+ }
+
+ $resource_id = $tmp_item->isLoaded() ? $tmp_item->GetDBField('ResourceId') : 0;
+ $tmp_item->SetDBFieldsFromHash($this->curItem->FieldValues);
+ if( ($save_method == 'Update') && $resource_id ) {
+ $tmp_item->SetDBField('ResourceId', $resource_id);
+ }
+
+ if (!$tmp_item->$save_method()) return false;
+
+ if ( ($save_method == 'Create') && $this->exportOptions['ReplaceDuplicates'] ) {
+ // map new id to old id
+ $this->addToCache('new_ids', $where_clause, $tmp_item->GetID() );
+ }
+
+ // set custom fields
+ foreach ($this->customValues as $custom_field => $custom_value) {
+ if (($save_method == 'Create') && !$custom_value) continue;
+ $tmp_item->SetCustomField($custom_field, $custom_value);
+ }
+
+ // assign item to categories
+ $tmp_item->assignToCategory($category_id, false);
+
+ $this->Application->SetVar('m_cat_id', $backup_category_id);
+ return true;
+ }
+
+ /**
+ * Returns category parent path, if possible, then from cache
+ *
+ * @param int $category_id
+ * @return string
+ */
+ function getParentPath($category_id)
+ {
+ $parent_path = $this->getFromCache('category_parent_path', $category_id);
+ if ($parent_path === false) {
+ $sql = 'SELECT ParentPath
+ FROM '.TABLE_PREFIX.'Category
+ WHERE CategoryId = '.$category_id;
+ $parent_path = $this->Conn->GetOne($sql);
+ $this->addToCache('category_parent_path', $category_id, $parent_path);
+ }
+ return $parent_path;
+ }
+
+ function loadItemCustomFields()
+ {
+ $sql = 'SELECT meta_data.Value, cf.FieldName
+ FROM '.TABLE_PREFIX.'CustomMetaData meta_data
+ LEFT JOIN '.TABLE_PREFIX.'CustomField cf ON cf.CustomFieldId = meta_data.CustomFieldId
+ WHERE meta_data.ResourceId = '.$this->curItem->GetDBField('ResourceId');
+ $this->customValues = $this->Conn->GetCol($sql, 'FieldName');
+ }
+
+ function getFileExtension()
+ {
+ return $this->exportOptions['ExportFormat'] == 1 ? 'csv' : 'xml';
+ }
+
+ function getLineSeparator($option = 'LineEndings')
+ {
+ return $this->exportOptions[$option] == 1 ? "\r\n" : "\n";
+ }
+
+ function scanCustomFields()
+ {
+ $ret = false;
+ $export_fields = $this->exportOptions['ExportColumns'];
+ foreach ($export_fields as $field)
+ {
+ if (substr($field, 0, 10) == '__CUSTOM__')
+ {
+ $ret = true;
+ break;
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Returns field caption for any exported field
+ *
+ * @param string $field
+ * @return string
+ */
+ function getFieldCaption($field)
+ {
+ if (substr($field, 0, 10) == '__CUSTOM__')
+ {
+ $ret = 'Custom_'.substr($field, 10, strlen($field) );
+ }
+ elseif (substr($field, 0, 12) == '__CATEGORY__')
+ {
+ return $this->getCategoryTitle();
+ }
+ else
+ {
+ $ret = $field;
+ }
+
+ return Array($ret);
+ }
+
+ /**
+ * Returns requested field value (including custom fields and category fields)
+ *
+ * @param string $field
+ * @return string
+ */
+ function getFieldValue($field)
+ {
+ if (substr($field, 0, 10) == '__CUSTOM__')
+ {
+ $field = substr($field, 10, strlen($field) );
+ $ret = isset($this->customValues[$field]) ? $this->customValues[$field] : '';
+ }
+ elseif (substr($field, 0, 12) == '__CATEGORY__')
+ {
+ return $this->getCategoryPath();
+ }
+ else
+ {
+ $ret = $this->curItem->GetField($field);
+ }
+
+ $ret = str_replace("\r\n", $this->getLineSeparator('LineEndingsInside'), $ret);
+ return Array($ret);
+ }
+
+ /**
+ * Returns category field(-s) caption based on export mode
+ *
+ * @return string
+ */
+ function getCategoryTitle()
+ {
+ if ($this->exportOptions['CategoryFormat'] == 1)
+ {
+ // category path in one field
+ return Array('CategoryPath');
+ }
+ else
+ {
+ // category path in separated fields
+ $category_count = $this->getMaxCategoryLevel();
+
+ $i = 0;
+ $ret = Array();
+ while ($i < $category_count) {
+ $ret[] = 'Category'.($i + 1);
+ $i++;
+ }
+ return $ret;
+ }
+ }
+
+ /**
+ * Returns category path in required format for current link
+ *
+ * @return string
+ */
+ function getCategoryPath()
+ {
+ $category_id = $this->curItem->GetDBField('CategoryId');
+ $category_path = $this->getFromCache('category_path', $category_id);
+ if (!$category_path)
+ {
+ $sql = 'SELECT CachedNavbar
+ FROM '.TABLE_PREFIX.'Category
+ WHERE CategoryId = '.$category_id;
+ $category_path = $this->Conn->GetOne($sql);
+ $category_path = explode('>', $category_path);
+
+ if ($this->exportOptions['IsBaseCategory'] ) {
+ $i = $this->exportOptions['BaseLevel'];
+ while ($i > 0) {
+ array_shift($category_path);
+ $i--;
+ }
+ }
+
+ if ($this->exportOptions['CategoryFormat'] == 1) {
+ // category path in single field
+ $category_path = Array( implode($this->exportOptions['CategorySeparator'], $category_path) );
+ }
+ else {
+ // category path in separated fields
+ $category_count = $this->getMaxCategoryLevel();
+ $levels_used = count($category_path);
+ if ($levels_used < $category_count)
+ {
+ $i = 0;
+ while ($i < $category_count - $levels_used) {
+ $category_path[] = '';
+ $i++;
+ }
+ }
+ }
+ $this->addToCache('category_path', $category_id, $category_path);
+ }
+
+ return $category_path;
+ }
+
+ /**
+ * Get maximal category deep level from links beeing exported
+ *
+ * @return int
+ */
+ function getMaxCategoryLevel()
+ {
+ static $max_level = -1;
+
+ if ($max_level != -1)
+ {
+ return $max_level;
+ }
+
+ $sql = 'SELECT IF(c.CategoryId IS NULL, 0, MAX( LENGTH(c.ParentPath) - LENGTH( REPLACE(c.ParentPath, "|", "") ) - 1 ))
+ FROM '.$this->curItem->TableName.' item_table
+ LEFT JOIN '.TABLE_PREFIX.'CategoryItems ci ON item_table.ResourceId = ci.ItemResourceId
+ LEFT JOIN '.TABLE_PREFIX.'Category c ON c.CategoryId = ci.CategoryId
+ WHERE (ci.PrimaryCat = 1) AND ';
+
+ $where_clause = '';
+ if ($this->exportOptions['export_ids'] === false) {
+ // get links from current category & all it's subcategories
+ if ($this->exportOptions['export_cats_ids'][0] == 0) {
+ $where_clause = 1;
+ }
+ else {
+ foreach ($this->exportOptions['export_cats_ids'] as $category_id) {
+ $where_clause .= '(c.ParentPath LIKE "%|'.$category_id.'|%") OR ';
+ }
+ $where_clause = preg_replace('/(.*) OR $/', '\\1', $where_clause);
+ }
+ }
+ else {
+ // get only selected links
+ $where_clause = $this->curItem->IDField.' IN ('.implode(',', $this->exportOptions['export_ids']).')';
+ }
+
+ $max_level = $this->Conn->GetOne($sql.'('.$where_clause.')');
+
+ if ($this->exportOptions['IsBaseCategory'] ) {
+ $max_level -= $this->exportOptions['BaseLevel'];
+ }
+
+ return $max_level;
+ }
+
+ /**
+ * Saves one record to export file
+ *
+ * @param Array $fields_hash
+ */
+ function writeRecord($fields_hash)
+ {
+ fputcsv2($this->filePointer, $fields_hash, $this->exportOptions['FieldsSeparatedBy'], $this->exportOptions['FieldsEnclosedBy'], $this->getLineSeparator() );
+ }
+
+ function readRecord()
+ {
+ return fgetcsv($this->filePointer, 10000, $this->exportOptions['FieldsSeparatedBy'], $this->exportOptions['FieldsEnclosedBy']);
+ }
+ }
+
+?>
Index: trunk/core/units/general/cat_dbitem_export.php
===================================================================
diff -u -N
--- trunk/core/units/general/cat_dbitem_export.php (revision 0)
+++ trunk/core/units/general/cat_dbitem_export.php (revision 3543)
@@ -0,0 +1,848 @@
+cache, $type, $key);
+ }
+
+ /**
+ * Adds value to be cached
+ *
+ * @param string $type
+ * @param int $key
+ * @param mixed $value
+ */
+ function addToCache($type, $key, $value)
+ {
+// if (!isset($this->cache[$type])) $this->cache[$type] = Array();
+ $this->cache[$type][$key] = $value;
+ }
+
+ /**
+ * Fill required fields with dummy values
+ *
+ * @param kEvent $event
+ */
+ function fillRequiredFields(&$event)
+ {
+ $object =& $event->getObject();
+
+ $fields = array_keys($object->Fields);
+ foreach ($fields as $field_name)
+ {
+ $field_options =& $object->Fields[$field_name];
+ if (isset($object->VirtualFields[$field_name]) || !getArrayValue($field_options, 'required') ) continue;
+
+ $formatter_class = getArrayValue($field_options, 'formatter');
+ if ($formatter_class) // not tested
+ {
+ $formatter =& $this->Application->recallObject($formatter_class);
+ $sample_value = $formatter->GetSample($field_name, $field_options, $object);
+ }
+ $object->SetDBField($field_name, isset($sample_value) && $sample_value ? $sample_value : 'dummy');
+ }
+ }
+
+ /**
+ * Verifies that all user entered export params are correct
+ *
+ * @param kEvent $event
+ */
+ function verifyOptions(&$event)
+ {
+ if ($this->Application->RecallVar($event->getPrefixSpecial().'_ForceNotValid'))
+ {
+ $this->Application->StoreVar($event->getPrefixSpecial().'_ForceNotValid', 0);
+ return false;
+ }
+
+ $this->fillRequiredFields($event);
+
+ $object =& $event->getObject();
+ $cross_unique_fields = Array('FieldsSeparatedBy', 'FieldsEnclosedBy');
+ if (($object->GetDBField('CategoryFormat') == 1) || ($event->Special == 'import')) // in one field
+ {
+ $object->setRequired('CategorySeparator', true);
+ $cross_unique_fields[] = 'CategorySeparator';
+ }
+
+ $ret = $object->Validate();
+
+ // check if cross unique fields has no same values
+ foreach ($cross_unique_fields as $field_index => $field_name)
+ {
+ if (getArrayValue($object->FieldErrors, $field_name, 'pseudo') == 'required') continue;
+
+ $check_fields = $cross_unique_fields;
+ unset($check_fields[$field_index]);
+
+ foreach ($check_fields as $check_field)
+ {
+ if ($object->GetDBField($field_name) == $object->GetDBField($check_field))
+ {
+ $object->SetError($check_field, 'unique');
+ }
+ }
+ }
+
+ if ($event->Special == 'import')
+ {
+ $this->exportOptions = unserialize($this->Application->RecallVar($event->getPrefixSpecial().'_options'));
+
+ $automatic_fields = ($object->GetDBField('FieldTitles') == 1);
+ $object->setRequired('ExportColumns', !$automatic_fields);
+ $category_prefix = '__CATEGORY__';
+ if ( $automatic_fields && ($this->exportOptions['SkipFirstRow']) ) {
+ $this->openFile($event);
+ $this->exportOptions['ExportColumns'] = $this->readRecord();
+ $this->closeFile();
+
+ // remove additional (non-parseble columns)
+ foreach ($this->exportOptions['ExportColumns'] as $field_index => $field_name) {
+ if (!$this->validateField($field_name, $object)) {
+ unset($this->exportOptions['ExportColumns'][$field_index]);
+ }
+ }
+ $category_prefix = '';
+ }
+
+ // 1. check, that we have column definitions
+ if (!$this->exportOptions['ExportColumns']) {
+ $object->setError('ExportColumns', 'required');
+ $ret = false;
+ }
+
+ // 2. check, that we have only mixed category field or only separated category fields
+ $category_found['mixed'] = false;
+ $category_found['separated'] = false;
+
+ foreach ($this->exportOptions['ExportColumns'] as $import_field) {
+ if (preg_match('/^'.$category_prefix.'Category(Path|[0-9]+)/', $import_field, $rets)) {
+ $category_found[$rets[1] == 'Path' ? 'mixed' : 'separated'] = true;
+ }
+ }
+ if ($category_found['mixed'] && $category_found['separated']) {
+ $object->SetError('ExportColumns', 'unique_category', 'la_error_unique_category_field');
+ $ret = false;
+ }
+
+ // 3. check, that duplicates check fields are selected & present in imported fields
+ if ($this->exportOptions['ReplaceDuplicates']) {
+ if ($this->exportOptions['CheckDuplicatesMethod'] == 1) {
+ $check_fields = Array($object->IDField);
+ }
+ else {
+ $check_fields = $this->exportOptions['DuplicateCheckFields'] ? explode('|', substr($this->exportOptions['DuplicateCheckFields'], 1, -1)) : Array();
+ }
+
+ if (!$check_fields) {
+ $object->setError('CheckDuplicatesMethod', 'required');
+ $ret = false;
+ }
+ else {
+ foreach ($check_fields as $check_field) {
+ if (!in_array($check_field, $this->exportOptions['ExportColumns'])) {
+ $object->setError('ExportColumns', 'required');
+ $ret = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Returns filename to read import data from
+ *
+ * @return string
+ */
+ function getImportFilename()
+ {
+ if ($this->exportOptions['ImportSource'] == 1)
+ {
+ $ret = $this->exportOptions['ImportFilename']['name'];
+ }
+ else {
+ $ret = $this->exportOptions['ImportLocalFilename'];
+ }
+ return EXPORT_PATH.'/'.$ret;
+ }
+
+ /**
+ * Returns filename to write export data to
+ *
+ * @return string
+ */
+ function getExportFilename()
+ {
+ return EXPORT_PATH.'/'.$this->exportOptions['ExportFilename'].'.'.$this->getFileExtension();
+ }
+
+ /**
+ * Opens file required for export/import operations
+ *
+ * @param kEvent $event
+ */
+ function openFile(&$event)
+ {
+ if ($event->Special == 'export') {
+ $write_mode = ($this->exportOptions['start_from'] == 0) ? 'w' : 'a';
+ $this->filePointer = fopen($this->getExportFilename(), $write_mode);
+ }
+ else {
+ $this->filePointer = fopen($this->getImportFilename(), 'r');
+ }
+ }
+
+ /**
+ * Closes opened file
+ *
+ */
+ function closeFile()
+ {
+ fclose($this->filePointer);
+ }
+
+ function getExportSQL($count_only = false)
+ {
+ if ($this->exportOptions['export_ids'] === false)
+ {
+ // get links from current category & all it's subcategories
+ $sql = 'SELECT item_table.*, ci.CategoryId
+ FROM '.$this->curItem->TableName.' item_table
+ LEFT JOIN '.TABLE_PREFIX.'CategoryItems ci ON ci.ItemResourceId = item_table.ResourceId
+ LEFT JOIN '.TABLE_PREFIX.'Category c ON c.CategoryId = ci.CategoryId
+ WHERE ';
+
+ if ($this->exportOptions['export_cats_ids'][0] == 0)
+ {
+ $sql .= '1';
+ }
+ else {
+ foreach ($this->exportOptions['export_cats_ids'] as $category_id) {
+ $sql .= '(c.ParentPath LIKE "%|'.$category_id.'|%") OR ';
+ }
+ $sql = preg_replace('/(.*) OR $/', '\\1', $sql);
+ }
+
+ $sql .= ' ORDER BY ci.PrimaryCat DESC'; // NEW
+ }
+ else {
+ // get only selected links
+ $sql = 'SELECT item_table.*, '.$this->exportOptions['export_cats_ids'][0].' AS CategoryId
+ FROM '.$this->curItem->TableName.' item_table
+ WHERE '.$this->curItem->IDField.' IN ('.implode(',', $this->exportOptions['export_ids']).')';
+ }
+
+ if (!$count_only)
+ {
+ $sql .= ' LIMIT '.$this->exportOptions['start_from'].','.EXPORT_STEP;
+ }
+ else {
+ $sql = preg_replace("/^.*SELECT(.*?)FROM(?!_)/is", "SELECT COUNT(*) AS count FROM ", $sql);
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @param kEvent $event
+ */
+ function performExport(&$event)
+ {
+ $this->exportOptions = unserialize($this->Application->RecallVar($event->getPrefixSpecial().'_options'));
+ $this->exportFields = $this->exportOptions['ExportColumns'];
+ $this->curItem =& $event->getObject( Array('skip_autoload' => true) );
+
+ $this->openFile($event);
+
+ if ($this->exportOptions['start_from'] == 0) // first export step
+ {
+ if ($this->exportOptions['IsBaseCategory'] ) {
+ $sql = 'SELECT CachedNavbar
+ FROM '.TABLE_PREFIX.'Category
+ WHERE CategoryId = '.$this->Application->GetVar('m_cat_id');
+ $this->exportOptions['BaseLevel'] = substr_count($this->Conn->GetOne($sql), '>') + 1; // level to cut from other categories
+ }
+
+ // 1. export field titles if required
+ if ($this->exportOptions['IncludeFieldTitles'])
+ {
+ $data_array = Array();
+ foreach ($this->exportFields as $export_field)
+ {
+ $data_array = array_merge($data_array, $this->getFieldCaption($export_field));
+ }
+ $this->writeRecord($data_array);
+ }
+ $this->exportOptions['total_records'] = $this->Conn->GetOne( $this->getExportSQL(true) );
+ $this->exportOptions['has_custom_fields'] = $this->scanCustomFields();
+ }
+
+ $this->hasCustomFields = $this->exportOptions['has_custom_fields'];
+
+ // 2. export data
+ $records = $this->Conn->Query( $this->getExportSQL() );
+ $records_exported = 0;
+ foreach ($records as $record_info) {
+ $this->curItem->SetDBFieldsFromHash($record_info);
+ if ($this->hasCustomFields)
+ {
+ $this->loadItemCustomFields();
+ }
+
+ $data_array = Array();
+ foreach ($this->exportFields as $export_field)
+ {
+ $data_array = array_merge($data_array, $this->getFieldValue($export_field) );
+ }
+ $this->writeRecord($data_array);
+ $records_exported++;
+ }
+ $this->closeFile();
+
+ $this->exportOptions['start_from'] += $records_exported;
+ $this->Application->StoreVar($event->getPrefixSpecial().'_options', serialize($this->exportOptions) );
+
+ return $this->exportOptions;
+ }
+
+ function getItemFields()
+ {
+ // just in case dummy user selected automtic mode & moved columns too :(
+ return array_merge($this->curItem->Fields['AvailableColumns']['options'], $this->curItem->Fields['ExportColumns']['options']);
+ }
+
+ /**
+ * Checks if field really belongs to importable field list
+ *
+ * @param string $field_name
+ * @param kCatDBItem $object
+ * @return bool
+ */
+ function validateField($field_name, &$object)
+ {
+ // 1. convert custom field
+ $field_name = preg_replace('/^Custom_(.*)/', '__CUSTOM__\\1', $field_name);
+
+ // 2. convert category field (mixed version & serparated version)
+ $field_name = preg_replace('/^Category(Path|[0-9]+)/', '__CATEGORY__Category\\1', $field_name);
+
+ $valid_fields = $object->getPossibleExportColumns();
+ return isset($valid_fields[$field_name]);
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @param kEvent $event
+ */
+ function performImport(&$event)
+ {
+ if (!$this->exportOptions) {
+ // load import options in case if not previously loaded in verification function
+ $this->exportOptions = unserialize($this->Application->RecallVar($event->getPrefixSpecial().'_options'));
+ }
+
+ $this->curItem =& $event->getObject( Array('skip_autoload' => true) );
+
+ $backup_category_id = $this->Application->GetVar('m_cat_id');
+ $this->Application->SetVar('m_cat_id', (int)$this->Application->RecallVar('ImportCategory') );
+
+ $this->openFile($event);
+
+ $bytes_imported = 0;
+ if ($this->exportOptions['start_from'] == 0) // first export step
+ {
+ // 1st time run
+ if ($this->exportOptions['SkipFirstRow']) {
+ $this->readRecord();
+ $this->exportOptions['start_from'] = ftell($this->filePointer);
+ $bytes_imported = ftell($this->filePointer);
+ }
+
+ $current_category_id = $this->Application->GetVar('m_cat_id');
+ if ($current_category_id > 0) {
+ $sql = 'SELECT ParentPath FROM '.TABLE_PREFIX.'Category WHERE CategoryId = '.$current_category_id;
+ $this->exportOptions['ImportCategoryPath'] = $this->Conn->GetOne($sql);
+ }
+ else {
+ $this->exportOptions['ImportCategoryPath'] = '';
+ }
+ $this->exportOptions['total_records'] = filesize($this->getImportFilename());
+ }
+ else {
+ $this->cache['new_ids'] = $this->exportOptions['new_ids_hash'];
+ }
+ $this->exportFields = $this->exportOptions['ExportColumns'];
+ $this->addToCache('category_parent_path', $this->Application->GetVar('m_cat_id'), $this->exportOptions['ImportCategoryPath']);
+
+ // 2. import data
+ $this->dummyCategory =& $this->Application->recallObject('c.-tmpitem', 'c', Array('skip_autoload' => true));
+ fseek($this->filePointer, $this->exportOptions['start_from']);
+
+ while (($bytes_imported < IMPORT_CHUNK) && !feof($this->filePointer)) {
+ $this->customValues = Array();
+ $data = $this->readRecord();
+ if ($data) {
+ foreach ($data as $field_index => $field_value) {
+ $this->setFieldValue($field_index, $field_value);
+ }
+ $this->curItem->setID( $this->curItem->GetDBField($this->curItem->IDField) );
+ $this->processCurrentItem($event);
+ }
+ $bytes_imported = ftell($this->filePointer) - $this->exportOptions['start_from'];
+ }
+
+ $this->closeFile();
+ $this->Application->SetVar('m_cat_id', $backup_category_id);
+
+ $this->exportOptions['start_from'] += $bytes_imported;
+ $this->exportOptions['new_ids_hash'] = getArrayValue($this->cache, 'new_ids');
+ $this->Application->StoreVar($event->getPrefixSpecial().'_options', serialize($this->exportOptions) );
+
+ return $this->exportOptions;
+ }
+
+ function setFieldValue($field_index, $value)
+ {
+ $field_name = $this->exportFields[$field_index];
+
+ if (substr($field_name, 0, 7) == 'Custom_') {
+ $field_name = substr($field_name, 7);
+ $this->customValues[$field_name] = $value;
+ }
+ elseif ($field_name == 'CategoryPath') {
+ $this->curItem->CategoryPath = explode($this->exportOptions['CategorySeparator'], $value);
+ }
+ elseif (substr($field_name, 0, 8) == 'Category') {
+ $this->curItem->CategoryPath[ (int)substr($field_name, 8) ] = $value;
+ }
+ else {
+ $this->curItem->SetField($field_name, $value);
+ }
+ }
+
+ /**
+ * Returns temporary items for import procedures
+ *
+ * @param kEvent $event
+ * @return kCatDBItem
+ */
+ function &getTempItem(&$event)
+ {
+ return $this->Application->recallObject($event->Prefix.'.-tmpitem', $event->Prefix, Array('skip_autoload' => true));
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @param kEvent $event
+ */
+ function processCurrentItem(&$event)
+ {
+ $tmp_item =& $this->getTempItem($event);
+
+ // create/update categories
+ $backup_category_id = $this->Application->GetVar('m_cat_id');
+
+ foreach ($this->curItem->CategoryPath as $category_name) {
+ if (!$category_name) continue;
+ $category_id = $this->getFromCache('category_names', $category_name);
+ if ($category_id === false) {
+ // get parent category path to search only in it
+ $current_category_id = $this->Application->GetVar('m_cat_id');
+ $parent_path = $this->getParentPath($current_category_id);
+
+ // get category id from database by name
+ $sql = 'SELECT CategoryId
+ FROM '.TABLE_PREFIX.'Category
+ WHERE (Name = '.$this->Conn->qstr($category_name).') AND (ParentPath LIKE "'.$parent_path.'%")';
+ $category_id = $this->Conn->GetOne($sql);
+
+ if ($category_id === false) {
+ // category not in db -> create
+ $category_fields = Array( 'Name' => $category_name, 'Description' => $category_name,
+ 'Status' => STATUS_ACTIVE, 'ParentId' => $current_category_id,
+ );
+ $this->dummyCategory->SetDBFieldsFromHash($category_fields);
+ if ($this->dummyCategory->Create()) {
+ $category_id = $this->dummyCategory->GetID();
+ $this->addToCache('category_parent_path', $category_id, $this->dummyCategory->GetDBField('ParentPath'));
+ $this->addToCache('category_names', $category_name, $category_id);
+ }
+ }
+ else {
+ $this->addToCache('category_names', $category_name, $category_id);
+ }
+ }
+
+ if ($category_id) {
+ $this->Application->SetVar('m_cat_id', $category_id);
+ }
+ }
+
+ // create main record
+ $save_method = 'Create';
+ if ($this->exportOptions['ReplaceDuplicates']) {
+ if ($this->exportOptions['CheckDuplicatesMethod'] == 1) {
+ $load_keys = Array($this->curItem->IDField => $this->curItem->GetID());
+ }
+ else {
+ $key_fields = explode('|', substr($this->exportOptions['DuplicateCheckFields']) );
+ foreach ($key_fields as $key_field) {
+ $load_keys[$key_field] = $this->curItem->GetDBField($key_field);
+ }
+ }
+
+ $where_clause = '';
+ foreach ($load_keys as $field_name => $field_value) {
+ $where_clause .= '(item_table.`'.$field_name.'` = '.$this->Conn->qstr($field_value).') AND ';
+ }
+ $where_clause = preg_replace('/(.*) AND $/', '\\1', $where_clause);
+
+ $item_id = $this->getFromCache('new_ids', $where_clause);
+ if (!$item_id) {
+ $parent_path = $this->getParentPath($category_id);
+ $sql = 'SELECT '.$this->curItem->IDField.'
+ FROM '.$this->curItem->TableName.' item_table
+ LEFT JOIN '.TABLE_PREFIX.'CategoryItems ci ON ci.ItemResourceId = item_table.ResourceId
+ LEFT JOIN '.TABLE_PREFIX.'Category c ON c.CategoryId = ci.CategoryId
+ WHERE (c.ParentPath LIKE "'.$parent_path.'%") AND '.$where_clause;
+ $item_id = $this->Conn->GetOne($sql);
+ }
+ $save_method = $tmp_item->Load($item_id) ? 'Update' : 'Create';
+ }
+
+ $resource_id = $tmp_item->isLoaded() ? $tmp_item->GetDBField('ResourceId') : 0;
+ $tmp_item->SetDBFieldsFromHash($this->curItem->FieldValues);
+ if( ($save_method == 'Update') && $resource_id ) {
+ $tmp_item->SetDBField('ResourceId', $resource_id);
+ }
+
+ if (!$tmp_item->$save_method()) return false;
+
+ if ( ($save_method == 'Create') && $this->exportOptions['ReplaceDuplicates'] ) {
+ // map new id to old id
+ $this->addToCache('new_ids', $where_clause, $tmp_item->GetID() );
+ }
+
+ // set custom fields
+ foreach ($this->customValues as $custom_field => $custom_value) {
+ if (($save_method == 'Create') && !$custom_value) continue;
+ $tmp_item->SetCustomField($custom_field, $custom_value);
+ }
+
+ // assign item to categories
+ $tmp_item->assignToCategory($category_id, false);
+
+ $this->Application->SetVar('m_cat_id', $backup_category_id);
+ return true;
+ }
+
+ /**
+ * Returns category parent path, if possible, then from cache
+ *
+ * @param int $category_id
+ * @return string
+ */
+ function getParentPath($category_id)
+ {
+ $parent_path = $this->getFromCache('category_parent_path', $category_id);
+ if ($parent_path === false) {
+ $sql = 'SELECT ParentPath
+ FROM '.TABLE_PREFIX.'Category
+ WHERE CategoryId = '.$category_id;
+ $parent_path = $this->Conn->GetOne($sql);
+ $this->addToCache('category_parent_path', $category_id, $parent_path);
+ }
+ return $parent_path;
+ }
+
+ function loadItemCustomFields()
+ {
+ $sql = 'SELECT meta_data.Value, cf.FieldName
+ FROM '.TABLE_PREFIX.'CustomMetaData meta_data
+ LEFT JOIN '.TABLE_PREFIX.'CustomField cf ON cf.CustomFieldId = meta_data.CustomFieldId
+ WHERE meta_data.ResourceId = '.$this->curItem->GetDBField('ResourceId');
+ $this->customValues = $this->Conn->GetCol($sql, 'FieldName');
+ }
+
+ function getFileExtension()
+ {
+ return $this->exportOptions['ExportFormat'] == 1 ? 'csv' : 'xml';
+ }
+
+ function getLineSeparator($option = 'LineEndings')
+ {
+ return $this->exportOptions[$option] == 1 ? "\r\n" : "\n";
+ }
+
+ function scanCustomFields()
+ {
+ $ret = false;
+ $export_fields = $this->exportOptions['ExportColumns'];
+ foreach ($export_fields as $field)
+ {
+ if (substr($field, 0, 10) == '__CUSTOM__')
+ {
+ $ret = true;
+ break;
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Returns field caption for any exported field
+ *
+ * @param string $field
+ * @return string
+ */
+ function getFieldCaption($field)
+ {
+ if (substr($field, 0, 10) == '__CUSTOM__')
+ {
+ $ret = 'Custom_'.substr($field, 10, strlen($field) );
+ }
+ elseif (substr($field, 0, 12) == '__CATEGORY__')
+ {
+ return $this->getCategoryTitle();
+ }
+ else
+ {
+ $ret = $field;
+ }
+
+ return Array($ret);
+ }
+
+ /**
+ * Returns requested field value (including custom fields and category fields)
+ *
+ * @param string $field
+ * @return string
+ */
+ function getFieldValue($field)
+ {
+ if (substr($field, 0, 10) == '__CUSTOM__')
+ {
+ $field = substr($field, 10, strlen($field) );
+ $ret = isset($this->customValues[$field]) ? $this->customValues[$field] : '';
+ }
+ elseif (substr($field, 0, 12) == '__CATEGORY__')
+ {
+ return $this->getCategoryPath();
+ }
+ else
+ {
+ $ret = $this->curItem->GetField($field);
+ }
+
+ $ret = str_replace("\r\n", $this->getLineSeparator('LineEndingsInside'), $ret);
+ return Array($ret);
+ }
+
+ /**
+ * Returns category field(-s) caption based on export mode
+ *
+ * @return string
+ */
+ function getCategoryTitle()
+ {
+ if ($this->exportOptions['CategoryFormat'] == 1)
+ {
+ // category path in one field
+ return Array('CategoryPath');
+ }
+ else
+ {
+ // category path in separated fields
+ $category_count = $this->getMaxCategoryLevel();
+
+ $i = 0;
+ $ret = Array();
+ while ($i < $category_count) {
+ $ret[] = 'Category'.($i + 1);
+ $i++;
+ }
+ return $ret;
+ }
+ }
+
+ /**
+ * Returns category path in required format for current link
+ *
+ * @return string
+ */
+ function getCategoryPath()
+ {
+ $category_id = $this->curItem->GetDBField('CategoryId');
+ $category_path = $this->getFromCache('category_path', $category_id);
+ if (!$category_path)
+ {
+ $sql = 'SELECT CachedNavbar
+ FROM '.TABLE_PREFIX.'Category
+ WHERE CategoryId = '.$category_id;
+ $category_path = $this->Conn->GetOne($sql);
+ $category_path = explode('>', $category_path);
+
+ if ($this->exportOptions['IsBaseCategory'] ) {
+ $i = $this->exportOptions['BaseLevel'];
+ while ($i > 0) {
+ array_shift($category_path);
+ $i--;
+ }
+ }
+
+ if ($this->exportOptions['CategoryFormat'] == 1) {
+ // category path in single field
+ $category_path = Array( implode($this->exportOptions['CategorySeparator'], $category_path) );
+ }
+ else {
+ // category path in separated fields
+ $category_count = $this->getMaxCategoryLevel();
+ $levels_used = count($category_path);
+ if ($levels_used < $category_count)
+ {
+ $i = 0;
+ while ($i < $category_count - $levels_used) {
+ $category_path[] = '';
+ $i++;
+ }
+ }
+ }
+ $this->addToCache('category_path', $category_id, $category_path);
+ }
+
+ return $category_path;
+ }
+
+ /**
+ * Get maximal category deep level from links beeing exported
+ *
+ * @return int
+ */
+ function getMaxCategoryLevel()
+ {
+ static $max_level = -1;
+
+ if ($max_level != -1)
+ {
+ return $max_level;
+ }
+
+ $sql = 'SELECT IF(c.CategoryId IS NULL, 0, MAX( LENGTH(c.ParentPath) - LENGTH( REPLACE(c.ParentPath, "|", "") ) - 1 ))
+ FROM '.$this->curItem->TableName.' item_table
+ LEFT JOIN '.TABLE_PREFIX.'CategoryItems ci ON item_table.ResourceId = ci.ItemResourceId
+ LEFT JOIN '.TABLE_PREFIX.'Category c ON c.CategoryId = ci.CategoryId
+ WHERE (ci.PrimaryCat = 1) AND ';
+
+ $where_clause = '';
+ if ($this->exportOptions['export_ids'] === false) {
+ // get links from current category & all it's subcategories
+ if ($this->exportOptions['export_cats_ids'][0] == 0) {
+ $where_clause = 1;
+ }
+ else {
+ foreach ($this->exportOptions['export_cats_ids'] as $category_id) {
+ $where_clause .= '(c.ParentPath LIKE "%|'.$category_id.'|%") OR ';
+ }
+ $where_clause = preg_replace('/(.*) OR $/', '\\1', $where_clause);
+ }
+ }
+ else {
+ // get only selected links
+ $where_clause = $this->curItem->IDField.' IN ('.implode(',', $this->exportOptions['export_ids']).')';
+ }
+
+ $max_level = $this->Conn->GetOne($sql.'('.$where_clause.')');
+
+ if ($this->exportOptions['IsBaseCategory'] ) {
+ $max_level -= $this->exportOptions['BaseLevel'];
+ }
+
+ return $max_level;
+ }
+
+ /**
+ * Saves one record to export file
+ *
+ * @param Array $fields_hash
+ */
+ function writeRecord($fields_hash)
+ {
+ fputcsv2($this->filePointer, $fields_hash, $this->exportOptions['FieldsSeparatedBy'], $this->exportOptions['FieldsEnclosedBy'], $this->getLineSeparator() );
+ }
+
+ function readRecord()
+ {
+ return fgetcsv($this->filePointer, 10000, $this->exportOptions['FieldsSeparatedBy'], $this->exportOptions['FieldsEnclosedBy']);
+ }
+ }
+
+?>
Index: trunk/core/module_help/visits_list.txt
===================================================================
diff -u -N -r2976 -r3543
--- trunk/core/module_help/visits_list.txt (.../visits_list.txt) (revision 2976)
+++ trunk/core/module_help/visits_list.txt (.../visits_list.txt) (revision 3543)
@@ -1,24 +1,24 @@
-Visits (In-portal Platform)
-
-This section displays the log of all visits to your website. The visit is recorded when a user comes to your site. If the user logs in during his or her visit, the username will be updated in the visit record. If the user’s session expires during the visit, a new visit record will be created when he or she comes back to the site.
-The grid on this page displays the following columns:
-
-
Visit Date – Date and Time of the visit’s start
-
IP Address – the IP address of the visitor, as identified by the web server
-
Referrer – the URL the visitor came from, as reported by visitor’s browser
-
Username – the username of the visitor if he or she logs in during the visit
-
-
-Visits (with In-commerce installed)
-
-This section displays the log of all visits to your website. The visit is recorded when user comes to your site. If the user logs in during his or her visit, the username will be updated in the visit record. If the user’s session expires during the visit, a new visit record will be created when he or she comes back to the site.
-The grid on this page displays the following columns:
-
-
Visit Date – Date and Time of the visit’s start
-
IP Address – the IP address of the visitor, as identified by the web server
-
Referrer – the URL the visitor came from, as reported by visitor’s browser
-
Username – the username of the visitor if he or she logs in during the visit
-
Affiliate User – the username of affiliate who has referred the visitor
-
Order Total – the total amount of the orders made during this visit
-
Affiliate Commission – amount of affiliate commission received for the orders made during the visit
+Visits (In-portal Platform)
+
+This section displays the log of all visits to your website. The visit is recorded when a user comes to your site. If the user logs in during his or her visit, the username will be updated in the visit record. If the user’s session expires during the visit, a new visit record will be created when he or she comes back to the site.
+The grid on this page displays the following columns:
+
+
Visit Date – Date and Time of the visit’s start
+
IP Address – the IP address of the visitor, as identified by the web server
+
Referrer – the URL the visitor came from, as reported by visitor’s browser
+
Username – the username of the visitor if he or she logs in during the visit
+
+
+Visits (with In-commerce installed)
+
+This section displays the log of all visits to your website. The visit is recorded when user comes to your site. If the user logs in during his or her visit, the username will be updated in the visit record. If the user’s session expires during the visit, a new visit record will be created when he or she comes back to the site.
+The grid on this page displays the following columns:
+
+
Visit Date – Date and Time of the visit’s start
+
IP Address – the IP address of the visitor, as identified by the web server
+
Referrer – the URL the visitor came from, as reported by visitor’s browser
+
Username – the username of the visitor if he or she logs in during the visit
+
Affiliate User – the username of affiliate who has referred the visitor
+
Order Total – the total amount of the orders made during this visit
+
Affiliate Commission – amount of affiliate commission received for the orders made during the visit