Index: branches/RC/themes/default2007/platform/img/delete_button_background.gif =================================================================== diff -u -r9010 -r9021 Binary files differ Index: branches/RC/themes/default2007/platform/elements/forms.tpl =================================================================== diff -u -r8929 -r9021 --- branches/RC/themes/default2007/platform/elements/forms.tpl (.../forms.tpl) (revision 8929) +++ branches/RC/themes/default2007/platform/elements/forms.tpl (.../forms.tpl) (revision 9021) @@ -156,7 +156,7 @@ - " value="" /> + " id="" value="" /> @@ -288,4 +288,41 @@ + + + + + + "> + + + +
+
+ + + " onclick="$ItemCategories.AddCategory('» ', $delete_button);"/>
+ + + + + + + +
+ : +
+ + + + + + +
\ No newline at end of file Index: branches/RC/core/units/general/cat_tag_processor.php =================================================================== diff -u -r9001 -r9021 --- branches/RC/core/units/general/cat_tag_processor.php (.../cat_tag_processor.php) (revision 9001) +++ branches/RC/core/units/general/cat_tag_processor.php (.../cat_tag_processor.php) (revision 9021) @@ -527,6 +527,85 @@ $today_only = isset($params['today']) && $params['today']; return $count_helper->ItemCount($this->Prefix, $today_only); } + + function CategorySelector($params) + { + $category_id = isset($params['category_id']) && is_numeric($params['category_id']) ? $params['category_id'] : false; + if ($category_id === false) { + // if category id not given use module root category + $category_id = $this->Application->findModule('Var', $this->Prefix, 'RootCat'); + } + + $id_field = $this->Application->getUnitOption('c', 'IDField'); + $title_field = $this->Application->getUnitOption('c', 'TitleField'); + $table_name = $this->Application->getUnitOption('c', 'TableName'); + + $count_helper =& $this->Application->recallObject('CountHelper'); + /* @var $count_helper kCountHelper */ + + list ($view_perm, $view_filter) = $count_helper->GetPermissionClause('c', 'perm_cache'); + + // get category list (permission based) + $sql = 'SELECT c.'.$title_field.', c.'.$id_field.' + FROM '.$table_name.' c + INNER JOIN '.TABLE_PREFIX.'PermCache perm_cache ON c.CategoryId = perm_cache.CategoryId + WHERE (ParentId = '.$category_id.') AND ('.$view_filter.') AND (perm_cache.PermId = '.$view_perm.') AND (c.Status = '.STATUS_ACTIVE.') + ORDER BY c.'.$title_field.' ASC'; + $categories = $this->Conn->GetCol($sql, $id_field); + + $block_params = $this->prepareTagParams($params); + $block_params['name'] = $params['render_as']; + $block_params['strip_nl'] = 2; + + $ret = ''; + foreach ($categories as $category_id => $category_name) { + // print category + $block_params['separator'] = isset($params['category_id']) ? $params['separator'] : ''; // return original separator, remove separator for top level categories + $block_params['category_id'] = $category_id; + $block_params['category_name'] = $category_name; + $ret .= $this->Application->ParseBlock($block_params); + + // print it's children + $block_params['separator'] = '   '.$params['separator']; + $ret .= $this->CategorySelector($block_params); + } + + return $ret; + } + + function PrintMoreCategories($params) + { + $object =& $this->getObject(); + /* @var $object kDBItem */ + + $category_ids = $this->Field($params); + if (!$category_ids) { + return ''; + } + + $category_ids = explode('|', substr($category_ids, 1, -1)); + + $id_field = $this->Application->getUnitOption('c', 'IDField'); + $title_field = $this->Application->getUnitOption('c', 'TitleField'); + $table_name = $this->Application->getUnitOption('c', 'TableName'); + + $sql = 'SELECT '.$title_field.', '.$id_field.' + FROM '.$table_name.' + WHERE '.$id_field.' IN ('.implode(',', $category_ids).')'; + $categories = $this->Conn->GetCol($sql, $id_field); + + $block_params = $this->prepareTagParams($params); + $block_params['name'] = $params['render_as']; + + $ret = ''; + foreach ($categories as $category_id => $category_name) { + $block_params['category_id'] = $category_id; + $block_params['category_name'] = $category_name; + $ret .= $this->Application->ParseBlock($block_params); + } + + return $ret; + } } ?> \ No newline at end of file Index: branches/RC/themes/default2007/platform/img/delete_button_background_1.gif =================================================================== diff -u -r9010 -r9021 Binary files differ Index: branches/RC/core/units/general/cat_dbitem.php =================================================================== diff -u -r8929 -r9021 --- branches/RC/core/units/general/cat_dbitem.php (.../cat_dbitem.php) (revision 8929) +++ branches/RC/core/units/general/cat_dbitem.php (.../cat_dbitem.php) (revision 9021) @@ -65,19 +65,17 @@ $ret = parent::Create(); if ($ret) { - $primary_category = $this->GetDBField('CategoryId') > 0 ? $this->GetDBField('CategoryId') : $this->Application->GetVar('m_cat_id'); - $fields_hash = Array( - 'CategoryId' => $primary_category, - 'ItemResourceId' => $this->GetField('ResourceId'), - 'PrimaryCat' => 1, - 'ItemPrefix' => $this->Prefix, - 'Filename' => $this->GetDBField('Filename'), - ); - $this->Conn->doInsert($fields_hash, $this->CategoryItemsTable()); + $this->assignPrimaryCategory(); } return $ret; } + function assignPrimaryCategory() + { + $primary_category = $this->GetDBField('CategoryId') > 0 ? $this->GetDBField('CategoryId') : $this->Application->GetVar('m_cat_id'); + $this->assignToCategory($primary_category, true); + } + function Update($id=null, $system_update=false) { $this->VirtualFields['ResourceId'] = Array(); @@ -319,7 +317,7 @@ */ function assignToCategory($category_id, $is_primary = false) { - $table = $this->mode == 't' ? $this->Application->GetTempName(TABLE_PREFIX.'CategoryItems', 'prefix:'.$this->Prefix) : TABLE_PREFIX.'CategoryItems'; + $table = $this->CategoryItemsTable(); $key_clause = '(ItemResourceId = '.$this->GetDBField('ResourceId').')'; // get all cateories, where item is in @@ -357,9 +355,14 @@ $this->Conn->Query($sql); } else { - $sql = 'INSERT INTO '.$table.' (CategoryId,ItemResourceId,PrimaryCat,ItemPrefix,Filename) VALUES (%s,%s,%s,%s,%s)'; - $filename = $this->useFilenames ? $this->GetDBField('Filename') : ''; // because some prefixes does not use filenames - $this->Conn->Query( sprintf($sql, $category_id, $this->GetDBField('ResourceId'), $is_primary ? 1 : 0, $this->Conn->qstr($this->Prefix), $this->Conn->qstr($filename)) ); + $fields_hash = Array( + 'CategoryId' => $category_id, + 'ItemResourceId' => $this->GetField('ResourceId'), + 'PrimaryCat' => $is_primary ? 1 : 0, + 'ItemPrefix' => $this->Prefix, + 'Filename' => $this->useFilenames ? $this->GetDBField('Filename') : '', // because some prefixes does not use filenames, + ); + $this->Conn->doInsert($fields_hash, $table); } // to ensure filename update after adding to another category // this is critical since there may be an item with same filename in newly added category! @@ -499,6 +502,33 @@ } } + /** + * Returns part of SQL WHERE clause identifing the record, ex. id = 25 + * + * @access public + * @param string $method Child class may want to know who called GetKeyClause, Load(), Update(), Delete() send its names as method + * @param Array $keys_hash alternative, then item id, keys hash to load item by + * @return void + * @see kDBItem::Load() + * @see kDBItem::Update() + * @see kDBItem::Delete() + */ + function GetKeyClause($method = null, $keys_hash = null) + { + if ($method == 'load') { + // for item with many categories makes primary to load + $ci_table = TABLE_PREFIX.'CategoryItems'; + if ($this->IsTempTable()) { + $ci_table = $this->Application->GetTempName($ci_table, 'prefix:'.$this->Prefix); + } + $keys_hash = Array( + $this->IDField => $this->ID, + '`'.$ci_table.'`.`PrimaryCat`' => 1, + ); + } + return parent::GetKeyClause($method, $keys_hash); + } + } ?> \ No newline at end of file Index: branches/RC/themes/default2007/platform/inc/script.js =================================================================== diff -u -r8929 -r9021 --- branches/RC/themes/default2007/platform/inc/script.js (.../script.js) (revision 8929) +++ branches/RC/themes/default2007/platform/inc/script.js (.../script.js) (revision 9021) @@ -1,3 +1,5 @@ +String.prototype.trim = function() { return this.replace(/^\s+|\s+$/, ''); }; + function update_checkbox(cb, cb_hidden) { cb_hidden.value = cb.checked ? 1 : 0; } @@ -9,4 +11,83 @@ function open_window($url, $window_name, $width, $height) { window.open($url, $window_name, 'width='+$width+',height='+$height+',resizable=yes'); return false; +} + +// ItemCategories class +function ItemCategories($table_id, $field_id, $primary_category) { + this.CategoryTable = document.getElementById($table_id); + this.CategorySelector = document.getElementById($field_id + '_select'); + this.MoreCategoriesField = document.getElementById($field_id); + this.PrimaryCategory = $primary_category; + + // get additional categories from item + if (this.MoreCategoriesField.value.length) { + this.MoreCategories = this.MoreCategoriesField.value; + this.MoreCategories = this.MoreCategories.substring(1, this.MoreCategories.length - 1).split('|'); + } + else { + this.MoreCategories = new Array (); + } +} + +ItemCategories.prototype.AddCategory = function($separator, $delete_button) { + var $category_id = this.CategorySelector.options[this.CategorySelector.selectedIndex].value; + var $category_name = this.CategorySelector.options[this.CategorySelector.selectedIndex].innerHTML.trim(); + + if ((this.SearchCategory($category_id) !== false) || ($category_id == this.PrimaryCategory) || ($category_id == 0)) { + // don't add same category twice & don't allow to add item's primary category + return ; + } + + // strip trailing HTML spaces & separator + var $separator_pos = $category_name.indexOf($separator); + if ($separator_pos != -1) { + $category_name = $category_name.substring($separator_pos + $separator.length); + } + + + var $row = this.CategoryTable.insertRow(-1); + $row.id = 'category_' + $category_id; + + var $cell = $row.insertCell(-1); + $cell.innerHTML = $category_name; + + $cell = $row.insertCell(-1); + $cell.innerHTML = $delete_button.replace(/#CATEGORY_ID#/g, $category_id); + + this.MoreCategories.push($category_id); + this.updateMoreCategoriesField(); +} + +ItemCategories.prototype.SearchCategory = function($category_id) { + var $i = 0; + while ($i < this.CategoryTable.rows.length) { + if (this.CategoryTable.rows[$i].id == 'category_' + $category_id) { + return $i; + } + $i++; + } + + return false; +} + +ItemCategories.prototype.DeleteCategory = function($category_id) { + var $row_index = this.SearchCategory($category_id); + + if ($row_index !== false) { + this.CategoryTable.deleteRow($row_index); + var $i = 0; + while ($i < this.MoreCategories.length) { + if (this.MoreCategories[$i] == $category_id) { + this.MoreCategories.splice($i, 1); + break; + } + $i++; + } + this.updateMoreCategoriesField(); + } +} + +ItemCategories.prototype.updateMoreCategoriesField = function() { + this.MoreCategoriesField.value = this.MoreCategories.length ? '|' + this.MoreCategories.join('|') + '|' : ''; } \ No newline at end of file Index: branches/RC/core/units/general/cat_event_handler.php =================================================================== diff -u -r9001 -r9021 --- branches/RC/core/units/general/cat_event_handler.php (.../cat_event_handler.php) (revision 9001) +++ branches/RC/core/units/general/cat_event_handler.php (.../cat_event_handler.php) (revision 9021) @@ -700,6 +700,10 @@ /* @var $image_helper ImageHelper */ $image_helper->LoadItemImages($object); + + // set item's additional categories to virtual field (used in editing) + $item_categories = $this->getItemCategories($object->GetDBField('ResourceId')); + $object->SetDBField('MoreCategories', $item_categories ? '|'.implode('|', $item_categories).'|' : ''); } function OnAfterItemUpdate(&$event) @@ -719,6 +723,11 @@ // process image upload in virtual fields $image_helper->SaveItemImages($object); + + if ($event->Special != '-item') { + // don't touch categories during cloning + $this->processAdditionalCategories($object, 'update'); + } } } @@ -742,6 +751,11 @@ // process image upload in virtual fields $image_helper->SaveItemImages($object); + + if ($event->Special != '-item') { + // don't touch categories during cloning + $this->processAdditionalCategories($object, 'create'); + } } } @@ -1865,7 +1879,59 @@ $this->SetFrontRedirectTemplate($event, 'suggest'); } - /** + /** + * Returns item's categories (allows to exclude primary category) + * + * @param int $resource_id + * @param bool $with_primary + * @return Array + */ + function getItemCategories($resource_id, $with_primary = false) + { + $sql = 'SELECT CategoryId + FROM '.TABLE_PREFIX.'CategoryItems + WHERE (ItemResourceId = '.$resource_id.')'; + + if (!$with_primary) { + $sql .= ' AND (PrimaryCat = 0)'; + } + + return $this->Conn->GetCol($sql); + } + + /** + * Adds new and removes old additional categories from category item + * + * @param kCatDBItem $object + */ + function processAdditionalCategories(&$object, $mode) + { + $process_categories = $object->GetDBField('MoreCategories'); + if ($process_categories === '') { + // field was not in submit & have default value (when no categories submitted, then value is null) + return ; + } + + if ($mode == 'create') { + // prevents first additional category to become primary + $object->assignPrimaryCategory(); + } + + $process_categories = $process_categories ? explode('|', substr($process_categories, 1, -1)) : Array (); + $existing_categories = $this->getItemCategories($object->GetDBField('ResourceId')); + + $add_categories = array_diff($process_categories, $existing_categories); + foreach ($add_categories as $category_id) { + $object->assignToCategory($category_id); + } + + $remove_categories = array_diff($existing_categories, $process_categories); + foreach ($remove_categories as $category_id) { + $object->removeFromCategory($category_id); + } + } + + /** * Creates category item & redirects to confirmation template (front-end only) * * @param kEvent $event @@ -1899,7 +1965,7 @@ $object->Load($id); $edit_perm = $perm_helper->ModifyCheckPermission($object->GetDBField($owner_field), $object->GetDBField('CategoryId'), $event->Prefix); - if ($use_pending && !$object->GetDBField('OrgId') && ($edit_perm == STATUS_PENDING)) { + if ($use_pending && !$object->GetDBField('OrgId') && ($edit_perm == STATUS_PENDING)) { // pending editing enabled + not pending copy -> get/create pending copy & save changes to it $original_id = $object->GetID(); $original_resource_id = $object->GetDBField('ResourceId'); @@ -1937,11 +2003,11 @@ // update id in request (used for redirect in mod-rewrite mode) $this->Application->SetVar($event->getPrefixSpecial().'_id', $object->GetID()); - } - else { - // 3. already editing pending copy -> just update field values - $object->SetFieldsFromHash($field_values); - } + } + else { + // 3. already editing pending copy -> just update field values + $object->SetFieldsFromHash($field_values); + } if ($object->Update()) { $event->status = erSUCCESS; Index: branches/RC/core/admin_templates/js/ajax.js =================================================================== diff -u -r8929 -r9021 --- branches/RC/core/admin_templates/js/ajax.js (.../ajax.js) (revision 8929) +++ branches/RC/core/admin_templates/js/ajax.js (.../ajax.js) (revision 9021) @@ -200,7 +200,7 @@ return queryString; }; -// AJAX ProgressBar classs +// AJAX ProgressBar class function AjaxProgressBar($url) { this.WindowTitle = this.GetWindow().document.title; this.URL = $url; Index: branches/RC/themes/default2007/platform/designs/categories.tpl =================================================================== diff -u -r8929 -r9021 --- branches/RC/themes/default2007/platform/designs/categories.tpl (.../categories.tpl) (revision 8929) +++ branches/RC/themes/default2007/platform/designs/categories.tpl (.../categories.tpl) (revision 9021) @@ -31,3 +31,19 @@ + + + + + + "> + + + + + " onclick="$ItemCategories.DeleteCategory();"/> + + + \ No newline at end of file Index: branches/RC/themes/default2007/platform/inc/styles.css =================================================================== diff -u -r8929 -r9021 --- branches/RC/themes/default2007/platform/inc/styles.css (.../styles.css) (revision 8929) +++ branches/RC/themes/default2007/platform/inc/styles.css (.../styles.css) (revision 9021) @@ -173,6 +173,15 @@ padding: 0px 5px; } +.delete-button { + background: url(../img/delete_button_background.gif) bottom repeat-x #FF6D6D; + color: #FFFFFF; + border: 1px solid #CC0000; + padding: 0px; + font-size: 11px; + padding: 0px 5px; +} + /* --- Form Field Styles --- */ .field-name { padding: 5px 15px; @@ -206,6 +215,9 @@ font-size: 16px; } +.item-categories td { + padding: 1px; +} /* --- Login Sidebox Styles --- */ .login-status, .login-status a { font-size: 10px;