+
+
+
">
+
+
+
+
+
+ " id="_select" style="">
+
+
+
+
+ " 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 @@
+