CategoryPath = Array(); } /** * Set's prefix and special * * @param string $prefix * @param string $special * @access public */ function Init($prefix, $special) { parent::Init($prefix, $special); $this->usePendingEditing = $this->getUnitConfig()->getUsePendingEditing(); } /** * Assigns primary category for the item * * @access public */ public function assignPrimaryCategory() { if ( $this->GetDBField('CategoryId') <= 0 ) { // set primary category in item object $this->SetDBField('CategoryId', $this->Application->GetVar('m_cat_id')); } $this->assignToCategory($this->GetDBField('CategoryId'), true); } /** * Updates previously loaded record with current item' values * * @access public * @param int $id Primary Key Id to update * @param Array $update_fields * @param bool $system_update * @return bool * @access public */ public function Update($id = null, $update_fields = null, $system_update = false) { if ( $this->useFilenames ) { $this->checkFilename(); $this->generateFilename(); } $ret = parent::Update($id, $update_fields, $system_update); if ( $ret ) { $filename = $this->useFilenames ? (string)$this->GetDBField('Filename') : ''; $sql = 'UPDATE ' . $this->CategoryItemsTable() . ' SET Filename = ' . $this->Conn->qstr($filename) . ' WHERE ItemResourceId = ' . $this->GetDBField('ResourceId'); $this->Conn->Query($sql); } return $ret; } /** * Returns CategoryItems table based on current item mode (temp/live) * * @return string */ function CategoryItemsTable() { $table = TABLE_PREFIX.'CategoryItems'; if ($this->Application->IsTempTable($this->TableName)) { $table = $this->Application->GetTempName($table, 'prefix:'.$this->Prefix); } return $table; } function checkFilename() { if( !$this->GetDBField('AutomaticFilename') ) { $filename = $this->GetDBField('Filename'); $this->SetDBField('Filename', $this->stripDisallowed($filename) ); } } function Copy($cat_id=null) { if (!isset($cat_id)) $cat_id = $this->Application->GetVar('m_cat_id'); $this->NameCopy($cat_id); return $this->Create($cat_id); } /** * Sets new name for item in case if it is being copied in same table * * @param array $master Table data from TempHandler * @param int $foreign_key ForeignKey value to filter name check query by * @param string $title_field FieldName to alter, by default - TitleField of the prefix * @param string $format sprintf-style format of renaming pattern, by default Copy %1$s of %2$s which makes it Copy [Number] of Original Name * @access public */ public function NameCopy($master=null, $foreign_key=null, $title_field=null, $format='Copy %1$s of %2$s') { $title_field = $this->getUnitConfig()->getTitleField(); if (!$title_field) return; $new_name = $this->GetDBField($title_field); $cat_id = (int)$this->Application->GetVar('m_cat_id'); $original_checked = false; do { if ( preg_match('/Copy ([0-9]*) *of (.*)/', $new_name, $regs) ) { $new_name = 'Copy '.( (int)$regs[1] + 1 ).' of '.$regs[2]; } elseif ($original_checked) { $new_name = 'Copy of '.$new_name; } $query = 'SELECT '.$title_field.' FROM '.$this->TableName.' LEFT JOIN '.TABLE_PREFIX.'CategoryItems ON ('.TABLE_PREFIX.'CategoryItems.ItemResourceId = '.$this->TableName.'.ResourceId) WHERE ('.TABLE_PREFIX.'CategoryItems.CategoryId = '.$cat_id.') AND '. $title_field.' = '.$this->Conn->qstr($new_name); $res = $this->Conn->GetOne($query); $original_checked = true; } while ($res !== false); $this->SetDBField($title_field, $new_name); // this is needed, because Create will create items in its own CategoryId (if it's set), // but we need to create it in target Paste category @see{kCatDBItem::Create} and its primary_category detection $this->SetDBField('CategoryId', $cat_id); } /** * Changes item primary category to given/current category * * @param int $category_id */ function MoveToCat($category_id = null) { // $this->NameCopy(); if (!isset($category_id)) { $category_id = $this->Application->GetVar('m_cat_id'); } $table_name = TABLE_PREFIX . 'CategoryItems'; if ($this->IsTempTable()) { $table_name = $this->Application->GetTempName($table_name, 'prefix:' . $this->Prefix); } // check if the item already exists in destination category $sql = 'SELECT PrimaryCat FROM ' . $table_name . ' WHERE (CategoryId = ' . (int)$category_id . ') AND (ItemResourceId = ' . $this->GetDBField('ResourceId') . ')'; $is_primary = $this->Conn->GetOne($sql); // if it's not found is_primary will be FALSE, if it's found but not primary it will be int 0 $exists = $is_primary !== false; if ($exists) { // if the item already exists in destination category if ($is_primary) { // do nothing when we paste to primary return ; } // if it's not primary - delete it from destination category, as we will move it from current primary below $sql = 'DELETE FROM ' . $table_name . ' WHERE (CategoryId = ' . (int)$category_id . ') AND (ItemResourceId = ' . $this->GetDBField('ResourceId') . ')'; $this->Conn->Query($sql); } // change category id in existing primary category record $sql = 'UPDATE ' . $table_name . ' SET CategoryId = ' . (int)$category_id . ' WHERE (ItemResourceId = ' . $this->GetDBField('ResourceId') . ') AND (PrimaryCat = 1)'; $this->Conn->Query($sql); $this->Update(); } /** * When item is deleted, then also delete it from all categories * * @param int $id * @return bool * @access public */ public function Delete($id = null) { if ( isset($id) ) { $this->setID($id); } $this->Load($this->GetID()); $ret = parent::Delete(); if ( $ret ) { // TODO: move to OnAfterItemDelete method $query = ' DELETE FROM ' . $this->CategoryItemsTable() . ' WHERE ItemResourceId = ' . $this->GetDBField('ResourceId'); $this->Conn->Query($query); } return $ret; } /** * Deletes item from categories * * @param Array $delete_category_ids * @author Alex */ function DeleteFromCategories($delete_category_ids) { $id_field = $this->getUnitConfig()->getIDField(); // because item was loaded before by ResourceId $ci_table = $this->Application->getUnitConfig($this->Prefix . '-ci')->getTableName(); $resource_id = $this->GetDBField('ResourceId'); $item_cats_sql = ' SELECT CategoryId FROM %s WHERE ItemResourceId = %s'; $delete_category_items_sql = ' DELETE FROM %s WHERE ItemResourceId = %s AND CategoryId IN (%s)'; $category_ids = $this->Conn->GetCol( sprintf($item_cats_sql, $ci_table, $resource_id) ); $cats_left = array_diff($category_ids, $delete_category_ids); if ( !$cats_left ) { $sql = 'SELECT %s FROM %s WHERE ResourceId = %s'; $ids = $this->Conn->GetCol(sprintf($sql, $id_field, $this->TableName, $resource_id)); /** @var kTempTablesHandler $temp_handler */ $temp_handler = $this->Application->recallObject($this->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler'); $temp_handler->DeleteItems($this->Prefix, $this->Special, $ids); } else { $this->Conn->Query( sprintf($delete_category_items_sql, $ci_table, $resource_id, implode(',', $delete_category_ids)) ); $sql = 'SELECT CategoryId FROM %s WHERE PrimaryCat = 1 AND ItemResourceId = %s'; $primary_cat_id = $this->Conn->GetCol(sprintf($sql, $ci_table, $resource_id)); if ( count($primary_cat_id) == 0 ) { $sql = 'UPDATE %s SET PrimaryCat = 1 WHERE (CategoryId = %s) AND (ItemResourceId = %s)'; $this->Conn->Query( sprintf($sql, $ci_table, reset($cats_left), $resource_id) ); } } } /** * replace not allowed symbols with "_" chars + remove duplicate "_" chars in result * * @param string $filename * @return string */ function stripDisallowed($filename) { /** @var kFilenamesHelper $filenames_helper */ $filenames_helper = $this->Application->recallObject('FilenamesHelper'); $table = $this->IsTempTable() ? $this->Application->GetTempName(TABLE_PREFIX.'CategoryItems', 'prefix:'.$this->Prefix) : TABLE_PREFIX.'CategoryItems'; return $filenames_helper->stripDisallowed($table, 'ItemResourceId', $this->GetDBField('ResourceId'), $filename); } /* commented out because it's called only from stripDisallowed body, which is moved to helper function checkAutoFilename($filename) { $filenames_helper = $this->Application->recallObject('FilenamesHelper'); return $filenames_helper->checkAutoFilename($this->TableName, $this->IDField, $this->GetID(), $filename); }*/ /** * Generate item's filename based on it's title field value * * @return void * @access protected */ protected function generateFilename() { if ( !$this->GetDBField('AutomaticFilename') && $this->GetDBField('Filename') ) { return ; } $title_field = $this->getUnitConfig()->getTitleField(); if ( preg_match('/l([\d]+)_(.*)/', $title_field, $regs) ) { // if title field is multilingual, then use it's name from primary language $title_field = 'l' . $this->Application->GetDefaultLanguageId() . '_' . $regs[2]; } $name = $this->stripDisallowed( $this->GetDBField($title_field) ); if ( $name != $this->GetDBField('Filename') ) { $this->SetDBField('Filename', $name); } } /** * Adds item to other category * * @param int $category_id * @param bool $is_primary * @return void * @access public */ public function assignToCategory($category_id, $is_primary = false) { $table = $this->CategoryItemsTable(); $key_clause = '(ItemResourceId = ' . $this->GetDBField('ResourceId') . ')'; // get all categories, where item is in $sql = 'SELECT PrimaryCat, CategoryId FROM ' . $table . ' WHERE ' . $key_clause; $item_categories = $this->Conn->GetCol($sql, 'CategoryId'); $primary_found = $item_category_id = false; if ( $item_categories ) { // find primary category foreach ($item_categories as $item_category_id => $primary_found) { if ( $primary_found ) { break; } } } if ( $primary_found && ($item_category_id == $category_id) && !$is_primary ) { // want to make primary category as non-primary :( return; } elseif ( !$primary_found ) { $is_primary = true; } if ( $is_primary && $item_categories ) { // reset primary mark from all other categories $sql = 'UPDATE ' . $table . ' SET PrimaryCat = 0 WHERE ' . $key_clause; $this->Conn->Query($sql); } // UPDATE & INSERT instead of REPLACE because CategoryItems table has no primary key defined in database if ( isset($item_categories[$category_id]) ) { $sql = 'UPDATE ' . $table . ' SET PrimaryCat = ' . ($is_primary ? 1 : 0) . ' WHERE ' . $key_clause . ' AND (CategoryId = ' . $category_id . ')'; $this->Conn->Query($sql); } else { $fields_hash = Array ( 'CategoryId' => $category_id, 'ItemResourceId' => $this->GetField('ResourceId'), 'PrimaryCat' => $is_primary ? 1 : 0, 'ItemPrefix' => $this->Prefix, 'Filename' => $this->useFilenames ? (string)$this->GetDBField('Filename') : '', // because some prefixes does not use filenames, ); if ( $this->Application->IsTempTable($table) ) { $new_id = (int)$this->Conn->GetOne('SELECT MIN(Id) FROM ' . $table .' WHERE Id < 0' ); $fields_hash['Id'] = $new_id - 1; } $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! $this->Update(); } /** * Removes item from category specified * * @param int $category_id */ function removeFromCategory($category_id) { $sql = 'DELETE FROM '.TABLE_PREFIX.'CategoryItems WHERE (CategoryId = %s) AND (ItemResourceId = %s)'; $this->Conn->Query( sprintf($sql, $category_id, $this->GetDBField('ResourceId')) ); } /** * Returns list of columns, that could exist in imported file * * @return Array */ function getPossibleExportColumns() { static $columns = null; if (!is_array($columns)) { $columns = array_merge($this->Fields['AvailableColumns']['options'], $this->Fields['ExportColumns']['options']); } return $columns; } /** * Returns item's primary image data * * @return Array */ function getPrimaryImageData() { $sql = 'SELECT * FROM '.TABLE_PREFIX.'CatalogImages WHERE (ResourceId = '.$this->GetDBField('ResourceId').') AND (DefaultImg = 1)'; $image_data = $this->Conn->GetRow($sql); if (!$image_data) { // 2. no primary image, then get image with name "main" $sql = 'SELECT * FROM '.TABLE_PREFIX.'CatalogImages WHERE (ResourceId = '.$this->GetDBField('ResourceId').') AND (Name = "main")'; $image_data = $this->Conn->GetRow($sql); } return $image_data; } function ChangeStatus($new_status, $pending_editing = false) { $status_field = $this->getUnitConfig()->getStatusField(true); if ($new_status != $this->GetDBField($status_field)) { // status was changed $this->sendEmails($new_status, $pending_editing); } $this->SetDBField($status_field, $new_status); return $this->Update(); } function sendEmails($new_status, $pending_editing = false) { $config = $this->getUnitConfig(); $owner_field = $config->getOwnerField('CreatedById'); $event_name = $config->getPermItemPrefix(); if ($pending_editing) { $event_name .= '.MODIFY'; } $event_name .= $new_status == STATUS_ACTIVE ? '.APPROVE' : '.DENY'; $this->Application->emailUser($event_name, $this->GetDBField($owner_field), $this->getEmailParams()); } /** * Approves changes made to category item * * @return bool */ function ApproveChanges() { $original_id = $this->GetDBField('OrgId'); if ( !($this->usePendingEditing && $original_id) ) { // non-pending copy of original link return $this->ChangeStatus(STATUS_ACTIVE); } if ( $this->raiseEvent('OnBeforeDeleteOriginal', null, Array ('original_id' => $original_id)) ) { // delete original item, because changes made in pending copy (this item) got to be approved in this method /** @var kTempTablesHandler $temp_handler */ $temp_handler = $this->Application->recallObject($this->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler'); $temp_handler->DeleteItems($this->Prefix, $this->Special, Array ($original_id)); $this->raiseEvent('OnAfterDeleteOriginal', null, Array ('original_id' => $original_id)); $this->SetDBField('OrgId', 0); return $this->ChangeStatus(STATUS_ACTIVE, true); } return false; } /** * Decline changes made to category item * * @return bool */ function DeclineChanges() { $original_id = $this->GetDBField('OrgId'); if ( !($this->usePendingEditing && $original_id) ) { // non-pending copy of original link return $this->ChangeStatus(STATUS_DISABLED); } // delete this item, because changes made in pending copy (this item) will be declined in this method /** @var kTempTablesHandler $temp_handler */ $temp_handler = $this->Application->recallObject($this->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler'); $temp_handler->DeleteItems($this->Prefix, $this->Special, Array ($this->GetID())); $this->sendEmails(STATUS_DISABLED, true); // original item is not changed here, because it is already enabled (thrus pending copy is visible to item's owner or admin with permission) return true; } function RegisterHit() { $already_viewed = $this->Application->RecallVar($this->getPrefixSpecial().'_already_viewed'); $already_viewed = $already_viewed ? unserialize($already_viewed) : Array (); $id = $this->GetID(); if (!in_array($id, $already_viewed)) { $property_map = $this->getUnitConfig()->getItemPropertyMappings(Array ()); if (!$property_map) { return ; } $hits_field = $property_map['ClickField']; $new_hits = $this->GetDBField($hits_field) + 1; $sql = 'SELECT MAX('.$hits_field.') FROM '.$this->TableName.' WHERE FLOOR('.$hits_field.') = '.$new_hits; $max_hits = $this->Conn->GetOne($sql); if ($max_hits) { $new_hits = $max_hits + 0.000001; } $fields_hash = Array ($hits_field => $new_hits,); $this->Conn->doUpdate($fields_hash, $this->TableName, $this->IDField.' = '.$id); array_push($already_viewed, $id); $this->Application->StoreVar($this->getPrefixSpecial().'_already_viewed', serialize($already_viewed)); } } /** * Returns part of SQL WHERE clause identifying the record, ex. id = 25 * * @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 * @see kDBItem::Load() * @see kDBItem::Update() * @see kDBItem::Delete() * @return string * @access protected */ protected function GetKeyClause($method = null, $keys_hash = null) { if ( $method == 'load' && !isset($keys_hash) ) { // 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); } if ( $this->Application->isAdmin ) { // When coping new item from temp table, where no CategoryItems record present yet. return parent::GetKeyClause($method, $keys_hash) . ' ORDER BY `' . $ci_table . '`.PrimaryCat DESC'; } else { // Ensures, that CategoryId calculated field has primary category id in it. $keys_hash = Array ( $this->IDField => $this->ID, '`' . $ci_table . '`.`PrimaryCat`' => 1 ); } } return parent::GetKeyClause($method, $keys_hash); } }