Index: branches/RC/core/units/admin/admin_config.php =================================================================== diff -u -N -r8929 -r9639 --- branches/RC/core/units/admin/admin_config.php (.../admin_config.php) (revision 8929) +++ branches/RC/core/units/admin/admin_config.php (.../admin_config.php) (revision 9639) @@ -2,6 +2,7 @@ $config = Array( 'Prefix' => 'adm', + 'ItemClass' => Array('class' => 'kDBItem','file'=>'','build_event'=>'OnItemBuild'), 'EventHandlerClass' => Array('class' => 'AdminEventsHandler', 'file' => 'admin_events_handler.php', 'build_event' => 'OnBuild'), 'TagProcessorClass' => Array('class' => 'AdminTagProcessor', 'file' => 'admin_tag_processor.php', 'build_event' => 'OnBuild'), @@ -23,6 +24,8 @@ 'no_permissions' => Array('format' => '!la_title_NoPermissions!'), 'column_picker' => Array('format' => '!la_title_ColumnPicker!'), + 'csv_export' => Array('format' => '!la_title_CSVExport!'), + 'csv_import' => Array('format' => '!la_title_CSVImport!'), ), @@ -51,6 +54,18 @@ ), ), + 'Fields' => Array(), // we need empty array because kernel doesn't use virtual fields else + 'VirtualFields' => Array ( + 'ImportFile' => Array( + 'type'=>'string', 'formatter'=>'kUploadFormatter', + 'max_size'=>MAX_UPLOAD_SIZE, // in Bytes ! + 'file_types'=>'*.csv', 'files_description'=>'!la_CSVFiles!', + 'upload_dir'=>'/system/import/', // relative to project's home + 'multiple'=>false, // false or max number of files - will be stored as serialized array of paths + 'direct_links'=>false, // use direct file urls or send files through wrapper (requires mod_mime_magic) + 'default' => null, + ), + ), ); ?> \ No newline at end of file Index: branches/RC/core/units/general/inp_ses_storage.php =================================================================== diff -u -N -r8929 -r9639 --- branches/RC/core/units/general/inp_ses_storage.php (.../inp_ses_storage.php) (revision 8929) +++ branches/RC/core/units/general/inp_ses_storage.php (.../inp_ses_storage.php) (revision 9639) @@ -42,6 +42,17 @@ $this->SetField('GroupList', $group_list); } } + + function Destroy() + { + $this->Storage->DeleteSession($this); + $this->Storage->DeleteEditTables(); + $this->Data =& new Params(); + $this->SID = ''; + if ($this->CookiesEnabled) $this->SetSessionCookie(); //will remove the cookie due to value (sid) is empty + $this->SetSession(); //will create a new session + } + } class InpSessionStorage extends SessionStorage { Index: branches/RC/core/admin_templates/index.tpl =================================================================== diff -u -N -r9239 -r9639 --- branches/RC/core/admin_templates/index.tpl (.../index.tpl) (revision 9239) +++ branches/RC/core/admin_templates/index.tpl (.../index.tpl) (revision 9639) @@ -31,7 +31,7 @@ " name="head" scrolling="no" noresize="noresize"> - 2000,*" border="0"> + ,0,*" border="0"> " name="menu" target="main" noresize scrolling="auto" marginwidth="0" marginheight="0"> " name="main" marginwidth="0" marginheight="0" frameborder="no" noresize scrolling="auto"> Index: branches/RC/core/install/install_data.sql =================================================================== diff -u -N -r9419 -r9639 --- branches/RC/core/install/install_data.sql (.../install_data.sql) (revision 9419) +++ branches/RC/core/install/install_data.sql (.../install_data.sql) (revision 9639) @@ -67,7 +67,12 @@ INSERT INTO ConfigurationAdmin VALUES ('UseToolbarLabels', 'la_Text_Website', 'la_config_UseToolbarLabels', 'checkbox', NULL , NULL , 10.20, 0, 0); INSERT INTO ConfigurationAdmin VALUES ('UseSmallHeader', 'la_Text_Website', 'la_config_UseSmallHeader', 'checkbox', '', '', 10.21, 0, 0); INSERT INTO ConfigurationAdmin VALUES ('User_Default_Registration_Country', 'la_Text_General', 'la_config_DefaultRegistrationCountry', 'select', NULL , '=+,SELECT DestName AS OptionName, DestId AS OptionValue FROM StdDestinations WHERE DestParentId IS NULL Order BY OptionName', 10.111, 0, 0); +INSERT INTO ConfigurationAdmin VALUES ('CSVExportDelimiter', 'la_Text_CSV_Export', 'la_config_CSVExportDelimiter', 'select', NULL, '0=la_Tab,1=la_Comma,2=la_Semicolon,3=la_Space,4=la_Colon', 40.1, 0, 1); +INSERT INTO ConfigurationAdmin VALUES ('CSVExportEnclosure', 'la_Text_CSV_Export', 'la_config_CSVExportEnclosure', 'radio', NULL, '0=la_Doublequotes,1=la_Quotes', 40.2, 0, 1); +INSERT INTO ConfigurationAdmin VALUES ('CSVExportSeparator', 'la_Text_CSV_Export', 'la_config_CSVExportSeparator', 'radio', NULL, '0=la_Linux,1=la_Windows', 40.3, 0, 1); +INSERT INTO ConfigurationAdmin VALUES ('CSVExportEncoding', 'la_Text_CSV_Export', 'la_config_CSVExportEncoding', 'radio', NULL, '0=la_Unicode,1=la_Regular', 40.4, 0, 1); + INSERT INTO ConfigurationValues VALUES (DEFAULT, 'Columns_Category', '2', 'In-Portal', 'Categories'); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'DomainSelect','1','In-Portal','in-portal:configure_general'); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'Site_Path', '/', 'In-Portal', 'in-portal:configure_general'); @@ -207,6 +212,10 @@ INSERT INTO ConfigurationValues VALUES (DEFAULT, 'UseToolbarLabels', '1', 'In-Portal', 'in-portal:configure_general'); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'UseSmallHeader', '0', 'In-Portal', 'in-portal:configure_general'); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'User_Default_Registration_Country', '', 'In-Portal:Users', 'in-portal:configure_users'); +INSERT INTO ConfigurationValues VALUES (DEFAULT, 'CSVExportDelimiter', '0', 'In-Portal', 'in-portal:configure_general'); +INSERT INTO ConfigurationValues VALUES (DEFAULT, 'CSVExportEnclosure', '0', 'In-Portal', 'in-portal:configure_general'); +INSERT INTO ConfigurationValues VALUES (DEFAULT, 'CSVExportSeparator', '0', 'In-Portal', 'in-portal:configure_general'); +INSERT INTO ConfigurationValues VALUES (DEFAULT, 'CSVExportEncoding', '0', 'In-Portal', 'in-portal:configure_general'); INSERT INTO Events VALUES (DEFAULT, 'USER.ADD', NULL, 1, 0, 'In-Portal:Users', 'la_event_user.add', 0); INSERT INTO Events VALUES (DEFAULT, 'USER.ADD', NULL, 2, 0, 'In-Portal:Users', 'la_event_user.add', 1); Index: branches/RC/core/units/users/users_event_handler.php =================================================================== diff -u -N -r9419 -r9639 --- branches/RC/core/units/users/users_event_handler.php (.../users_event_handler.php) (revision 9419) +++ branches/RC/core/units/users/users_event_handler.php (.../users_event_handler.php) (revision 9639) @@ -1218,6 +1218,31 @@ } } + function OnMassResetSettings(&$event) + { + if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { + return; + } + + $event->status=erSUCCESS; + $ids = $this->StoreSelectedIDs($event); + + $default_user_id = $this->Application->ConfigValue('DefaultSettingsUserId'); + if (in_array($default_user_id, $ids)) { + array_splice($ids, array_search($default_user_id, $ids), 1); + } + if ($ids) { + $q = 'DELETE FROM '.TABLE_PREFIX.'PersistantSessionData WHERE PortalUserId IN ('.join(',', $ids).') AND + (VariableName LIKE "%_columns_%" + OR + VariableName LIKE "%_filter%" + OR + VariableName LIKE "%_PerPage%")'; + $this->Conn->Query($q); + } + $this->clearSelectedIDs($event); + } + /** * Checks, that currently loaded item is allowed for viewing (non permission-based) * Index: branches/RC/core/admin_templates/incs/footer.tpl =================================================================== diff -u -N -r8929 -r9639 --- branches/RC/core/admin_templates/incs/footer.tpl (.../footer.tpl) (revision 8929) +++ branches/RC/core/admin_templates/incs/footer.tpl (.../footer.tpl) (revision 9639) @@ -7,6 +7,8 @@ document.body.style.overflow = 'hidden'; document.body.scroll = 'no' + var _Simultanious_Edit_Message = ''; + var _DropTempUrl = ''; addLoadEvent(function() {Form.Init('scroll_container')}); } Index: branches/RC/core/kernel/globals.php =================================================================== diff -u -N -r9558 -r9639 --- branches/RC/core/kernel/globals.php (.../globals.php) (revision 9558) +++ branches/RC/core/kernel/globals.php (.../globals.php) (revision 9639) @@ -305,7 +305,7 @@ * @param unknown_type $post * @return unknown */ - function curl_post($url, $post, $headers=null, $request_type = 'POST') + function curl_post($url, $post, $headers=null, $request_type = 'POST', $curl_options=null) { if( is_array($post) ) { @@ -348,6 +348,11 @@ curl_setopt($ch,CURLOPT_FOLLOWLOCATION, 0); curl_setopt($ch, CURLOPT_TIMEOUT, 90); + if (is_array($curl_options)) { + foreach ($curl_options as $option => $value) { + curl_setopt($ch, $option, $value); + } + } $ret = curl_exec($ch); $GLOBALS['curl_errorno'] = curl_errno($ch); $GLOBALS['curl_error'] = curl_error($ch); @@ -472,10 +477,30 @@ } $line = $enclosure.implode($enclosure.$delimiter.$enclosure, $data).$enclosure.$recordSeparator; + $line = preg_replace('/'.preg_quote($enclosure).'([0-9\.]+)'.preg_quote($enclosure).'/', '$1', $line); fwrite($filePointer, $line); } /** + * Enter description here... + * + * @param resource $filePointer the file resource to write to + * @param Array $data the data to write out + * @param string $delimiter the field separator + * @param string $enclosure symbol to enclose field data to + * @param string $recordSeparator symbols to separate records with + */ + function getcsvline($data, $delimiter = ',', $enclosure = '"', $recordSeparator = "\r\n") + { + foreach($data as $field_index => $field_value) { + // replaces an enclosure with two enclosures + $data[$field_index] = str_replace($enclosure, $enclosure.$enclosure, $field_value); + } + $line = $enclosure.implode($enclosure.$delimiter.$enclosure, $data).$enclosure.$recordSeparator; + $line = preg_replace('/'.preg_quote($enclosure).'([0-9\.]+)'.preg_quote($enclosure).'/', '$1', $line); + return $line; + } + /** * Allows to replace #section# within any string with current section * * @param string $string Index: branches/RC/core/units/skins/skin_eh.php =================================================================== diff -u -N -r9313 -r9639 --- branches/RC/core/units/skins/skin_eh.php (.../skin_eh.php) (revision 9313) +++ branches/RC/core/units/skins/skin_eh.php (.../skin_eh.php) (revision 9639) @@ -49,7 +49,8 @@ $object =& $event->getObject( Array('skip_autoload' => true) ); $object->SwitchToLive(); - $ids = explode(',', $event->MasterEvent->getEventParam('ids') ); + $ids = $event->MasterEvent->getEventParam('ids'); + if (!is_array($ids)) $ids = explode(',', $ids); if(!$ids) return false; foreach($ids as $id) Index: branches/RC/core/kernel/parser/construct_tags.php =================================================================== diff -u -N -r9286 -r9639 --- branches/RC/core/kernel/parser/construct_tags.php (.../construct_tags.php) (revision 9286) +++ branches/RC/core/kernel/parser/construct_tags.php (.../construct_tags.php) (revision 9639) @@ -66,7 +66,7 @@ $inverse = $this->GetParam('inverse'); if ($prefix !== false) { - $tag = new Tag('', $this->Parser); + $tag =& new Tag('', $this->Parser); $tag->Tag = $function; $tmp = $this->Application->processPrefix($prefix); @@ -219,6 +219,7 @@ } $this->Parser->UsedProcessors = null;*/ + // because of unshift the code is added from bottom to top here, so read from the bottom below: array_unshift($code, '$o = \'\';'); array_unshift($code, '$application->Parser->SetParams($params, false);'); array_unshift($code, '$application =& kApplication::Instance();'); @@ -233,6 +234,7 @@ array_unshift($code, '$params = array_merge_recursive2($defaults, $params);'); array_unshift($code, $defaults); + // ^^^^ read up from here ^^^^ $code[] = 'return $o;'; global $debugger; Index: branches/RC/core/install/upgrades.sql =================================================================== diff -u -N -r9419 -r9639 --- branches/RC/core/install/upgrades.sql (.../upgrades.sql) (revision 9419) +++ branches/RC/core/install/upgrades.sql (.../upgrades.sql) (revision 9639) @@ -137,4 +137,14 @@ ADD Priority INT NOT NULL AFTER AdminInterfaceLang; DELETE FROM PersistantSessionData WHERE VariableName = 'lang_columns_.'; -ALTER TABLE SessionData CHANGE VariableValue VariableValue longtext NOT NULL; \ No newline at end of file +ALTER TABLE SessionData CHANGE VariableValue VariableValue longtext NOT NULL; + +REPLACE INTO ConfigurationAdmin VALUES ('CSVExportDelimiter', 'la_Text_CSV_Export', 'la_config_CSVExportDelimiter', 'select', NULL, '0=la_Tab,1=la_Comma,2=la_Semicolon,3=la_Space,4=la_Colon', 40.1, 0, 1); +REPLACE INTO ConfigurationAdmin VALUES ('CSVExportEnclosure', 'la_Text_CSV_Export', 'la_config_CSVExportEnclosure', 'radio', NULL, '0=la_Doublequotes,1=la_Quotes', 40.2, 0, 1); +REPLACE INTO ConfigurationAdmin VALUES ('CSVExportSeparator', 'la_Text_CSV_Export', 'la_config_CSVExportSeparator', 'radio', NULL, '0=la_Linux,1=la_Windows', 40.3, 0, 1); +REPLACE INTO ConfigurationAdmin VALUES ('CSVExportEncoding', 'la_Text_CSV_Export', 'la_config_CSVExportEncoding', 'radio', NULL, '0=la_Unicode,1=la_Regular', 40.4, 0, 1); + +REPLACE INTO ConfigurationValues VALUES (DEFAULT, 'CSVExportDelimiter', '0', 'In-Portal', 'in-portal:configure_general'); +REPLACE INTO ConfigurationValues VALUES (DEFAULT, 'CSVExportEnclosure', '0', 'In-Portal', 'in-portal:configure_general'); +REPLACE INTO ConfigurationValues VALUES (DEFAULT, 'CSVExportSeparator', '0', 'In-Portal', 'in-portal:configure_general'); +REPLACE INTO ConfigurationValues VALUES (DEFAULT, 'CSVExportEncoding', '0', 'In-Portal', 'in-portal:configure_general'); Index: branches/RC/core/admin_templates/img/toolbar/tool_import.gif =================================================================== diff -u -N Binary files differ Index: branches/RC/core/units/general/helpers/search_helper.php =================================================================== diff -u -N -r8929 -r9639 --- branches/RC/core/units/general/helpers/search_helper.php (.../search_helper.php) (revision 8929) +++ branches/RC/core/units/general/helpers/search_helper.php (.../search_helper.php) (revision 9639) @@ -291,7 +291,7 @@ // update "custom filter" with values from submit: begin $view_name = $this->Application->RecallVar($event->getPrefixSpecial().'_current_view'); - $custom_filters = $this->Application->RecallPersistentVar($event->getPrefixSpecial().'_custom_filter.'.$view_name); + $custom_filters = $this->Application->RecallPersistentVar($event->getPrefixSpecial().'_custom_filter.'.$view_name, ALLOW_DEFAULT_SETTINGS); if ($custom_filters) { $custom_filters = unserialize($custom_filters); $custom_filter = isset($custom_filters[$grid_name]) ? $custom_filters[$grid_name] : Array (); Index: branches/RC/core/units/general/helpers/priority_helper.php =================================================================== diff -u -N -r8929 -r9639 --- branches/RC/core/units/general/helpers/priority_helper.php (.../priority_helper.php) (revision 8929) +++ branches/RC/core/units/general/helpers/priority_helper.php (.../priority_helper.php) (revision 9639) @@ -22,7 +22,10 @@ FROM '.$table_name; if ($constrain) { $sql .= ' WHERE '.$constrain; + } + if (!isset($object->Fields['OldPriority'])) { + $object->VirtualFields['OldPriority'] = Array('type' => 'int', 'default' => 0); } $items_count = $this->Conn->GetOne($sql); @@ -32,19 +35,23 @@ $object->SetDBField('Priority', -$items_count); $object->SetDBField('OldPriority', -$items_count); } + else { + $object->SetDBField('OldPriority', $object->GetDBField('Priority')); + } for ($i = 1; $i <= $items_count; $i++) { $field_options['options'][-$i] = $i; } $object->SetFieldOptions('Priority', $field_options); + // storing prioriry right after load for comparing when updating } /** * Updates priorities for changed items * * @param kEvent $event - * @param Array $changes = Array (CategoryID => Array ('parent' => ..., 'new' => ..., 'old' => ...), ...) + * @param Array $changes = Array (ID => Array ('parent' => ..., 'new' => ..., 'old' => ...), ...) * @param Array $new_ids = Array (temp_id => live_id) * @param string $constrain */ @@ -55,8 +62,10 @@ $not_processed = array_keys($changes); + $ids = array(); foreach ($changes as $id => $pair) { - $constrain = 'ParentId = '.$pair['parent'].' AND '; + array_push($ids, $id); + $constrain = isset($pair['parent']) ? 'ParentId = '.$pair['parent'].' AND ' : ''; if ($pair['old'] == 'new') { // replace 0 with newly created item id (from $new_ids mapping) @@ -65,7 +74,7 @@ $sql = 'SELECT MIN(Priority) FROM '.$table_name.' - WHERE '.$constrain.' CategoryId NOT IN ('.implode(',', $not_processed).')'; + WHERE '.$constrain.' '.$id_field.' NOT IN ('.implode(',', $not_processed).')'; $min_priority = (int)$this->Conn->GetOne($sql) - 1; if ($pair['new'] < $min_priority) { @@ -75,31 +84,34 @@ } if ($pair['new'] < $pair['old']) { - $q = ' SET Priority = Priority + 1 - WHERE '.$constrain.' + $set = ' SET Priority = Priority + 1'; + $where =' WHERE '.$constrain.' Priority >= '.$pair['new'].' AND Priority < '.$pair['old'].' AND - CategoryId NOT IN ('.implode(',', $not_processed).')'; + '.$id_field.' NOT IN ('.implode(',', $not_processed).')'; } elseif ($pair['new'] > $pair['old']) { - $q = ' SET Priority = Priority - 1 - WHERE '.$constrain.' + $set = ' SET Priority = Priority - 1'; + $where =' WHERE '.$constrain.' Priority > '.$pair['old'].' AND Priority <= '.$pair['new'].' AND - CategoryId NOT IN ('.implode(',', $not_processed).')'; + '.$id_field.' NOT IN ('.implode(',', $not_processed).')'; } else { - $q = 'SET Priority = '.$pair['new'].' WHERE '.$id_field.' = '.$id; + $set = 'SET Priority = '.$pair['new']; + $where = ' WHERE '.$id_field.' = '.$id; } - $q = 'UPDATE '.$table_name.' '.$q; + $ids = array_merge($ids, $this->Conn->GetCol('SELECT '.$id_field.' FROM '.$table_name.$where)); + $q = 'UPDATE '.$table_name.' '.$set.$where; $this->Conn->Query($q); unset( $not_processed[array_search($id, $not_processed)] ); } + return $ids; } /** @@ -126,6 +138,7 @@ WHERE '.$id_field.' = '.$item_id; $this->Conn->Query($sql); } + return $items; } } Index: branches/RC/core/admin_templates/js/simple_grid.js =================================================================== diff -u -N --- branches/RC/core/admin_templates/js/simple_grid.js (revision 0) +++ branches/RC/core/admin_templates/js/simple_grid.js (revision 9639) @@ -0,0 +1,144 @@ +function SimpleGrid($id_prefix, $field_mask, $show_order) { + this.ItemIDs = new Array (); + this.NextNumber = 0; + this.IDPrefix = $id_prefix; + this.FieldMask = $field_mask; + this.Container = this.getControl('container'); + this.ShowOrder = $show_order; +} + +SimpleGrid.prototype.registerRecord = function ($id) { + var $prev_id = this.lastID(); + if ($prev_id !== false) { + this.getControl('button_' + $prev_id).value = '-'; + } + + $id = parseInt($id); + this.ItemIDs.push($id); + + if ($id < 0 && Math.abs($id) > Math.abs(this.NextNumber)) { + this.NextNumber = $id; + } + + if (this.ShowOrder) { + this.getControl('number_'+$id).innerHTML = this.ItemIDs.length; + } + + this.processRecordDependancies($id); +} + +SimpleGrid.prototype.renumberRecords = function () { + if (!this.ShowOrder) { + return ; + } + + var $i = 0; + while ($i < this.ItemIDs.length) { + this.getControl('number_' + this.ItemIDs[$i]).innerHTML = $i + 1; + $i++; + } +} + +SimpleGrid.prototype.nextNumber = function () { + return --this.NextNumber; +} + +SimpleGrid.prototype.lastID = function () { + return this.ItemIDs.length ? this.ItemIDs.slice(this.ItemIDs.length - 1)[0] : false; +} + +SimpleGrid.prototype.toggleRecord = function ($item_id) { + if (this.lastID() == $item_id) { + this.addRecord(); + } + else { + this.removeRecord($item_id); + } +} + +SimpleGrid.prototype.getControl = function ($name) { + return document.getElementById(this.IDPrefix + '_' + $name); +} + +SimpleGrid.prototype.getField = function ($id, $field_name, $prepend, $append) { + $prepend = isset($prepend) ? $prepend + '_' : ''; + $append = isset($append) ? '_' + $append : ''; + + return document.getElementById($prepend + this.FieldMask.replace('#FIELD#', $field_name).replace('#ID#', $id) + $append); +} + +SimpleGrid.prototype.markError = function ($item_id, $field_name, $error_found) { + if (!isset($error_found)) { + $error_found = true; + } + + var $field = this.getField($item_id, $field_name); + if ($error_found) { + $field.style.backgroundColor = '#EF2C2C'; + + // TODO: duplicate focus events possible on one element, fix this later + addEvent($field, 'focus', + function($e) { + if (!$e) { + $e = window.event; + } + + var $element = document.all ? $e.srcElement : $e.currentTarget; + $element.style.backgroundColor = ''; + } + + ); + } + else { + $field.style.backgroundColor = ''; + } +} + +SimpleGrid.prototype.validateRecord = function ($item_id) { + return true; +} + +SimpleGrid.prototype.validate = function () { + var $i = 0; + var $is_valid = true; + while ($i < this.ItemIDs.length) { + if (!this.validateRecord(this.ItemIDs[$i])) { + $is_valid = false; + } + $i++; + } + + return $is_valid; +} + +SimpleGrid.prototype.addRecord = function () { + var new_item_number = this.nextNumber(); + + var $item_mask = this.getControl('mask').value; + var $result_html = $item_mask.replace(/#NUMBER#/g, new_item_number); + + var $div = document.createElement('DIV'); + $div.id = this.IDPrefix + '_' + new_item_number; + this.getControl('container').appendChild($div); + $div.innerHTML = $result_html; + + var $js_mask = this.getControl('js_mask').value; + var result_js = $js_mask.replace(/#NUMBER#/g, new_item_number); + eval(result_js); +} + +SimpleGrid.prototype.removeRecord = function ($number) { + this.getControl('container').removeChild( this.getControl($number) ); + var $index = array_search($number, this.ItemIDs); + this.ItemIDs.splice($index, 1); + this.renumberRecords(); + this.updateTotals(); +} + +SimpleGrid.prototype.updateTotals = function () { + // prototype +} + +SimpleGrid.prototype.processRecordDependancies = function ($id) { + // prototype +} Index: branches/RC/core/units/general/helpers/helpers_config.php =================================================================== diff -u -N -r8929 -r9639 --- branches/RC/core/units/general/helpers/helpers_config.php (.../helpers_config.php) (revision 8929) +++ branches/RC/core/units/general/helpers/helpers_config.php (.../helpers_config.php) (revision 9639) @@ -23,5 +23,6 @@ Array('pseudo'=>'ImageHelper','class'=>'ImageHelper','file'=>'image_helper.php','build_event'=>'','require_classes'=>'kHelper'), Array('pseudo'=>'CategoryHelper','class'=>'CategoryHelper','file'=>'category_helper.php','build_event'=>'','require_classes'=>'kHelper'), + Array('pseudo'=>'CSVHelper','class'=>'kCSVHelper','file'=>'csv_helper.php','build_event'=>'','require_classes'=>'kHelper'), ), ); \ No newline at end of file Index: branches/RC/core/admin_templates/js/ajax_dropdown.js =================================================================== diff -u -N -r9321 -r9639 --- branches/RC/core/admin_templates/js/ajax_dropdown.js (.../ajax_dropdown.js) (revision 9321) +++ branches/RC/core/admin_templates/js/ajax_dropdown.js (.../ajax_dropdown.js) (revision 9639) @@ -97,6 +97,7 @@ this.Box.style.overflow = 'auto'; this.Box.className = 'suggest-box' + this.Input.setAttribute('autocomplete', 'off'); // add onkeyup var obj = this; addEvent(this.Input, 'keyup', function(ev) {obj.KeyUp(ev)}) Index: branches/RC/core/admin_templates/js/application.js =================================================================== diff -u -N --- branches/RC/core/admin_templates/js/application.js (revision 0) +++ branches/RC/core/admin_templates/js/application.js (revision 9639) @@ -0,0 +1,72 @@ +function kApplication() { + this.Hooks = new Array (); + + addLoadEvent( + function () { + Application.processHooks('m:OnAfterWindowLoad'); + } + ); + +} + +kApplication.prototype.SetVar = function ($name, $value) { + set_hidden_field($name, $value); +} + +// hidden fields are printed in footer, that's why use this in m:OnAfterWindowLoad hook +kApplication.prototype.GetVar = function ($name) { + return get_hidden_field($name); +} + +kApplication.prototype.setHook = function ($hook_to, $do_code, $hook_mode) { + if (!isset($hook_mode)) { + $hook_mode = hBEFORE; + } + if (typeof $hook_to == 'string') { + setArrayValue(this.Hooks, $hook_to, $hook_mode, $do_code); + } + else { + var $i = 0; + while ($i < $hook_to.length) { + this.setHook($hook_to[$i], $do_code, $hook_mode); + $i++; + } + } +} + +kApplication.prototype.processHooks = function ($hook_to, $hook_mode) { + if (!isset($hook_mode)) { + $hook_mode = hBEFORE; + } + + var $i = 0; + var $defined_hooks = getArrayValue(this.Hooks, $hook_to, $hook_mode); + + while($i < $defined_hooks.length) { + var $do_event = new kEvent($hook_to); + $defined_hooks[$i]($do_event); + if (!$do_event.status) { + return false; + break; + } + $i++; + } + return true; +} + +// ======================================= + +function kEvent($params) { + if ($params.match(/([^.:]*)[.]{0,1}([^:]*):(.*)/, $params)) { + this.Prefix = RegExp.$1; + this.Special = RegExp.$2; + this.Name = RegExp.$3; + } + else { + this.Prefix = ''; + this.Special = ''; + this.Name = ''; + } + + this.status = true; +} \ No newline at end of file Index: branches/RC/core/units/admin/admin_tag_processor.php =================================================================== diff -u -N -r9359 -r9639 --- branches/RC/core/units/admin/admin_tag_processor.php (.../admin_tag_processor.php) (revision 9359) +++ branches/RC/core/units/admin/admin_tag_processor.php (.../admin_tag_processor.php) (revision 9639) @@ -783,7 +783,12 @@ return $this->Application->HREF($t, '', $vars, $index_file); } - + function MenuFrameWidth($params) + { + $w = $this->Application->ConfigValue('MenuFrameWidth'); + if (!$w) $w = 200; + return $w; + } function AdminSkin($params) { static $style; @@ -857,6 +862,46 @@ $this->Application->RemoveVar('compile_errors'); return $o; } + + function ExportData($params) + { + $export_helper =& $this->Application->recallObject('CSVHelper'); + /* @var $export_helper kCSVHelper */ + $result = $export_helper->ExportData( $this->SelectParam($params, 'var,name,field') ); + return ($result === false) ? '' : $result; + } + function ImportData($params) + { + $import_helper =& $this->Application->recallObject('CSVHelper'); + /* @var $import_helper kCSVHelper */ + $result = $import_helper->ImportData( $this->SelectParam($params, 'var,name,field') ); + return ($result === false) ? '' : $result; + } + + function PrintCSVNotImportedLines($params) + { + $import_helper =& $this->Application->recallObject('CSVHelper'); + /* @var $import_helper kCSVHelper */ + return $import_helper->GetNotImportedLines(); + } + + /** + * Returns input field name to + * be placed on form (for correct + * event processing) + * + * @param Array $params + * @return string + * @access public + */ + function InputName($params) + { + list($id, $field) = $this->prepareInputName($params); + + $ret = $this->getPrefixSpecial().'[0]['.$field.']'; // 0 always, as has no idfield + if( getArrayValue($params, 'as_preg') ) $ret = preg_quote($ret, '/'); + return $ret; + } } ?> \ No newline at end of file Index: branches/RC/core/admin_templates/js/forms.js =================================================================== diff -u -N -r9266 -r9639 --- branches/RC/core/admin_templates/js/forms.js (.../forms.js) (revision 9266) +++ branches/RC/core/admin_templates/js/forms.js (.../forms.js) (revision 9639) @@ -104,6 +104,35 @@ } } } + + addEvent(window, 'unload', function() { + if (!unload_legal) { // it's set in submit_kernel_form - if we are submitting form, the unload is already handled + // if we got here, it means that we are closing window ilegally (through OS interace X button) +// alert('closing window'); + // we need to clear temp tables then + + var req = Request.getRequest(); + var $ajax_mark = (_DropTempUrl.indexOf('?') ? '&' : '?') + 'ajax=yes'; + req.open('GET', _DropTempUrl + $ajax_mark, false); //!!!SYNCRONIOUS!!! REQUEST (3rd param = false!!!) + req.send(null); + +// window.opener.EditWindowClosed(_DropTempUrl); +// alert('request sent, closing widow'); + } + }); + + // we need to set unload_legal to true if we are refreshing current window, because onunload will still fire + addEvent(document, 'keydown', function(e) { + var code; + if (!e) var e = window.event; + if (e.keyCode) code = e.keyCode; + else if (e.which) code = e.which; + if (code == 116 || (e.ctrlKey && code == 82)) unload_legal = true; //F5 or Ctrl+R + }); + + if (_Simultanious_Edit_Message != '') { + alert(_Simultanious_Edit_Message); + } } Form.addControl = function(id, options) { Index: branches/RC/core/admin_templates/js/dropdown_mapper.js =================================================================== diff -u -N --- branches/RC/core/admin_templates/js/dropdown_mapper.js (revision 0) +++ branches/RC/core/admin_templates/js/dropdown_mapper.js (revision 9639) @@ -0,0 +1,129 @@ +function DropDownMapper(map, request, input_mask) { + this.Map = map; + this.Request = request; + this.InputMask = input_mask; + this.Objects = new Object(); + this.WatchFields = []; + this.UpdateFields = {}; + this.Init() +} + +DropDownMapper.prototype.Init = function() +{ + this.AssignDrills(this.Map); + this.DrillDownRequests(this.Map,'root'); + for (var i in this.WatchFields) { + var elem = document.getElementById(this.InputMask.replace('#FIELD#', this.WatchFields[i])); + elem.fieldName = this.WatchFields[i]; + addEvent(elem, 'change', function(ev) { + var el = !is.ie ? ev.currentTarget : window.event.srcElement; + mapper.Changed(el.fieldName); + }) + } +} + +DropDownMapper.prototype.GetFilters = function(map, o) +{ + if (!map) map = this.Map; + if (!o) o = ''; + for (var i in map) { + if (map[i].Field) o += escape(map[i].Field+'='+this.GetValue(map[i].Field)+'&'); + if (map[i].Pass) { // additional fields to pass to the ajax query + for (var x in map[i].Pass) { + o += escape(map[i].Pass[x]+'='+this.GetValue(map[i].Pass[x])+'&'); + } + } + if (map[i].SubNodes) { + o += this.GetFilters(map[i].SubNodes) + } + } + return o; +} + +DropDownMapper.prototype.GetValue = function(field) +{ + elem = document.getElementById(this.InputMask.replace('#FIELD#', field)); + if (elem.type && elem.type == 'checkbox') return elem.checked ? 1:0; + return elem.value; +} + +DropDownMapper.prototype.Changed = function(field) +{ + var branch = this.FindBranch(field); + this.DrillDownRequests(branch, field); + if (this.UpdateFields[field]) { + for (var i in this.UpdateFields[field]) { + this.Objects[this.UpdateFields[field][i]].Query(); + } + } +} + +DropDownMapper.prototype.AssignDrills = function(map) +{ + var mapper = this; + for (var i in map) { + if ( map[i].Field ) { + this.WatchFields.push(map[i].Field); + if ( map[i].Pass ) { + for (var p in map[i].Pass) { + if (!in_array(map[i].Pass[p], this.WatchFields)) { + this.WatchFields.push(map[i].Pass[p]); + } + if (!this.UpdateFields[map[i].Pass[p]]) this.UpdateFields[map[i].Pass[p]] = new Array(); + if (!in_array(map[i].Field, this.UpdateFields[map[i].Pass[p]])) { + this.UpdateFields[map[i].Pass[p]].push(map[i].Field); + } + } + } + } + if ( typeof(map[i].Dependent) == 'undefined' || i.Dependent ) { + var val = map[i].Value ? map[i].Value : null + var obj = new AjaxDropdownPreloader(this.Request, this.InputMask, null, map[i].Field, val ); + obj.prepareURL = function() + { + return this.URL.replace('#QUESTIONED#', this.DependendField).replace('#FILTERS#', mapper.GetFilters().replace(/%26$/, '')) + } + this.Objects[map[i].Field] = obj; + } + + if (typeof(map[i].SubNodes) == 'object') { + if ( typeof(map[i].Dependent) == 'undefined' || i.Dependent ) { + var ii = i; // we need this, otherwise AfterProcess will use the wrong i (be reference) + obj.AfterProcess = function() { +// mapper.DrillDownRequests(map[ii].SubNodes, ii); + } + } + this.AssignDrills(map[i].SubNodes); + } + } +} + +DropDownMapper.prototype.FindBranch = function(branch, map) +{ + if (!map) map = this.Map; + for (var i in map) { + if (typeof(map[i].SubNodes) == 'object') { + if (map[i].Field == branch) return map[i].SubNodes; + var res = this.FindBranch(branch, map[i].SubNodes); + if (res) return res; + } + } + return false; +} + +DropDownMapper.prototype.DrillDownRequests = function(map,item) +{ + for (var i in map) { + if (map[i].Field && this.Objects[map[i].Field]) { + this.Objects[map[i].Field].Query(); + } + else { + this.DrillDownRequests(map[i].SubNodes,item); //if there is no object for current item, try its children + } + } +} + +DropDownMapper.prototype.QueryByField = function(field) +{ + this.Objects[field].Query(); +} \ No newline at end of file Index: branches/RC/core/admin_templates/img/toolbar/tool_import_f2.gif =================================================================== diff -u -N Binary files differ Index: branches/RC/core/admin_templates/img/toolbar/tool_import_f3.gif =================================================================== diff -u -N Binary files differ Index: branches/RC/core/admin_templates/img/toolbar/tool_export.gif =================================================================== diff -u -N Binary files differ Index: branches/RC/core/units/admin/admin_events_handler.php =================================================================== diff -u -N -r9542 -r9639 --- branches/RC/core/units/admin/admin_events_handler.php (.../admin_events_handler.php) (revision 9542) +++ branches/RC/core/units/admin/admin_events_handler.php (.../admin_events_handler.php) (revision 9639) @@ -9,6 +9,12 @@ 'OnSaveColumns' => array('self' => true), 'OnClosePopup' => array('self' => true), 'OnSaveSetting' => array('self' => true), + // export/import permissions is checked within events + 'OnExportCSV' => Array('self' => true), + 'OnGetCSV' => Array('self' => true), + 'OnCSVImportBegin' => Array('self' => true), + 'OnCSVImportStep' => Array('self' => true), + 'OnDropTempTablesByWID' => array('self' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); } @@ -240,6 +246,14 @@ } /** + * Occurs right before echoing the output, in Done method of application, used mainly as hook-to event + * + * @param kEvent $event + */ + function OnBeforeShutdown(&$event) + { + } + /** * Is called after tree was build (when not from cache) * * @param kEvent $event @@ -249,6 +263,116 @@ } + /** + * Called by AJAX to perform CSV export + * + * @param kEvent $event + */ + function OnExportCSV(&$event) + { + $export_helper =& $this->Application->recallObject('CSVHelper'); + /* @var $export_helper kCSVHelper */ + + $prefix_special = $this->Application->GetVar('PrefixSpecial'); + if(!$prefix_special) { + $prefix_special = $export_helper->ExportData('prefix'); + } + $prefix_elems = split('\.|_', $prefix_special, 2); + $perm_sections = $this->Application->getUnitOption($prefix_elems[0], 'PermSection'); + if(!$this->Application->CheckPermission($perm_sections['main'].'.view')) { + $this->Application->Redirect('no_permission'); + } + + $export_helper->PrefixSpecial = $prefix_special; + $export_helper->grid = $this->Application->GetVar('grid'); + $export_helper->ExportStep(); + $event->status = erSTOP; + } + + /** + * Returning created by AJAX CSV file + * + * @param kEvent $event + */ + function OnGetCSV(&$event) + { + $export_helper =& $this->Application->recallObject('CSVHelper'); + /* @var $export_helper kCSVHelper */ + + $prefix_special = $export_helper->ExportData('prefix'); + $prefix_elems = split('\.|_', $prefix_special, 2); + $perm_sections = $this->Application->getUnitOption($prefix_elems[0], 'PermSection'); + + if(!$this->Application->CheckPermission($perm_sections['main'].'.view')) { + $this->Application->Redirect('no_permission'); + } + + $export_helper->GetCSV(); + } + + /** + * Enter description here... + * + * @param kEvent $event + */ + function OnCSVImportBegin(&$event) + { + $prefix_special = $this->Application->GetVar('PrefixSpecial'); + $prefix_elems = split('\.|_', $prefix_special, 2); + $perm_sections = $this->Application->getUnitOption($prefix_elems[0], 'PermSection'); + + if(!$this->Application->CheckPermission($perm_sections['main'].'.add') && !$this->Application->CheckPermission($perm_sections['main'].'.edit')) { + $this->Application->Redirect('no_permission'); + } + + $object =& $event->getObject( Array('skip_autoload' => true) ); + /* @var $object kDBItem */ + $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); + $field_values = array_shift($items_info); + $object->SetFieldsFromHash($field_values); + + $event->redirect = false; + $result = 'required'; + if($object->GetDBField('ImportFile')) { + $import_helper =& $this->Application->recallObject('CSVHelper'); + /* @var $import_helper kCSVHelper */ + $import_helper->PrefixSpecial = $this->Application->GetVar('PrefixSpecial'); + $import_helper->grid = $this->Application->GetVar('grid'); + $result = $import_helper->ImportStart( $object->GetField('ImportFile', 'file_paths') ); + if($result === true) { + $event->redirect = $this->Application->GetVar('next_template'); + $event->SetRedirectParam('PrefixSpecial', $this->Application->GetVar('PrefixSpecial')); + $event->SetRedirectParam('grid', $this->Application->GetVar('grid')); + } + } + + if($event->redirect === false) { + $object->SetError('ImportFile', $result); + $event->status = erFAIL; + } + } + + /** + * Enter description here... + * + * @param kEvent $event + */ + function OnCSVImportStep(&$event) + { + $import_helper =& $this->Application->recallObject('CSVHelper'); + /* @var $export_helper kCSVHelper */ + + $prefix_special = $import_helper->ImportData('prefix'); + $prefix_elems = split('\.|_', $prefix_special, 2); + $perm_sections = $this->Application->getUnitOption($prefix_elems[0], 'PermSection'); + if(!$this->Application->CheckPermission($perm_sections['main'].'.add') && !$this->Application->CheckPermission($perm_sections['main'].'.edit')) { + $this->Application->Redirect('no_permission'); + } + + $import_helper->ImportStep(); + $event->status = erSTOP; + } + function OnCheckPrefixConfig(&$event) { $prefix = $this->Application->GetVar('config_prefix'); @@ -258,4 +382,69 @@ $event->redirect = false; } + function OnUploadFile(&$event) + { + // Flash uploader does NOT send correct cookies, so we need to make our own check + $cookie_name = 'adm_'.$this->Application->ConfigValue('SessionCookieName'); + $this->Application->HttpQuery->Cookie['cookies_on'] = 1; + $this->Application->HttpQuery->Cookie[$cookie_name] = $this->Application->GetVar('flashsid'); + + $admin_ses =& $this->Application->recallObject('Session.admin'); + /* @var $admin_ses Session */ + $user = $admin_ses->RecallVar('user_id'); + $perm_helper =& $this->Application->recallObject('PermissionsHelper'); + /* @var $perm_helper kPermissionsHelper */ + + /*if() { + $prefix_special = $this->Application->GetVar('PrefixSpecial'); + $prefix_elems = split('\.|_', $prefix_special, 2); + $perm_sections = $this->Application->getUnitOption($prefix_elems[0], 'PermSection'); + $section = $perm_sections['main']; + } + else {*/ + $section = $event->getSection(); + /*}*/ + + if ($this->Application->GetVar('t') != 'import/import_start' && !$perm_helper->CheckUserPermission($user, $section.'.add') && !$perm_helper->CheckUserPermission($user, $section.'.edit')) { + $event->status = erPERM_FAIL; + header('HTTP/1.0 403 You don\'t have permissions to upload'); + exit; + return; + } + + if (!$cookie_name) $cookie_name = 'sid'; + + $value = $this->Application->GetVar('Filedata'); + if (!$value) return ; + $tmp_path = WRITEABLE.'/tmp/'; + $fname = $value['name']; + $id = $this->Application->GetVar('id'); + if ($id) $fname = $id.'_'.$fname; + + if (!is_writable($tmp_path)) { + header('HTTP/1.0 500 Write permissions not set on the server'); + exit; + } + + move_uploaded_file($value['tmp_name'], $tmp_path.$fname); + exit; + } + + function OnDropTempTablesByWID(&$event) + { + $sid = $this->Application->GetSID(); + $wid = $this->Application->GetVar('m_wid'); + $tables = $this->Conn->GetCol('SHOW TABLES'); + $mask_edit_table = '/'.TABLE_PREFIX.'ses_'.$sid.'_'.$wid.'_edit_(.*)$/'; + foreach($tables as $table) + { + if( preg_match($mask_edit_table,$table,$rets) ) + { + $this->Conn->Query('DROP TABLE IF EXISTS '.$table); + } + } + echo 'OK'; + $event->status = erSTOP; + return ; + } } \ No newline at end of file Index: branches/RC/core/admin_templates/users/admins_list.tpl =================================================================== diff -u -N -r8929 -r9639 --- branches/RC/core/admin_templates/users/admins_list.tpl (.../admins_list.tpl) (revision 8929) +++ branches/RC/core/admin_templates/users/admins_list.tpl (.../admins_list.tpl) (revision 9639) @@ -1,5 +1,5 @@ - + Index: branches/RC/core/units/general/helpers/csv_helper.php =================================================================== diff -u -N --- branches/RC/core/units/general/helpers/csv_helper.php (revision 0) +++ branches/RC/core/units/general/helpers/csv_helper.php (revision 9639) @@ -0,0 +1,362 @@ + "\t", 1 => ',', 2 => ';', 3 => ' ', 4 => ':'); + var $enclosure_mapping = Array(0 => '"', 1 => "'"); + var $separator_mapping = Array(0 => "\n", 1 => "\r\n"); + + function ExportStep() + { + $export_data = $this->Application->RecallVar('export_data'); + $export_rand = $this->Application->RecallVar('export_rand'); + $get_rand = $this->Application->GetVar('export_rand'); + + if($export_data && $export_rand == $get_rand) { + $export_data = unserialize($export_data); + $first_step = false; + } + else { + // first step + $export_data = Array(); + $export_data['prefix'] = $this->PrefixSpecial; + $export_data['grid'] = $this->grid; + $export_data['file_name'] = EXPORT_PATH.'/'.$this->ValidateFileName(EXPORT_PATH, 'export_'.$export_data['prefix'].'.csv'); + $export_data['step'] = EXPORT_STEP; + $export_data['delimiter'] = $this->delimiter_mapping[(int)$this->Application->ConfigValue('CSVExportDelimiter')]; + $export_data['enclosure'] = $this->enclosure_mapping[(int)$this->Application->ConfigValue('CSVExportEnclosure')]; + $export_data['record_separator'] = $this->separator_mapping[(int)$this->Application->ConfigValue('CSVExportSeparator')]; + $export_data['page'] = 1; + + $lang_object =& $this->Application->recallObject('lang.current'); + /* @var $lang_object LanguagesItem */ + $export_data['source_encoding'] = strtoupper( $lang_object->GetDBField('Charset') ); + $export_data['encoding'] = $this->Application->ConfigValue('CSVExportEncoding') ? false : 'UTF-16LE'; + + $this->Application->StoreVar('export_rand', $get_rand); + + $first_step = true; + } + + $file = fopen($export_data['file_name'], $first_step ? 'w' : 'a'); + + $prefix_elems = split('\.|_', $export_data['prefix']); + $grids = $this->Application->getUnitOption($prefix_elems[0], 'Grids'); + $grid_config = $grids[ $export_data['grid'] ]['Fields']; + + $list_params = Array('per_page' => $export_data['step'], 'grid' => $export_data['grid']); + $list =& $this->Application->recallObject(rtrim(implode('.', $prefix_elems), '.'), $prefix_elems[0].'_List', $list_params); + /* @var $list kDBList */ + $list->SetPage($export_data['page']); + + $list->Query(); + $list->GoFirst(); + + $picker_helper =& $this->Application->RecallObject('ColumnPickerHelper'); + /* @var $picker_helper kColumnPickerHelper */ + $picker_helper->ApplyPicker(rtrim(implode('.', $prefix_elems), '.'), $grid_config, $export_data['grid']); + + if($first_step) { + // if UTF-16, write Unicode marker + if($export_data['encoding'] == 'UTF-16LE') { + fwrite($file, chr(0xFF).chr(0xFE)); + } + + // inserting header line + $headers = Array(); + foreach($grid_config as $field_name => $field_data) { + $header = $this->Application->Phrase( $field_data['title'] ); + array_push($headers, $header); + } + $csv_line = getcsvline($headers, $export_data['delimiter'], $export_data['enclosure'], $export_data['record_separator']); + if($export_data['encoding']) { + $csv_line = mb_convert_encoding($csv_line, $export_data['encoding'], $export_data['source_encoding']); + } + fwrite($file, $csv_line); + } + + while(!$list->EOL()) + { + $data = Array(); + foreach($grid_config as $field_name => $field_data) { + if(isset($field_data['export_field'])) { + $field_name = $field_data['export_field']; + } + $value = $list->GetField($field_name, isset($field_data['format']) ? $field_data['format'] : null); + $value = str_replace("\r\n", "\n", $value); + $value = str_replace("\r", "\n", $value); + array_push($data, $value); + } + if($export_data['encoding'] == 'UTF-16LE') { + fwrite($file, chr(0xFF).chr(0xFE)); + } + $csv_line = getcsvline($data, $export_data['delimiter'], $export_data['enclosure'], $export_data['record_separator']); + if($export_data['encoding']) { + $csv_line = mb_convert_encoding($csv_line, $export_data['encoding'], $export_data['source_encoding']); + } + fwrite($file, $csv_line); + + $list->GoNext(); + } + + $records_processed = $export_data['page'] * $export_data['step']; + $percent_complete = min(round($records_processed / $list->RecordsCount * 100), 100); + + fclose($file); + + if ($records_processed >= $list->RecordsCount) { + $this->Application->StoreVar('export_data', serialize($export_data)); + $this->Application->Redirect($this->Application->GetVar('finish_template')); + } + + echo $percent_complete; + + $export_data['page']++; + $this->Application->StoreVar('export_data', serialize($export_data)); + } + + function ValidateFileName($path, $name) + { + $parts = pathinfo($name); + $ext = '.'.$parts['extension']; + $filename = substr($parts['basename'], 0, -strlen($ext)); + $new_name = $filename.$ext; + while ( file_exists($path.'/'.$new_name) ) + { + if ( preg_match('/('.preg_quote($filename, '/').'_)([0-9]*)('.preg_quote($ext, '/').')/', $new_name, $regs) ) { + $new_name = $regs[1].($regs[2]+1).$regs[3]; + } + else { + $new_name = $filename.'_1'.$ext; + } + } + return $new_name; + } + + function ExportData($name) + { + $export_data = unserialize($this->Application->RecallVar('export_data')); + return isset($export_data[$name]) ? $export_data[$name] : false; + } + + function GetCSV() + { + safeDefine('DBG_SKIP_REPORTING', 1); + + $export_data = unserialize($this->Application->RecallVar('export_data')); + header('Content-type: text/csv'); + $filename = rtrim(basename($export_data['file_name']), '.csv').'.csv'; + header('Content-Disposition: attachment; filename="'.$filename.'"'); + readfile($export_data['file_name']); + die(); + } + + function ImportStart($filename) + { + if(!file_exists($filename) || !is_file($filename)) return 'cant_open_file'; + + $import_data = Array(); + + $lang_object =& $this->Application->recallObject('lang.current'); + /* @var $lang_object LanguagesItem */ + $import_data['source_encoding'] = strtoupper( $lang_object->GetDBField('Charset') ); + $import_data['encoding'] = $this->Application->ConfigValue('CSVExportEncoding') ? false : 'UTF-16LE'; + $import_data['errors'] = ''; + + // convert file in case of UTF-16LE + if($import_data['source_encoding'] != $import_data['encoding']) { + copy($filename, $filename.'.orginal'); + $file_content = file_get_contents($filename); + $file = fopen($filename, 'w'); + fwrite($file, mb_convert_encoding(str_replace(chr(0xFF).chr(0xFE), '', $file_content), $import_data['source_encoding'], $import_data['encoding'])); + fclose($file); + + } + + $import_data['prefix'] = $this->PrefixSpecial; + $import_data['grid'] = $this->grid; + $import_data['file'] = $filename; + $import_data['total_lines'] = count(file($filename)); + if(!$import_data['total_lines']) $import_data['total_lines'] = 1; + unset($file_content); + $import_data['lines_processed'] = 0; + $import_data['delimiter'] = $this->delimiter_mapping[(int)$this->Application->ConfigValue('CSVExportDelimiter')]; + $import_data['enclosure'] = $this->enclosure_mapping[(int)$this->Application->ConfigValue('CSVExportEnclosure')]; + $import_data['step'] = IMPORT_STEP; + $import_data['not_imported_lines'] = ''; + $import_data['added'] = 0; + $import_data['updated'] = 0; + + $file = fopen($filename, 'r'); + // getting first line for headers + $headers = fgetcsv($file, 8192, $import_data['delimiter'], $import_data['enclosure']); + fclose($file); + + $prefix_elems = split('\.|_', $import_data['prefix']); + $grids = $this->Application->getUnitOption($prefix_elems[0], 'Grids'); + $grid_config = $grids[ $import_data['grid'] ]['Fields']; + + $field_list = Array(); + foreach($grid_config as $field_name => $field_data) { + if(isset($field_data['export_field'])) { + $field_name = $field_data['export_field']; + } + $field_label = $this->Application->Phrase( $field_data['title'] ); + $field_pos = array_search($field_label, $headers); + if($field_pos !== false) { + $field_list[$field_pos] = $field_name; + } + } + + if(!count($field_list)) return 'no_matching_columns'; + $import_data['field_list'] = $field_list; + + // getting key list + $field_positions = Array(); + $config_key_list = $this->Application->getUnitOption($prefix_elems[0], 'ImportKeys'); + if(!$config_key_list) $config_key_list = Array(); + array_unshift($config_key_list, Array($this->Application->getUnitOption($prefix_elems[0], 'IDField'))); + + $key_list = Array(); + foreach($config_key_list as $arr_key => $import_key) { + $key_list[$arr_key] = is_array($import_key) ? $import_key : Array($import_key); + + foreach($key_list[$arr_key] as $key_field) { + $field_positions[$key_field] = array_search($key_field, $import_data['field_list']); + if($field_positions[$key_field] === false) { + // no such key field combination in imported file + unset($key_list[$arr_key]); + break; + } + } + } + $import_data['key_list'] = $key_list; + $import_data['field_positions'] = $field_positions; + + $this->Application->StoreVar('import_data', serialize($import_data)); + return true; + } + + function ImportStep() + { + $import_data = unserialize($this->Application->RecallVar('import_data')); + $prefix_elems = split('\.|_', $import_data['prefix']); + + $object =& $this->Application->recallObject($prefix_elems[0].'.-csvimport', $prefix_elems[0], Array('skip_autoload' => true, 'populate_ml_fields' => true)); + /* @var $object kDBItem */ + + $file = fopen($import_data['file'], 'r'); + $eof = false; + // skipping lines that has been already imported + for($i = 0; $i < $import_data['lines_processed'] + 1; $i++) { + if(feof($file)) break; + fgets($file, 8192); + } + + $import_event = new kEvent($prefix_elems[0].'.-csvimport:OnBeforeCSVLineImport'); + + for($i = 0; $i < $import_data['step']; $i++) { + if(feof($file)) break; + $data = fgetcsv($file, 8192, $import_data['delimiter'], $import_data['enclosure']); + if(!$data) continue; + + $object->Clear(); + $action = 'Create'; + + // 1. trying to load object by keys + foreach($import_data['key_list'] as $key) { + $fail = false; + $key_array = Array(); + foreach($key as $key_field) { + if(!isset($data[ $import_data['field_positions'][$key_field] ])) { + $fail = true; + break; + } + $key_array[$key_field] = $data[ $import_data['field_positions'][$key_field] ]; + } + if($fail) continue; + if($object->Load($key_array)) { + $action = 'Update'; + break; + } + } + + // 2. set object fields + foreach($import_data['field_list'] as $position => $field_name) { + if(isset($data[$position])) { + $object->SetField($field_name, $data[$position]); + } + } + + // 3. validate item and run event + $status = $object->Validate(); + $import_event->status = $status ? erSUCCESS : erFAIL; + $this->Application->HandleEvent($import_event); + + if($import_event->status == erSUCCESS && $object->$action()) { + $import_data[ ($action == 'Create') ? 'added' : 'updated' ]++; + } + else { + $msg = ''; + foreach ($object->FieldErrors as $field => $info) { + if (!$info['pseudo']) continue; + $msg .= "$field: {$info['pseudo']} "; + } + $import_data['errors'] .= ($i + $import_data['lines_processed'] + 1).": $msg\n"; + $import_data['not_imported_lines'] .= ','.($i + $import_data['lines_processed'] + 1); + } + } + + $import_data['lines_processed'] += $import_data['step']; + + $import_data['not_imported_lines'] = ltrim($import_data['not_imported_lines'], ','); + $this->Application->StoreVar('import_data', serialize($import_data)); + + $feof = feof($file); + fclose($file); + + if($feof) { + $this->Application->Redirect($this->Application->GetVar('finish_template')); + } + else { + $percent_complete = floor($import_data['lines_processed'] / $import_data['total_lines'] * 100); + if($percent_complete > 99) $percent_complete = 99; + echo $percent_complete; + } + } + + function ImportData($name) + { + $import_data = unserialize($this->Application->RecallVar('import_data')); + return isset($import_data[$name]) ? $import_data[$name] : false; + } + + function GetNotImportedLines() + { + $import_data = unserialize($this->Application->RecallVar('import_data')); + + if(!$import_data['not_imported_lines']) return false; + $line_numbers = explode(',', $import_data['not_imported_lines']); + $line_numbers[] = 0; // include header row in output + + $file = fopen($import_data['file'], 'r'); + $eof = false; + $result = ''; + for($i = 0; $i <= max($line_numbers); $i++) { + if(feof($file)) break; + $line = fgets($file, 8192); + if(in_array($i, $line_numbers)) { + $result .= $i.':'.$line; + } + } + return $result."\n\n".$import_data['errors']; + } + } + +?> \ No newline at end of file Index: branches/RC/core/kernel/db/dblist.php =================================================================== diff -u -N -r9561 -r9639 --- branches/RC/core/kernel/db/dblist.php (.../dblist.php) (revision 9561) +++ branches/RC/core/kernel/db/dblist.php (.../dblist.php) (revision 9639) @@ -654,7 +654,7 @@ $ret .= $field[0].' '.$field[1].','; } else { - $ret .= '`'.$field[0] . '` ' . $field[1] . ','; + $ret .= (strpos($field[0], '.') === false ? '`'.$field[0] . '`' : $field[0]) . ' ' . $field[1] . ','; } } $ret = rtrim($ret, ','); Index: branches/RC/core/units/general/xml_helper.php =================================================================== diff -u -N -r9313 -r9639 --- branches/RC/core/units/general/xml_helper.php (.../xml_helper.php) (revision 9313) +++ branches/RC/core/units/general/xml_helper.php (.../xml_helper.php) (revision 9639) @@ -201,6 +201,29 @@ return $false; } } + /** + * Reconstructs XML of the node and subnodes + * + */ + function GetXML() + { + $xml = '<'.$this->Name; + if (count($this->Attributes)) { + $xml .= ' '; + $att_contents = array(); + foreach ($this->Attributes as $name => $value) { + $att_contents[] = $name.'="'.$value.'"'; + } + $xml .= implode(' ', $att_contents); + } + $xml .= '>'.$this->Data; + foreach ($this->Children as $node) + { + $xml .= $node->GetXML(); + } + $xml .= 'Name.'>'; + return $xml; + } } ?> \ No newline at end of file Index: branches/RC/core/kernel/constants.php =================================================================== diff -u -N -r9121 -r9639 --- branches/RC/core/kernel/constants.php (.../constants.php) (revision 9121) +++ branches/RC/core/kernel/constants.php (.../constants.php) (revision 9639) @@ -62,4 +62,5 @@ // common usage regular expressions define('REGEX_EMAIL_USER', '[-a-zA-Z0-9!\#$%&*+\/=?^_`{|}~.]+'); define('REGEX_EMAIL_DOMAIN', '[a-zA-Z0-9]{1}[-.a-zA-Z0-9_]*\.[a-zA-Z]{2,6}'); + define('ALLOW_DEFAULT_SETTINGS', '_USE_DEFAULT_USER_DATA_'); //Allow persistent vars to take data from default user's persistent data ?> \ No newline at end of file Index: branches/RC/core/admin_templates/tools/system_tools.tpl =================================================================== diff -u -N -r8953 -r9639 --- branches/RC/core/admin_templates/tools/system_tools.tpl (.../system_tools.tpl) (revision 8953) +++ branches/RC/core/admin_templates/tools/system_tools.tpl (.../system_tools.tpl) (revision 9639) @@ -13,6 +13,14 @@ set_hidden_field('events[adm]', ''); } + function check_prefix_config() { + var $form = document.getElementById($form_name); + openwin('about:blank', 'config_check', 800, 575); + $form.target = 'config_check'; + submit_event('adm', 'OnCheckPrefixConfig'); + set_hidden_field('events[adm]', ''); + } + function compile_templates() { openwin('', 'compile', 800, 575); @@ -48,6 +56,20 @@ + + "> + + + + + + "> Index: branches/RC/core/kernel/application.php =================================================================== diff -u -N -r9389 -r9639 --- branches/RC/core/kernel/application.php (.../application.php) (revision 9389) +++ branches/RC/core/kernel/application.php (.../application.php) (revision 9639) @@ -288,6 +288,9 @@ $this->SetVar('visits_id', $this->RecallVar('visit_id') ); $language =& $this->recallObject( 'lang.current', null, Array('live_table' => true) ); + if (preg_match('/utf-8/', $language->GetDBField('Charset'))) { + setlocale(LC_ALL, 'en_US.UTF-8'); + } $this->ValidateLogin(); @@ -780,6 +783,7 @@ */ function Done() { + $this->HandleEvent( new kEvent('adm:OnBeforeShutdown') ); if ($this->isDebugMode() && constOn('DBG_PROFILE_MEMORY')) { $this->Debugger->appendMemoryUsage('Application before Done:'); } @@ -1628,6 +1632,7 @@ $session =& $this->Application->recallObject('Session'); /* @var $session Session */ + $this->HandleEvent( new kEvent('adm:OnBeforeShutdown') ); $session->SaveData(); exit; } @@ -2424,6 +2429,9 @@ function TimeZoneAdjustment($time_zone = null) { + if ($time_zone == 'GMT') { + return (-1) * adodb_date('Z'); + } $target_zone = isset($time_zone) ? $time_zone : $this->ConfigValue('Config_Site_Time'); return 3600 * ($target_zone - $this->ConfigValue('Config_Server_Time')); } Index: branches/RC/core/units/general/helpers/col_picker_helper.php =================================================================== diff -u -N -r9359 -r9639 --- branches/RC/core/units/general/helpers/col_picker_helper.php (.../col_picker_helper.php) (revision 9359) +++ branches/RC/core/units/general/helpers/col_picker_helper.php (.../col_picker_helper.php) (revision 9639) @@ -7,7 +7,7 @@ function LoadColumns($prefix) { $view_name = $this->Application->RecallVar($prefix.'_current_view'); - $val = $this->Application->RecallPersistentVar($prefix.'_columns_.'.$view_name); + $val = $this->Application->RecallPersistentVar($prefix.'_columns_.'.$view_name, ALLOW_DEFAULT_SETTINGS); if (!$val) { $cols = $this->RebuildColumns($prefix); Index: branches/RC/core/admin_templates/img/toolbar/tool_export_f2.gif =================================================================== diff -u -N Binary files differ Index: branches/RC/core/admin_templates/js/script.js =================================================================== diff -u -N -r9275 -r9639 --- branches/RC/core/admin_templates/js/script.js (.../script.js) (revision 9275) +++ branches/RC/core/admin_templates/js/script.js (.../script.js) (revision 9639) @@ -1,4 +1,5 @@ if ( !( isset($init_made) && $init_made ) ) { + var Application = new kApplication(); var Grids = new Array(); var Toolbars = new Array(); var $Menus = new Array(); @@ -11,6 +12,7 @@ var $env = ''; var submitted = false; + var unload_legal = false; var $edit_mode = false; var $init_made = true; // in case of double inclusion of script.js :) @@ -107,6 +109,9 @@ function submit_event(prefix_special, event, t, form_action, $ajax) { + if (!Application.processHooks(prefix_special + ':' + event)) { + return false; + } if ($ajax) { return $Catalog.submit_event(prefix_special, event, t); } @@ -153,6 +158,7 @@ return; } submitted = true; + unload_legal = true; var $form = document.getElementById($form_name); processHooks('SubmitKF', hBEFORE); @@ -553,6 +559,28 @@ } } +function std_csv_export(prefix_special, grid, template) +{ + set_hidden_field('PrefixSpecial', prefix_special); + set_hidden_field('grid', grid); + if (use_popups(prefix_special, '')) { + open_popup(prefix_special, '', template); + } + else { + submit_event(prefix_special, '', template); + } +} +function std_csv_import(prefix_special, grid, template) +{ + set_hidden_field('PrefixSpecial', prefix_special); + set_hidden_field('grid', grid); + if (use_popups(prefix_special, '')) { + open_popup(prefix_special, '', template); + } + else { + submit_event(prefix_special, '', template); + } +} // set current form base on ajax function set_form($prefix_special, $ajax) { @@ -1407,4 +1435,17 @@ } } } +} +function runOnChange(elId) { + var evt; + var el = typeof(elId) == 'string' ? document.getElementById(elId) : elId + if (document.createEvent) { + evt = document.createEvent("HTMLEvents"); + evt.initEvent("change", true, false); + (evt) ? el.dispatchEvent(evt) : (el.onchange && el.onchange()); + return; + } + if (el.fireEvent) { + el.fireEvent('onchange'); + } } \ No newline at end of file Index: branches/RC/core/admin_templates/img/toolbar/tool_export_f3.gif =================================================================== diff -u -N Binary files differ Index: branches/RC/core/kernel/db/db_event_handler.php =================================================================== diff -u -N -r9618 -r9639 --- branches/RC/core/kernel/db/db_event_handler.php (.../db_event_handler.php) (revision 9618) +++ branches/RC/core/kernel/db/db_event_handler.php (.../db_event_handler.php) (revision 9639) @@ -2172,6 +2172,21 @@ exit; } + /** + * Called from CSV import script after item fields + * are set and validated, but before actual item create/update. + * If event status is erSUCCESS, line will be imported, + * else it will not be imported but added to skipped lines + * and displayed in the end of import. + * Event status is preset from import script. + * + * @param kEvent $event + */ + function OnBeforeCSVLineImport(&$event) + { + // abstract, for hooking + } + } Index: branches/RC/core/install/utf.php =================================================================== diff -u -N -r9259 -r9639 --- branches/RC/core/install/utf.php (.../utf.php) (revision 9259) +++ branches/RC/core/install/utf.php (.../utf.php) (revision 9639) @@ -1,7 +1,7 @@ Conn->Query('SHOW FULL COLUMNS FROM '.$table); foreach ($columns as $a_column) { - if ($a_column['Collation'] != 'NULL') { + if ($a_column['Collation'] && $a_column['Collation'] != 'NULL') { if ($a_column['Collation'] == $target_collation) { echo "skipping ".$a_column['Field'].'
'; continue; Index: branches/RC/core/kernel/utility/temp_handler.php =================================================================== diff -u -N -r8929 -r9639 --- branches/RC/core/kernel/utility/temp_handler.php (.../temp_handler.php) (revision 8929) +++ branches/RC/core/kernel/utility/temp_handler.php (.../temp_handler.php) (revision 9639) @@ -67,8 +67,18 @@ function saveID($prefix, $special = '', $id = null) { + if (!isset($this->savedIDs[$prefix.($special ? '.' : '').$special])) { + $this->savedIDs[$prefix.($special ? '.' : '').$special] = array(); + } + if (is_array($id)) { + foreach ($id as $tmp_id => $live_id) { + $this->savedIDs[$prefix.($special ? '.' : '').$special][$tmp_id] = $live_id; + } + } + else { $this->savedIDs[$prefix.($special ? '.' : '').$special][] = $id; } + } /** * Get temp table name @@ -543,7 +553,7 @@ $this->Conn->Query($query); $insert_id = $id_to_copy == 0 ? $this->Conn->getInsertID() : $id_to_copy; - $this->saveID($master['Prefix'], '', $insert_id); + $this->saveID($master['Prefix'], '', array($id => $insert_id)); $this->RaiseEvent( 'OnAfterCopyToLive', $master['Prefix'], '', Array($insert_id), null, array('temp_id' => $id) ); $this->UpdateForeignKeys($master, $insert_id, $id); @@ -565,12 +575,33 @@ // or in parent table processing for sub-tables $this->RaiseEvent('OnBeforeCopyToLive', $master['Prefix'], '', $current_ids); - // reset ALL negative IDs to 0 so it get inserted into live table with autoincrement + $live_ids = array(); + foreach ($current_ids as $an_id) { + if ($an_id > 0) { + $live_ids[$an_id] = $an_id; + // positive (already live) IDs will be copied in on query all togather below, + // so we just store it here + continue; + } + else { // zero or negaitve ids should be copied one by one to get their InsertId + // reseting to 0 so it get inserted into live table with autoincrement $query = 'UPDATE '.$this->GetTempName($master['TableName']).' SET '.$master['IdField'].' = 0 - WHERE '.$master['IdField'].' < 0'; - if (isset($master['Constrain'])) $query .= ' AND '.$master['Constrain']; + WHERE '.$master['IdField'].' = '.$an_id; + // constrain is not needed here because ID is already unique $this->Conn->Query($query); + // copying + $query = 'INSERT INTO '.$master['TableName'].' + SELECT * FROM '.$this->GetTempName($master['TableName']).' + WHERE '.$master['IdField'].' = 0'; + $this->Conn->Query($query); + $live_ids[$an_id] = $this->Conn->getInsertID(); //storing newly created live id + //delete already copied record from master temp table + $query = 'DELETE FROM '.$this->GetTempName($master['TableName']).' + WHERE '.$master['IdField'].' = 0'; + $this->Conn->Query($query); + } + } // copy ALL records to live table $query = 'INSERT INTO '.$master['TableName'].' @@ -579,15 +610,11 @@ $this->Conn->Query($query); $this->CopiedTables[] = $table_sig; - /* + $this->RaiseEvent('OnAfterCopyToLive', $master['Prefix'], '', $live_ids); - !!! WE NEED TO FIND A WAY TO DETERMINE IF OnAfterCopyToLive is not an empty method, and do on-by-one insert - and pass Ids to OnAfterCopyToLive, otherwise it's not smart to do on-by-one insert for any object - OR WE COULD FIND A WAY TO GET ALL INSERTED IDS as an array and iterate them !!! + $this->saveID($master['Prefix'], '', $live_ids); - $this->RaiseEvent('OnAfterCopyToLive', IDS ??? ); - */ // no need to clear temp table - it will be dropped by next statement } @@ -696,6 +723,9 @@ function PrepareEdit() { $this->DoCopyLiveToTemp($this->Tables, $this->Tables['IDs']); + if ($this->Application->getUnitOption($this->Tables['Prefix'],'CheckSimulatniousEdit')) { + $this->CheckSimultaniousEdit(); + } } function SaveEdit($master_ids = Array()) @@ -713,6 +743,45 @@ } } } + function CheckSimultaniousEdit() + { + $tables = $this->Conn->GetCol('SHOW TABLES'); + $mask_edit_table = '/'.TABLE_PREFIX.'ses_(.*)_edit_'.$this->MasterTable.'$/'; + $sql='SELECT COUNT(*) FROM '.$this->TableName.' WHERE '.$this->IDField.' = \'%s\''; + $my_sid = $this->Application->GetSID(); + $ids = join(',',$this->Tables['IDs']); + $sids = array(); + if (!$ids) return ; + foreach($tables as $table) + { + if( preg_match($mask_edit_table,$table,$rets) ) + { + $sid = preg_replace('/(.*)_(.*)/', '\\1', $rets[1]); // remove popup's wid from sid + if ($sid == $my_sid) continue; + $found = $this->Conn->GetOne("SELECT COUNT({$this->Tables['IdField']}) FROM $table WHERE {$this->Tables['IdField']} IN ($ids)"); + if (!$found || in_array($sid, $sids)) continue; + $sids[] = $sid; + } + } + if ($sids) { + //detect who is it + $users = $this->Conn->GetCol( + ' SELECT + CONCAT(IF (s.PortalUserId = -1, \'root\', + IF (s.PortalUserId = -2, \'Guest\', + CONCAT(FirstName, \' \', LastName, \' (\', Login, \')\') + ) + ), \' IP: \', s.IpAddress, \'\') FROM '.TABLE_PREFIX.'UserSession AS s + LEFT JOIN '.TABLE_PREFIX.'PortalUser AS u + ON u.PortalUserId = s.PortalUserId + WHERE s.SessionKey IN ('.join(',',$sids).')'); + if ($users) { + $this->Application->SetVar('_simultanious_edit_message', + sprintf($this->Application->Phrase('la_record_being_edited_by'), join(",\n", $users)) + ); + } + } + } } ?> \ No newline at end of file Index: branches/RC/core/kernel/utility/debugger.php =================================================================== diff -u -N -r8929 -r9639 --- branches/RC/core/kernel/utility/debugger.php (.../debugger.php) (revision 8929) +++ branches/RC/core/kernel/utility/debugger.php (.../debugger.php) (revision 9639) @@ -584,7 +584,8 @@ */ function isGecko() { - return strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'firefox') !== false; + // we need isset because we may run scripts from shell with no user_agent at all + return isset($_SERVER['HTTP_USER_AGENT']) && strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'firefox') !== false; } /** Index: branches/RC/core/kernel/db/db_tag_processor.php =================================================================== diff -u -N -r9616 -r9639 --- branches/RC/core/kernel/db/db_tag_processor.php (.../db_tag_processor.php) (revision 9616) +++ branches/RC/core/kernel/db/db_tag_processor.php (.../db_tag_processor.php) (revision 9639) @@ -648,7 +648,7 @@ if($first_chars) { $needs_cut = strlen($value) > $first_chars; - $value = substr($value,0,$first_chars); + $value = extension_loaded('mbstring') ? mb_substr($value,0,$first_chars) : substr($value,0,$first_chars); if($needs_cut) $value .= ' ...'; } if( getArrayValue($params,'nl2br' ) ) $value = nl2br($value); @@ -815,6 +815,9 @@ else { // single selection radio or checkboxes + if (!isset($options['options']) || !is_array($options['options'])) { + trigger_error("Options not defined for {$object->Prefix} field $field", E_USER_WARNING); + } foreach ($options['options'] as $key => $val) { $block_params['key'] = $key; @@ -836,7 +839,7 @@ $object->SetDBField($field, $this->SearchField($params)); $view_name = $this->Application->RecallVar($this->getPrefixSpecial().'_current_view'); - $custom_filter = $this->Application->RecallPersistentVar($this->getPrefixSpecial().'_custom_filter.'.$view_name); + $custom_filter = $this->Application->RecallPersistentVar($this->getPrefixSpecial().'_custom_filter.'.$view_name, ALLOW_DEFAULT_SETTINGS); $ret = $this->PredefinedOptions($params); $object->SetDBField($field, $saved_value); @@ -1632,7 +1635,7 @@ $field = $this->SelectParam($params, 'field,name'); $view_name = $this->Application->RecallVar($this->getPrefixSpecial().'_current_view'); - $custom_filter = $this->Application->RecallPersistentVar($this->getPrefixSpecial().'_custom_filter.'.$view_name); + $custom_filter = $this->Application->RecallPersistentVar($this->getPrefixSpecial().'_custom_filter.'.$view_name, ALLOW_DEFAULT_SETTINGS); $custom_filter = $custom_filter ? unserialize($custom_filter) : Array(); if (isset($custom_filter[ $params['grid'] ][$field])) { @@ -1953,6 +1956,13 @@ return $this->Prefix == $this->Application->GetTopmostPrefix($this->Prefix); } + function PermSection($params) + { + $section = $this->SelectParam($params, 'section,name'); + $perm_sections = $this->Application->getUnitOption($this->Prefix, 'PermSection'); + return isset($perm_sections[$section]) ? $perm_sections[$section] : ''; + } + function PerPageSelected($params) { $list =& $this->GetList($params); Index: branches/RC/core/kernel/session/session.php =================================================================== diff -u -N -r9286 -r9639 --- branches/RC/core/kernel/session/session.php (.../session.php) (revision 9286) +++ branches/RC/core/kernel/session/session.php (.../session.php) (revision 9639) @@ -41,7 +41,7 @@ Usage: -$session = new Session(smAUTO); //smAUTO is default, you could just leave the brackets empty, or provide another mode +$session =& new Session(smAUTO); //smAUTO is default, you could just leave the brackets empty, or provide another mode $session->SetCookieDomain('my.domain.com'); $session->SetCookiePath('/myscript'); @@ -302,7 +302,23 @@ function RecallPersistentVar(&$session, $var_name, $default = false) { - return isset($this->PersistentVars[$var_name]) ? $this->PersistentVars[$var_name] : $default; + if (isset($this->PersistentVars[$var_name])) { + return $this->PersistentVars[$var_name]; + } + elseif ($default == '_USE_DEFAULT_USER_DATA_') { + $default_user_id = $this->Application->ConfigValue('DefaultSettingsUserId'); + if (!$default_user_id) $default_user_id = -1; + $sql = 'SELECT VariableValue, VariableName + FROM '.TABLE_PREFIX.'PersistantSessionData + WHERE VariableName = '.$this->Conn->qstr($var_name).' AND PortalUserId = '.$default_user_id; + $value = $this->Conn->GetOne($sql); + if ($value !== false) { + $this->PersistentVars[$var_name] = $value; + $this->StorePersistentVar($session, $var_name, $value); //storing it, so next time we don't load default user setting + } + return $value; + } + else return $default; } function RemovePersistentVar(&$session, $var_name) @@ -402,7 +418,7 @@ $this->Checkers = Array(); $this->InitStorage($special); - $this->Data = new Params(); + $this->Data =& new Params(); $tmp_sid = $this->GetPassedSIDValue(); @@ -718,7 +734,7 @@ function Destroy() { $this->Storage->DeleteSession($this); - $this->Data = new Params(); + $this->Data =& new Params(); $this->SID = ''; if ($this->CookiesEnabled) $this->SetSessionCookie(); //will remove the cookie due to value (sid) is empty $this->SetSession(); //will create a new session Index: branches/RC/core/admin_templates/js/ajax.js =================================================================== diff -u -N -r9603 -r9639 --- branches/RC/core/admin_templates/js/ajax.js (.../ajax.js) (revision 9603) +++ branches/RC/core/admin_templates/js/ajax.js (.../ajax.js) (revision 9639) @@ -75,6 +75,15 @@ } } +Request.processRedirect = function($request) { + var $match_redirect = new RegExp('^#redirect#(.*)').exec($request.responseText); + if ($match_redirect != null) { + // redirect to external template requested + window.location.href = $match_redirect[1]; + return true; + } + return false; +} Request.sendHeaders = function($request) { for (var $header_name in Request.headers) { if (typeof Request.headers[$header_name] == 'function') { @@ -325,4 +334,110 @@ AjaxPopupManager.prototype.errorCallback = function($request, $params, $object) { alert('AJAX Error; class: AjaxPopupManager; ' + Request.getErrorHtml($request)); +} +// AJAX DropdownPreloader class +function AjaxDropdownPreloader($url, $input_mask, $filter_field, $dependend_field, value) { + this.URL = $url; + this.InputMask = $input_mask; + this.FilterField = $filter_field; + this.DependendField = $dependend_field; + this.Titles = this.prepareTitles(); + this.Value = value; + this.BusyRequest = false; +} +AjaxDropdownPreloader.prototype.prepareURL = function() +{ + return this.URL.replace('#DEPENDEND#', this.Dependend).replace('#FILTER_VALUE#', this.getValue(this.FilterField)); +} +AjaxDropdownPreloader.prototype.prepareTitles = function() { + var $control = this.getControl(this.DependendField); + var $i = 0; + var $ret = new Array (); + while ($i < $control.options.length) { + $ret[$control.options[$i].value] = $control.options[$i].innerHTML; + $i++; + } + return $ret; +} +AjaxDropdownPreloader.prototype.getValue = function($field_name) { + var $control = this.getControl($field_name); + if ($control.tagName == 'INPUT') return $control.value; + return $control.selectedIndex > 0 ? $control.options[$control.selectedIndex].value : ''; +} +AjaxDropdownPreloader.prototype.Query = function () { + var $url = this.prepareURL(); + var $selected_value = this.Value || this.getValue(this.DependendField); + // remove all existing options + this.removeOptions(); + Request.makeRequest($url, this.BusyRequest, '', this.successCallback, this.errorCallback, $selected_value, this); +} +AjaxDropdownPreloader.prototype.getControl = function($field) { + var $id = this.InputMask.replace('#FIELD#', $field); + return document.getElementById($id); +} +AjaxDropdownPreloader.prototype.successCallback = function($request, $params, $object) { + if (Request.processRedirect($request) === true) { + return ; + } + var control = $object.getControl($object.DependendField) + $object.ProcessXMLNode($request.responseXML, control, $params); + runOnChange(control); + $object.AfterProcess(); +} +AjaxDropdownPreloader.prototype.ProcessXMLNode = function($node, $dst_field, $selected_value) { + for (var i = 0; i < $node.childNodes.length; i++) { + var $child = $node.childNodes.item(i); + switch ($child.tagName) { + case 'option': + var opt_value = $child.getAttribute('value'); + var title; + if (opt_value) { // value is passed explicically + title = $child.firstChild.nodeValue + } + else { + opt_value = $child.firstChild.nodeValue; + title = this.Titles[$child.firstChild.nodeValue]; + } + this.addOption($dst_field, opt_value, title, $child.attributes); + if (opt_value == $selected_value) { + $dst_field.options[$dst_field.options.length - 1].selected = true; + } + break; + case 'field_options': + this.addOption($dst_field, '', ''); + // add new states + this.ProcessXMLNode($child, $dst_field, $selected_value); + if ($dst_field.options.length == 0 || $dst_field.options.length == 2) { + $dst_field.value = $dst_field.options[$dst_field.options.length - 1].value; + } + break; + } + } +} +AjaxDropdownPreloader.prototype.AfterProcess = function() +{ +} +AjaxDropdownPreloader.prototype.removeOptions = function($object) { + if (!$object) $object = this.getControl(this.DependendField); + if ($object.options.length > 0) { + while ($object.options.length > 0) { + $object.remove(0); + } + } +} +AjaxDropdownPreloader.prototype.addOption = function($object, $value, $title, attributes) { + var $option = document.createElement('OPTION'); + $object.options.add($option, $object.options.length); + $option.innerText = $title; + $option.innerHTML = $title; + $option.value = $value; + if (attributes) { + for (var i=0; i
+ Prefix: + + + + Unit config prefix +