Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Fri, Jun 20, 6:04 AM

in-portal

Index: branches/5.2.x/core/kernel/db/cat_dbitem.php
===================================================================
--- branches/5.2.x/core/kernel/db/cat_dbitem.php (revision 15453)
+++ branches/5.2.x/core/kernel/db/cat_dbitem.php (revision 15454)
@@ -1,623 +1,624 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class kCatDBItem extends kDBItem {
/**
* Category path, needed for import
*
* @var Array
*/
var $CategoryPath = Array();
/**
* Use automatic filename generation
*
* @var bool
*/
var $useFilenames = true;
/**
* Use pending editing abilities during item (delegated by permissions)
*
* @var bool
*/
var $usePendingEditing = false;
/**
* Removes all data from an object
*
* @param int $new_id
* @return bool
* @access public
*/
public function Clear($new_id = null)
{
parent::Clear($new_id);
$this->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->Application->getUnitOption($this->Prefix, 'UsePendingEditing');
}
/**
* 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, $system_update = false)
+ public function Update($id = null, $update_fields = null, $system_update = false)
{
if ( $this->useFilenames ) {
$this->checkFilename();
$this->generateFilename();
}
- $ret = parent::Update($id, $system_update);
+ $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->Application->getUnitOption($this->Prefix, 'TitleField');
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->Application->getUnitOption($this->Prefix, 'IDField'); // because item was loaded before by ResourceId
$ci_table = $this->Application->getUnitOption($this->Prefix . '-ci', 'TableName');
$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));
$temp_handler = $this->Application->recallObject($this->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler');
/* @var $temp_handler 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)
{
$filenames_helper = $this->Application->recallObject('FilenamesHelper');
/* @var $filenames_helper kFilenamesHelper */
$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->Application->getUnitOption($this->Prefix, 'TitleField');
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,
);
$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 = array_shift( $this->Application->getUnitOption($this->Prefix,'StatusField') );
if ($new_status != $this->GetDBField($status_field)) {
// status was changed
$this->sendEmailEvents($new_status, $pending_editing);
}
$this->SetDBField($status_field, $new_status);
return $this->Update();
}
function sendEmailEvents($new_status, $pending_editing = false)
{
$owner_field = $this->Application->getUnitOption($this->Prefix, 'OwnerField');
if (!$owner_field) {
$owner_field = 'CreatedById';
}
$event_name = $this->Application->getUnitOption($this->Prefix, 'PermItemPrefix');
if ($pending_editing) {
$event_name .= '.MODIFY';
}
$event_name .= $new_status == STATUS_ACTIVE ? '.APPROVE' : '.DENY';
$this->Application->EmailEventUser($event_name, $this->GetDBField($owner_field));
}
/**
* 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
$temp_handler = $this->Application->recallObject($this->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler');
/* @var $temp_handler 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
$temp_handler = $this->Application->recallObject($this->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler');
/* @var $temp_handler kTempTablesHandler */
$temp_handler->DeleteItems($this->Prefix, $this->Special, Array ($this->GetID()));
$this->sendEmailEvents(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->Application->getUnitOption($this->Prefix, 'ItemPropertyMappings');
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') {
// 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);
}
$primary_category_clause = Array ('`' . $ci_table . '`.`PrimaryCat`' => 1);
if (!isset($keys_hash)) {
$keys_hash = Array ($this->IDField => $this->ID);
}
// merge primary category clause in any case to be sure, that
// CategoryId field will always contain primary category of item
$keys_hash = array_merge($keys_hash, $primary_category_clause);
}
return parent::GetKeyClause($method, $keys_hash);
}
}
\ No newline at end of file
Index: branches/5.2.x/core/kernel/db/dbitem.php
===================================================================
--- branches/5.2.x/core/kernel/db/dbitem.php (revision 15453)
+++ branches/5.2.x/core/kernel/db/dbitem.php (revision 15454)
@@ -1,1533 +1,1547 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
/**
* DBItem
*
*/
class kDBItem extends kDBBase {
/**
* Description
*
* @var array Associative array of current item' field values
* @access protected
*/
protected $FieldValues = Array ();
/**
* Unformatted field values, before parse
*
* @var Array
* @access protected
*/
protected $DirtyFieldValues = Array ();
/**
* Holds item values after loading (not affected by submit)
*
* @var Array
* @access protected
*/
protected $OriginalFieldValues = Array ();
/**
* If set to true, Update will skip Validation before running
*
* @var array Associative array of current item' field values
* @access public
*/
public $IgnoreValidation = false;
/**
* Remembers if object was loaded
*
* @var bool
* @access protected
*/
protected $Loaded = false;
/**
* Holds item' primary key value
*
* @var int Value of primary key field for current item
* @access protected
*/
protected $ID;
/**
* This object is used in cloning operations
*
* @var bool
* @access public
*/
public $inCloning = false;
/**
* Validator object reference
*
* @var kValidator
*/
protected $validator = null;
/**
* Creates validator object, only when required
*
*/
public function initValidator()
{
if ( !is_object($this->validator) ) {
$validator_class = $this->Application->getUnitOption($this->Prefix, 'ValidatorClass', 'kValidator');
$this->validator = $this->Application->makeClass($validator_class);
}
$this->validator->setDataSource($this);
}
public function SetDirtyField($field_name, $field_value)
{
$this->DirtyFieldValues[$field_name] = $field_value;
}
public function GetDirtyField($field_name)
{
return $this->DirtyFieldValues[$field_name];
}
public function GetOriginalField($field_name, $formatted = false, $format=null)
{
if (array_key_exists($field_name, $this->OriginalFieldValues)) {
// item was loaded before
$value = $this->OriginalFieldValues[$field_name];
}
else {
// no original fields -> use default field value
$value = $this->Fields[$field_name]['default'];
}
if (!$formatted) {
return $value;
}
$res = $value;
$formatter = $this->GetFieldOption($field_name, 'formatter');
if ( $formatter ) {
$formatter = $this->Application->recallObject($formatter);
/* @var $formatter kFormatter */
$res = $formatter->Format($value, $field_name, $this, $format);
}
return $res;
}
/**
* Sets original field value (useful for custom virtual fields)
*
* @param string $field_name
* @param string $field_value
*/
public function SetOriginalField($field_name, $field_value)
{
$this->OriginalFieldValues[$field_name] = $field_value;
}
/**
* Set's default values for all fields
*
* @access public
*/
public function SetDefaultValues()
{
parent::SetDefaultValues();
if ($this->populateMultiLangFields) {
$this->PopulateMultiLangFields();
}
foreach ($this->Fields as $field => $field_options) {
$default_value = isset($field_options['default']) ? $field_options['default'] : NULL;
$this->SetDBField($field, $default_value);
}
}
/**
* Sets current item field value
* (applies formatting)
*
* @access public
* @param string $name Name of the field
* @param mixed $value Value to set the field to
* @return void
*/
public function SetField($name,$value)
{
$options = $this->GetFieldOptions($name);
$parsed = $value;
if ($value == '') {
$parsed = NULL;
}
// kFormatter is always used, to make sure, that numeric value is converted to normal representation
// according to regional format, even when formatter is not set (try seting format to 1.234,56 to understand why)
$formatter = $this->Application->recallObject(isset($options['formatter']) ? $options['formatter'] : 'kFormatter');
/* @var $formatter kFormatter */
$parsed = $formatter->Parse($value, $name, $this);
$this->SetDBField($name,$parsed);
}
/**
* Sets current item field value
* (doesn't apply formatting)
*
* @access public
* @param string $name Name of the field
* @param mixed $value Value to set the field to
* @return void
*/
public function SetDBField($name,$value)
{
$this->FieldValues[$name] = $value;
}
/**
* Set's field error, if pseudo passed not found then create it with message text supplied.
* Don't overwrite existing pseudo translation.
*
* @param string $field
* @param string $pseudo
* @param string $error_label
* @param Array $error_params
*
* @return bool
* @access public
*/
public function SetError($field, $pseudo, $error_label = null, $error_params = null)
{
$this->initValidator();
return $this->validator->SetError($field, $pseudo, $error_label, $error_params);
}
/**
* Removes error on field
*
* @param string $field
* @access public
*/
public function RemoveError($field)
{
if ( !is_object($this->validator) ) {
return ;
}
$this->validator->RemoveError($field);
}
/**
* Returns error pseudo
*
* @param string $field
* @return string
*/
public function GetErrorPseudo($field)
{
if ( !is_object($this->validator) ) {
return '';
}
return $this->validator->GetErrorPseudo($field);
}
/**
* Return current item' field value by field name
* (doesn't apply formatter)
*
* @param string $name field name to return
* @return mixed
* @access public
*/
public function GetDBField($name)
{
/*if (!array_key_exists($name, $this->FieldValues) && defined('DEBUG_MODE') && DEBUG_MODE) {
$this->Application->Debugger->appendTrace();
}*/
return $this->FieldValues[$name];
}
public function HasField($name)
{
return array_key_exists($name, $this->FieldValues);
}
public function GetFieldValues()
{
return $this->FieldValues;
}
/**
* Sets item' fields corresponding to elements in passed $hash values.
*
* The function sets current item fields to values passed in $hash, by matching $hash keys with field names
* of current item. If current item' fields are unknown {@link kDBItem::PrepareFields()} is called before actually setting the fields
*
* @param Array $hash
* @param Array $skip_fields Optional param, field names in target object not to set, other fields will be set
* @param Array $set_fields Optional param, field names in target object to set, other fields will be skipped
* @return void
* @access public
*/
public function SetFieldsFromHash($hash, $skip_fields = Array (), $set_fields = Array ())
{
if ( !$set_fields ) {
$set_fields = array_keys($hash);
}
if ( $skip_fields ) {
$set_fields = array_diff($set_fields, $skip_fields);
}
$set_fields = array_intersect($set_fields, array_keys($this->Fields));
// used in formatter which work with multiple fields together
foreach ($set_fields as $field_name) {
$this->SetDirtyField($field_name, $hash[$field_name]);
}
// formats all fields using associated formatters
foreach ($set_fields as $field_name) {
$this->SetField($field_name, $hash[$field_name]);
}
}
/**
* Sets object fields from $hash array
* @param Array $hash
* @param Array|null $skip_fields
* @param Array|null $set_fields
* @return void
* @access public
*/
public function SetDBFieldsFromHash($hash, $skip_fields = Array (), $set_fields = Array ())
{
if ( !$set_fields ) {
$set_fields = array_keys($hash);
}
if ( $skip_fields ) {
$set_fields = array_diff($set_fields, $skip_fields);
}
$set_fields = array_intersect($set_fields, array_keys($this->Fields));
foreach ($set_fields as $field_name) {
$this->SetDBField($field_name, $hash[$field_name]);
}
}
/**
* 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 ( !isset($keys_hash) ) {
$keys_hash = Array ($this->IDField => $this->ID);
}
$ret = '';
foreach ($keys_hash as $field => $value) {
$value_part = is_null($value) ? ' IS NULL' : ' = ' . $this->Conn->qstr($value);
$ret .= '(' . (strpos($field, '.') === false ? '`' . $this->TableName . '`.' : '') . $field . $value_part . ') AND ';
}
return substr($ret, 0, -5);
}
/**
* Loads item from the database by given id
*
* @access public
* @param mixed $id item id of keys->values hash to load item by
* @param string $id_field_name Optional parameter to load item by given Id field
* @param bool $cachable cache this query result based on it's prefix serial
* @return bool True if item has been loaded, false otherwise
*/
public function Load($id, $id_field_name = null, $cachable = false)
{
if ( isset($id_field_name) ) {
$this->IDField = $id_field_name; // set new IDField
}
$keys_sql = '';
if (is_array($id)) {
$keys_sql = $this->GetKeyClause('load', $id);
}
else {
$this->setID($id);
$keys_sql = $this->GetKeyClause('load');
}
if ( isset($id_field_name) ) {
// restore original IDField from unit config
$this->IDField = $this->Application->getUnitOption($this->Prefix, 'IDField');
}
if (($id === false) || !$keys_sql) {
return $this->Clear();
}
if (!$this->raiseEvent('OnBeforeItemLoad', $id)) {
return false;
}
$q = $this->GetSelectSQL() . ' WHERE ' . $keys_sql;
if ($cachable && $this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
$serial_name = $this->Application->incrementCacheSerial($this->Prefix == 'st' ? 'c' : $this->Prefix, isset($id_field_name) ? null : $id, false);
$cache_key = 'kDBItem::Load_' . crc32(serialize($id) . '-' . $this->IDField) . '[%' . $serial_name . '%]';
$field_values = $this->Application->getCache($cache_key, false);
if ($field_values === false) {
$field_values = $this->Conn->GetRow($q);
if ($field_values !== false) {
// only cache, when data was retrieved
$this->Application->setCache($cache_key, $field_values);
}
}
}
else {
$field_values = $this->Conn->GetRow($q);
}
if ($field_values) {
$this->FieldValues = array_merge($this->FieldValues, $field_values);
$this->OriginalFieldValues = $this->FieldValues;
}
else {
return $this->Clear();
}
if (is_array($id) || isset($id_field_name)) {
$this->setID($this->FieldValues[$this->IDField]);
}
$this->UpdateFormattersSubFields(); // used for updating separate virtual date/time fields from DB timestamp (for example)
$this->raiseEvent('OnAfterItemLoad', $this->GetID());
$this->Loaded = true;
return true;
}
/**
* Loads object from hash (not db)
*
* @param Array $fields_hash
* @param string $id_field
*/
public function LoadFromHash($fields_hash, $id_field = null)
{
if (!isset($id_field)) {
$id_field = $this->IDField;
}
$this->Clear();
if (!$fields_hash || !array_key_exists($id_field, $fields_hash)) {
// no data OR id field missing
return false;
}
$id = $fields_hash[$id_field];
if ( !$this->raiseEvent('OnBeforeItemLoad', $id) ) {
return false;
}
$this->FieldValues = array_merge($this->FieldValues, $fields_hash);
$this->OriginalFieldValues = $this->FieldValues;
$this->setID($id);
$this->UpdateFormattersSubFields(); // used for updating separate virtual date/time fields from DB timestamp (for example)
$this->raiseEvent('OnAfterItemLoad', $id);
$this->Loaded = true;
return true;
}
/**
* Builds select sql, SELECT ... FROM parts only
*
* @access public
* @return string
*/
/**
* Returns SELECT part of list' query
*
* @param string $base_query
* @param bool $replace_table
* @return string
* @access public
*/
public function GetSelectSQL($base_query = null, $replace_table = true)
{
if (!isset($base_query)) {
$base_query = $this->SelectClause;
}
$base_query = $this->addCalculatedFields($base_query);
return parent::GetSelectSQL($base_query, $replace_table);
}
public function UpdateFormattersMasterFields()
{
$this->initValidator(); // used, when called not from kValidator::Validate method
foreach ($this->Fields as $field => $options) {
if ( isset($options['formatter']) ) {
$formatter = $this->Application->recallObject($options['formatter']);
/* @var $formatter kFormatter */
$formatter->UpdateMasterFields($field, $this->GetDBField($field), $options, $this);
}
}
}
/**
* Actually moves uploaded files from temp to live folder
*
* @param int $id
* @return void
* @access public
*/
public function processUploads($id = NULL)
{
$uploader_fields = $this->_getUploaderFields();
foreach ($uploader_fields as $field) {
$formatter = $this->Application->recallObject($this->GetFieldOption($field, 'formatter'));
/* @var $formatter kUploadFormatter */
$this->SetDBField($field, $formatter->processFlashUpload($this, $field, $id));
}
if ( $this->GetChangedFields() ) {
$this->Update();
}
}
/**
* Removes any info about queued uploaded files
*
* @param int $id
* @return void
* @access public
*/
public function resetUploads($id = NULL)
{
$uploader_fields = $this->_getUploaderFields();
foreach ($uploader_fields as $field) {
$this->Application->RemoveVar($this->getFileInfoVariableName($field, $id));
}
}
/**
* Returns uploader fields
*
* @return Array
* @access protected
*/
protected function _getUploaderFields()
{
$ret = Array ();
foreach ($this->Fields as $field => $options) {
if ( !isset($options['formatter']) ) {
continue;
}
$formatter = $this->Application->recallObject($options['formatter']);
/* @var $formatter kUploadFormatter */
if ( $formatter instanceof kUploadFormatter ) {
$ret[] = $field;
}
}
return $ret;
}
/**
* Returns variable name, used to store pending file actions
*
* @return string
* @access public
*/
public function getPendingActionVariableName()
{
$window_id = $this->Application->GetTopmostWid($this->Prefix);
return $this->Prefix . '_file_pending_actions' . $window_id;
}
/**
* Returns variable name, which stores file information for object/field/window combination
*
* @param string $field_name
* @param int $id
* @return string
* @access public
*/
public function getFileInfoVariableName($field_name, $id = NULL)
{
if ( !isset($id) ) {
$id = $this->GetID();
}
$window_id = $this->Application->GetTopmostWid($this->Prefix);
return $this->Prefix . '[' . $id . '][' . $field_name . ']_file_info' . $window_id;
}
/**
* Allows to skip certain fields from getting into sql queries
*
* @param string $field_name
* @param mixed $force_id
* @return bool
*/
public function skipField($field_name, $force_id = false)
{
$skip = false;
// 1. skipping 'virtual' field
$skip = $skip || array_key_exists($field_name, $this->VirtualFields);
// 2. don't write empty field value to db, when "skip_empty" option is set
$field_value = array_key_exists($field_name, $this->FieldValues) ? $this->FieldValues[$field_name] : false;
if (array_key_exists($field_name, $this->Fields)) {
$skip_empty = array_key_exists('skip_empty', $this->Fields[$field_name]) ? $this->Fields[$field_name]['skip_empty'] : false;
}
else {
// field found in database, but not declared in unit config
$skip_empty = false;
}
$skip = $skip || (!$field_value && $skip_empty);
// 3. skipping field not in Fields (nor virtual, nor real)
$skip = $skip || !array_key_exists($field_name, $this->Fields);
return $skip;
}
/**
* 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, $system_update = false)
+ public function Update($id = null, $update_fields = null, $system_update = false)
{
if ( isset($id) ) {
$this->setID($id);
}
if ( !$this->raiseEvent('OnBeforeItemUpdate') ) {
return false;
}
if ( !isset($this->ID) ) {
// ID could be set inside OnBeforeItemUpdate event, so don't combine this check with previous one
return false;
}
// validate before updating
if ( !$this->Validate() ) {
return false;
}
if ( !$this->FieldValues ) {
// nothing to update
return true;
}
$sql = '';
- foreach ($this->FieldValues as $field_name => $field_value) {
+ $set_fields = isset($update_fields) ? $update_fields : array_keys($this->FieldValues);
+
+ foreach ($set_fields as $field_name) {
if ( $this->skipField($field_name) ) {
continue;
}
+ $field_value = $this->FieldValues[$field_name];
+
if ( is_null($field_value) ) {
if ( array_key_exists('not_null', $this->Fields[$field_name]) && $this->Fields[$field_name]['not_null'] ) {
// "kFormatter::Parse" methods converts empty values to NULL and for
// not-null fields they are replaced with default value here
$field_value = $this->Fields[$field_name]['default'];
}
}
$sql .= '`' . $field_name . '` = ' . $this->Conn->qstr($field_value) . ', ';
}
$sql = 'UPDATE ' . $this->TableName . '
SET ' . substr($sql, 0, -2) . '
WHERE ' . $this->GetKeyClause('update');
if ( $this->Conn->ChangeQuery($sql) === false ) {
// there was and sql error
$this->SetError($this->IDField, 'sql_error', '#' . $this->Conn->getErrorCode() . ': ' . $this->Conn->getErrorMsg());
return false;
}
$affected_rows = $this->Conn->getAffectedRows();
if ( !$system_update && ($affected_rows > 0) ) {
$this->setModifiedFlag(ChangeLog::UPDATE);
}
$this->saveCustomFields();
$this->raiseEvent('OnAfterItemUpdate');
- $this->OriginalFieldValues = $this->FieldValues;
+
+ if ( !isset($update_fields) ) {
+ $this->OriginalFieldValues = $this->FieldValues;
+ }
+ else {
+ foreach ($update_fields as $update_field) {
+ $this->OriginalFieldValues[$update_field] = $this->FieldValues[$update_field];
+ }
+ }
+
$this->Loaded = true;
if ( !$this->IsTempTable() ) {
$this->Application->resetCounters($this->TableName);
}
return true;
}
/**
* Validates given field
*
* @param string $field
* @return bool
* @access public
*/
public function ValidateField($field)
{
$this->initValidator();
return $this->validator->ValidateField($field);
}
/**
* Validate all item fields based on
* constraints set in each field options
* in config
*
* @return bool
* @access private
*/
public function Validate()
{
if ( $this->IgnoreValidation ) {
return true;
}
$this->initValidator();
// will apply any custom validation to the item
$this->raiseEvent('OnBeforeItemValidate');
if ( $this->validator->Validate() ) {
// no validation errors
$this->raiseEvent('OnAfterItemValidate');
return true;
}
return false;
}
/**
* Check if item has errors
*
* @param Array $skip_fields fields to skip during error checking
* @return bool
*/
public function HasErrors($skip_fields = Array ())
{
if ( !is_object($this->validator) ) {
return false;
}
return $this->validator->HasErrors($skip_fields);
}
/**
* Check if value is set for required field
*
* @param string $field field name
* @param Array $params field options from config
* @return bool
* @access public
* @todo Find a way to get rid of direct call from kMultiLanguage::UpdateMasterFields method
*/
public function ValidateRequired($field, $params)
{
return $this->validator->ValidateRequired($field, $params);
}
/**
* Return error message for field
*
* @param string $field
* @param bool $force_escape
* @return string
* @access public
*/
public function GetErrorMsg($field, $force_escape = null)
{
if ( !is_object($this->validator) ) {
return '';
}
return $this->validator->GetErrorMsg($field, $force_escape);
}
/**
* Returns field errors
*
* @return Array
* @access public
*/
public function GetFieldErrors()
{
if ( !is_object($this->validator) ) {
return Array ();
}
return $this->validator->GetFieldErrors();
}
/**
* Creates a record in the database table with current item' values
*
* @param mixed $force_id Set to TRUE to force creating of item's own ID or to value to force creating of passed id. Do not pass 1 for true, pass exactly TRUE!
* @param bool $system_create
* @return bool
* @access public
*/
public function Create($force_id = false, $system_create = false)
{
if (!$this->raiseEvent('OnBeforeItemCreate')) {
return false;
}
// Validating fields before attempting to create record
if (!$this->Validate()) {
return false;
}
if (is_int($force_id)) {
$this->FieldValues[$this->IDField] = $force_id;
}
elseif (!$force_id || !is_bool($force_id)) {
$this->FieldValues[$this->IDField] = $this->generateID();
}
$fields_sql = '';
$values_sql = '';
foreach ($this->FieldValues as $field_name => $field_value) {
if ($this->skipField($field_name, $force_id)) {
continue;
}
if (is_null($field_value)) {
if (array_key_exists('not_null', $this->Fields[$field_name]) && $this->Fields[$field_name]['not_null']) {
// "kFormatter::Parse" methods converts empty values to NULL and for
// not-null fields they are replaced with default value here
$values_sql .= $this->Conn->qstr($this->Fields[$field_name]['default']);
}
else {
$values_sql .= $this->Conn->qstr($field_value);
}
}
else {
if (($field_name == $this->IDField) && ($field_value == 0) && !is_int($force_id)) {
// don't skip IDField in INSERT statement, just use DEFAULT keyword as it's value
$values_sql .= 'DEFAULT';
}
else {
$values_sql .= $this->Conn->qstr($field_value);
}
}
$fields_sql .= '`' . $field_name . '`, '; //Adding field name to fields block of Insert statement
$values_sql .= ', ';
}
$sql = 'INSERT INTO ' . $this->TableName . ' (' . substr($fields_sql, 0, -2) . ')
VALUES (' . substr($values_sql, 0, -2) . ')';
//Executing the query and checking the result
if ($this->Conn->ChangeQuery($sql) === false) {
$this->SetError($this->IDField, 'sql_error', '#' . $this->Conn->getErrorCode() . ': ' . $this->Conn->getErrorMsg());
return false;
}
$insert_id = $this->Conn->getInsertID();
if ($insert_id == 0) {
// insert into temp table (id is not auto-increment field)
$insert_id = $this->FieldValues[$this->IDField];
}
$this->setID($insert_id);
$this->OriginalFieldValues = $this->FieldValues;
if (!$system_create){
$this->setModifiedFlag(ChangeLog::CREATE);
}
$this->saveCustomFields();
if (!$this->IsTempTable()) {
$this->Application->resetCounters($this->TableName);
}
if ($this->IsTempTable() && ($this->Application->GetTopmostPrefix($this->Prefix) != $this->Prefix) && !is_int($force_id)) {
// temp table + subitem = set negative id
$this->setTempID();
}
$this->raiseEvent('OnAfterItemCreate');
$this->Loaded = true;
return true;
}
/**
* Deletes the record from database
*
* @param int $id
* @return bool
* @access public
*/
public function Delete($id = null)
{
if ( isset($id) ) {
$this->setID($id);
}
if ( !$this->raiseEvent('OnBeforeItemDelete') ) {
return false;
}
$sql = 'DELETE FROM ' . $this->TableName . '
WHERE ' . $this->GetKeyClause('Delete');
$ret = $this->Conn->ChangeQuery($sql);
$affected_rows = $this->Conn->getAffectedRows();
if ( $affected_rows > 0 ) {
$this->setModifiedFlag(ChangeLog::DELETE); // will change affected rows, so get it before this line
// something was actually deleted
$this->raiseEvent('OnAfterItemDelete');
}
if ( !$this->IsTempTable() ) {
$this->Application->resetCounters($this->TableName);
}
return $ret;
}
public function PopulateMultiLangFields()
{
foreach ($this->Fields as $field => $options) {
// master field is set only for CURRENT language
$formatter = array_key_exists('formatter', $options) ? $options['formatter'] : false;
if ( ($formatter == 'kMultiLanguage') && isset($options['master_field']) && isset($options['error_field']) ) {
// MuliLanguage formatter sets error_field to master_field, but in PopulateMlFields mode,
// we display ML fields directly so we set it back to itself, otherwise error won't be displayed
unset( $this->Fields[$field]['error_field'] );
}
}
}
/**
* 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')
{
if (!isset($title_field)) {
$title_field = $this->Application->getUnitOption($this->Prefix, 'TitleField');
if (!$title_field || isset($this->CalculatedFields[$title_field]) ) return;
}
$new_name = $this->GetDBField($title_field);
$original_checked = false;
do {
if ( preg_match('/'.sprintf($format, '([0-9]*) *', '(.*)').'/', $new_name, $regs) ) {
$new_name = sprintf($format, ($regs[1]+1), $regs[2]);
}
elseif ($original_checked) {
$new_name = sprintf($format, '', $new_name);
}
// if we are cloning in temp table this will look for names in temp table,
// since object' TableName contains correct TableName (for temp also!)
// if we are cloning live - look in live
$query = 'SELECT '.$title_field.' FROM '.$this->TableName.'
WHERE '.$title_field.' = '.$this->Conn->qstr($new_name);
$foreign_key_field = getArrayValue($master, 'ForeignKey');
$foreign_key_field = is_array($foreign_key_field) ? $foreign_key_field[ $master['ParentPrefix'] ] : $foreign_key_field;
if ($foreign_key_field && isset($foreign_key)) {
$query .= ' AND '.$foreign_key_field.' = '.$foreign_key;
}
$res = $this->Conn->GetOne($query);
/*// if not found in live table, check in temp table if applicable
if ($res === false && $object->Special == 'temp') {
$query = 'SELECT '.$name_field.' FROM '.$this->GetTempName($master['TableName']).'
WHERE '.$name_field.' = '.$this->Conn->qstr($new_name);
$res = $this->Conn->GetOne($query);
}*/
$original_checked = true;
} while ($res !== false);
$this->SetDBField($title_field, $new_name);
}
protected function raiseEvent($name, $id = null, $additional_params = Array())
{
$additional_params['id'] = isset($id) ? $id : $this->GetID();
$event = new kEvent($this->getPrefixSpecial() . ':' . $name, $additional_params);
if ( is_object($this->parentEvent) ) {
$event->MasterEvent = $this->parentEvent;
}
$this->Application->HandleEvent($event);
return $event->status == kEvent::erSUCCESS;
}
/**
* Set's new ID for item
*
* @param int $new_id
* @access public
*/
public function setID($new_id)
{
$this->ID = $new_id;
$this->SetDBField($this->IDField, $new_id);
}
/**
* Generate and set new temporary id
*
* @access private
*/
public function setTempID()
{
$new_id = (int)$this->Conn->GetOne('SELECT MIN('.$this->IDField.') FROM '.$this->TableName);
if($new_id > 0) $new_id = 0;
--$new_id;
$this->Conn->Query('UPDATE '.$this->TableName.' SET `'.$this->IDField.'` = '.$new_id.' WHERE `'.$this->IDField.'` = '.$this->GetID());
if ($this->ShouldLogChanges(true)) {
// Updating TempId in ChangesLog, if changes are disabled
$ses_var_name = $this->Application->GetTopmostPrefix($this->Prefix) . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
$changes = $this->Application->RecallVar($ses_var_name);
$changes = $changes ? unserialize($changes) : Array ();
if ($changes) {
foreach ($changes as $key => $rec) {
if ($rec['Prefix'] == $this->Prefix && $rec['ItemId'] == $this->GetID()) {
// change log for record, that's ID was just updated -> update in change log record too
$changes[$key]['ItemId'] = $new_id;
}
if ($rec['MasterPrefix'] == $this->Prefix && $rec['MasterId'] == $this->GetID()) {
// master item id was changed
$changes[$key]['MasterId'] = $new_id;
}
if (in_array($this->Prefix, $rec['ParentPrefix']) && $rec['ParentId'][$this->Prefix] == $this->GetID()) {
// change log record of given item's sub item -> update changed id's in dependent fields
$changes[$key]['ParentId'][$this->Prefix] = $new_id;
if (array_key_exists('DependentFields', $rec)) {
// these are fields from table of $rec['Prefix'] table!
// when one of dependent fields goes into idfield of it's parent item, that was changed
$parent_table_key = $this->Application->getUnitOption($rec['Prefix'], 'ParentTableKey');
$parent_table_key = is_array($parent_table_key) ? $parent_table_key[$this->Prefix] : $parent_table_key;
if ($parent_table_key == $this->IDField) {
$foreign_key = $this->Application->getUnitOption($rec['Prefix'], 'ForeignKey');
$foreign_key = is_array($foreign_key) ? $foreign_key[$this->Prefix] : $foreign_key;
$changes[$key]['DependentFields'][$foreign_key] = $new_id;
}
}
}
}
}
$this->Application->StoreVar($ses_var_name, serialize($changes));
}
$this->SetID($new_id);
}
/**
* Set's modification flag for main prefix of current prefix to true
*
* @param int $mode
* @access private
*/
public function setModifiedFlag($mode = null)
{
$main_prefix = $this->Application->GetTopmostPrefix($this->Prefix);
$this->Application->StoreVar($main_prefix . '_modified', '1', true); // true for optional
if ($this->ShouldLogChanges(true)) {
$this->LogChanges($main_prefix, $mode);
if (!$this->IsTempTable()) {
$handler = $this->Application->recallObject($this->Prefix . '_EventHandler');
/* @var $handler kDBEventHandler */
$ses_var_name = $main_prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
$handler->SaveLoggedChanges($ses_var_name, $this->ShouldLogChanges());
}
}
}
/**
* Determines, that changes made to this item should be written to change log
*
* @param bool $log_changes
* @return bool
*/
public function ShouldLogChanges($log_changes = null)
{
if (!isset($log_changes)) {
// specific logging mode no forced -> use global logging settings
$log_changes = $this->Application->getUnitOption($this->Prefix, 'LogChanges') || $this->Application->ConfigValue('UseChangeLog');
}
return $log_changes && !$this->Application->getUnitOption($this->Prefix, 'ForceDontLogChanges');
}
protected function LogChanges($main_prefix, $mode)
{
if ( !$mode ) {
return ;
}
$ses_var_name = $main_prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
$changes = $this->Application->RecallVar($ses_var_name);
$changes = $changes ? unserialize($changes) : Array ();
$fields_hash = Array (
'Prefix' => $this->Prefix,
'ItemId' => $this->GetID(),
'OccuredOn' => adodb_mktime(),
'MasterPrefix' => $main_prefix,
'Action' => $mode,
);
if ( $this->Prefix == $main_prefix ) {
// main item
$fields_hash['MasterId'] = $this->GetID();
$fields_hash['ParentPrefix'] = Array ($main_prefix);
$fields_hash['ParentId'] = Array ($main_prefix => $this->GetID());
}
else {
// sub item
// collect foreign key values (for serial reset)
$foreign_keys = $this->Application->getUnitOption($this->Prefix, 'ForeignKey', Array ());
$dependent_fields = $fields_hash['ParentId'] = $fields_hash['ParentPrefix'] = Array ();
/* @var $foreign_keys Array */
if ( is_array($foreign_keys) ) {
foreach ($foreign_keys as $prefix => $field_name) {
$dependent_fields[$field_name] = $this->GetDBField($field_name);
$fields_hash['ParentPrefix'][] = $prefix;
$fields_hash['ParentId'][$prefix] = $this->getParentId($prefix);
}
}
else {
$dependent_fields[$foreign_keys] = $this->GetDBField($foreign_keys);
$fields_hash['ParentPrefix'] = Array ( $this->Application->getUnitOption($this->Prefix, 'ParentPrefix') );
$fields_hash['ParentId'][ $fields_hash['ParentPrefix'][0] ] = $this->getParentId('auto');
}
$fields_hash['DependentFields'] = $dependent_fields;
// works only, when main item is present in url, when sub-item is changed
$master_id = $this->Application->GetVar($main_prefix . '_id');
if ( $master_id === false ) {
// works in case of we are not editing topmost item, when sub-item is created/updated/deleted
$master_id = $this->getParentId('auto', true);
}
$fields_hash['MasterId'] = $master_id;
}
switch ( $mode ) {
case ChangeLog::UPDATE:
$to_save = array_merge($this->GetTitleField(), $this->GetChangedFields());
break;
case ChangeLog::CREATE:
$to_save = $this->GetTitleField();
break;
case ChangeLog::DELETE:
$to_save = array_merge($this->GetTitleField(), $this->GetRealFields());
break;
default:
$to_save = Array ();
break;
}
$fields_hash['Changes'] = serialize($to_save);
$changes[] = $fields_hash;
$this->Application->StoreVar($ses_var_name, serialize($changes));
}
/**
* Returns current item parent's ID
*
* @param string $parent_prefix
* @param bool $top_most return topmost parent, when used
* @return int
* @access public
*/
public function getParentId($parent_prefix, $top_most = false)
{
$current_id = $this->GetID();
$current_prefix = $this->Prefix;
if ($parent_prefix == 'auto') {
$parent_prefix = $this->Application->getUnitOption($current_prefix, 'ParentPrefix');
}
if (!$parent_prefix) {
return $current_id;
}
do {
// field in this table
$foreign_key = $this->Application->getUnitOption($current_prefix, 'ForeignKey');
$foreign_key = is_array($foreign_key) ? $foreign_key[$parent_prefix] : $foreign_key;
// get foreign key value for $current_prefix
if ($current_prefix == $this->Prefix) {
$foreign_key_value = $this->GetDBField($foreign_key);
}
else {
$id_field = $this->Application->getUnitOption($current_prefix, 'IDField');
$table_name = $this->Application->getUnitOption($current_prefix, 'TableName');
if ($this->IsTempTable()) {
$table_name = $this->Application->GetTempName($table_name, 'prefix:' . $current_prefix);
}
$sql = 'SELECT ' . $foreign_key . '
FROM ' . $table_name . '
WHERE ' . $id_field . ' = ' . $current_id;
$foreign_key_value = $this->Conn->GetOne($sql);
}
// field in parent table
$parent_table_key = $this->Application->getUnitOption($current_prefix, 'ParentTableKey');
$parent_table_key = is_array($parent_table_key) ? $parent_table_key[$parent_prefix] : $parent_table_key;
$parent_id_field = $this->Application->getUnitOption($parent_prefix, 'IDField');
$parent_table_name = $this->Application->getUnitOption($parent_prefix, 'TableName');
if ($this->IsTempTable()) {
$parent_table_name = $this->Application->GetTempName($parent_table_name, 'prefix:' . $current_prefix);
}
if ($parent_id_field == $parent_table_key) {
// sub-item is related by parent item idfield
$current_id = $foreign_key_value;
}
else {
// sub-item is related by other parent item field
$sql = 'SELECT ' . $parent_id_field . '
FROM ' . $parent_table_name . '
WHERE ' . $parent_table_key . ' = ' . $foreign_key_value;
$current_id = $this->Conn->GetOne($sql);
}
$current_prefix = $parent_prefix;
if (!$top_most) {
break;
}
} while ( $parent_prefix = $this->Application->getUnitOption($current_prefix, 'ParentPrefix') );
return $current_id;
}
/**
* Returns title field (if any)
*
* @return Array
*/
public function GetTitleField()
{
$title_field = $this->Application->getUnitOption($this->Prefix, 'TitleField');
if ($title_field) {
$value = $this->GetField($title_field);
return $value ? Array ($title_field => $value) : Array ();
}
return Array ();
}
/**
* Returns only fields, that are present in database (no virtual and no calculated fields)
*
* @return Array
*/
public function GetRealFields()
{
return array_diff_key($this->FieldValues, $this->VirtualFields, $this->CalculatedFields);
}
/**
* Returns only changed database field
*
* @param bool $include_virtual_fields
* @return Array
*/
public function GetChangedFields($include_virtual_fields = false)
{
$changes = Array ();
$fields = $include_virtual_fields ? $this->FieldValues : $this->GetRealFields();
$diff = array_diff_assoc($fields, $this->OriginalFieldValues);
foreach ($diff as $field => $new_value) {
$old_value = $this->GetOriginalField($field, true);
$new_value = $this->GetField($field);
if ($old_value != $new_value) {
// "0.00" and "0.0000" are stored as strings and will differ. Double check to prevent that.
$changes[$field] = Array ('old' => $old_value, 'new' => $new_value);
}
}
return $changes;
}
/**
* Returns ID of currently processed record
*
* @return int
* @access public
*/
public function GetID()
{
return $this->ID;
}
/**
* Generates ID for new items before inserting into database
*
* @return int
* @access private
*/
protected function generateID()
{
return 0;
}
/**
* Returns true if item was loaded successfully by Load method
*
* @return bool
*/
public function isLoaded()
{
return $this->Loaded;
}
/**
* Checks if field is required
*
* @param string $field
* @return bool
*/
public function isRequired($field)
{
return isset($this->Fields[$field]['required']) && $this->Fields[$field]['required'];
}
/**
* Sets new required flag to field
*
* @param mixed $fields
* @param bool $is_required
*/
public function setRequired($fields, $is_required = true)
{
if ( !is_array($fields) ) {
$fields = explode(',', $fields);
}
foreach ($fields as $field) {
$this->Fields[$field]['required'] = $is_required;
}
}
/**
* Removes all data from an object
*
* @param int $new_id
* @return bool
* @access public
*/
public function Clear($new_id = null)
{
$this->Loaded = false;
$this->FieldValues = $this->OriginalFieldValues = Array ();
$this->SetDefaultValues(); // will wear off kDBItem::setID effect, so set it later
if ( is_object($this->validator) ) {
$this->validator->reset();
}
$this->setID($new_id);
return $this->Loaded;
}
public function Query($force = false)
{
throw new Exception('<b>Query</b> method is called in class <strong>' . get_class($this) . '</strong> for prefix <strong>' . $this->getPrefixSpecial() . '</strong>');
}
protected function saveCustomFields()
{
if ( !$this->customFields || $this->inCloning ) {
return true;
}
$cdata_key = rtrim($this->Prefix . '-cdata.' . $this->Special, '.');
$cdata = $this->Application->recallObject($cdata_key, null, Array ('skip_autoload' => true));
/* @var $cdata kDBItem */
$resource_id = $this->GetDBField('ResourceId');
$cdata->Load($resource_id, 'ResourceId');
$cdata->SetDBField('ResourceId', $resource_id);
$ml_formatter = $this->Application->recallObject('kMultiLanguage');
/* @var $ml_formatter kMultiLanguage */
$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');
/* @var $ml_helper kMultiLanguageHelper */
$languages = $ml_helper->getLanguages();
foreach ($this->customFields as $custom_id => $custom_name) {
$force_primary = $cdata->GetFieldOption('cust_' . $custom_id, 'force_primary');
if ( $force_primary ) {
$cdata->SetDBField($ml_formatter->LangFieldName('cust_' . $custom_id, true), $this->GetDBField('cust_' . $custom_name));
}
else {
foreach ($languages as $language_id) {
$cdata->SetDBField('l' . $language_id . '_cust_' . $custom_id, $this->GetDBField('l' . $language_id . '_cust_' . $custom_name));
}
}
}
return $cdata->isLoaded() ? $cdata->Update() : $cdata->Create();
}
/**
* Returns specified field value from all selected rows.
* Don't affect current record index
*
* @param string $field
* @param bool $formatted
* @param string $format
* @return Array
*/
public function GetCol($field, $formatted = false, $format = null)
{
if ($formatted) {
return Array (0 => $this->GetField($field, $format));
}
return Array (0 => $this->GetDBField($field));
}
/**
* Set's loaded status of object
*
* @param bool $is_loaded
* @access public
* @todo remove this method, since item can't be marked as loaded externally
*/
public function setLoaded($is_loaded = true)
{
$this->Loaded = $is_loaded;
}
}
\ No newline at end of file
Index: branches/5.2.x/core/units/users/users_item.php
===================================================================
--- branches/5.2.x/core/units/users/users_item.php (revision 15453)
+++ branches/5.2.x/core/units/users/users_item.php (revision 15454)
@@ -1,197 +1,198 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class UsersItem extends kDBItem {
/**
* Returns IDs of groups to which user belongs and membership is not expired
*
* @param bool $force_reload
* @return Array
* @access public
*/
public function getMembershipGroups($force_reload = false)
{
$user_groups = $this->Application->RecallVar('UserGroups');
if ( $user_groups === false || $force_reload ) {
// primary group goes first
$sql = 'SELECT GroupId
FROM ' . TABLE_PREFIX . 'UserGroupRelations
WHERE (PortalUserId = ' . $this->GetID() . ') AND ( (MembershipExpires IS NULL) OR (MembershipExpires >= ' . adodb_mktime() . ') )
ORDER BY IF(GroupId = ' . $this->GetDBField('PrimaryGroupId') . ', 1, 0) DESC';
$groups = $this->Conn->GetCol($sql);
$user_helper = $this->Application->recallObject('UserHelper');
/* @var $user_helper UserHelper */
$user_groups = Array ();
$ip_restrictions = $user_helper->getGroupsWithIPRestrictions();
foreach ($groups as $group_id) {
if ( !isset($ip_restrictions[$group_id]) || kUtil::ipMatch($ip_restrictions[$group_id], "\n") ) {
$user_groups[] = $group_id;
}
}
return $user_groups;
}
return explode(',', $user_groups);
}
function SendEmailEvents()
{
switch ( $this->GetDBField('Status') ) {
case STATUS_ACTIVE:
$event_name = $this->Application->ConfigValue('User_Password_Auto') ? 'USER.VALIDATE' : 'USER.ADD';
$this->Application->EmailEventAdmin($event_name);
$this->Application->EmailEventUser($event_name, $this->GetID());
break;
case STATUS_PENDING:
$this->Application->EmailEventAdmin('USER.ADD.PENDING');
$this->Application->EmailEventUser('USER.ADD.PENDING', $this->GetID());
break;
}
}
/**
* Checks that user is subscriber only
*
* @return bool
*/
public function isSubscriberOnly()
{
return $this->GetDBField('PrimaryGroupId') == $this->Application->ConfigValue('User_SubscriberGroup');
}
/**
* Checks that user is subscribed
*
* @return bool
*/
public function isSubscribed()
{
$group_id = $this->Application->ConfigValue('User_SubscriberGroup');
$sql = 'SELECT GroupId
FROM ' . TABLE_PREFIX . 'UserGroupRelations
WHERE (PortalUserId = ' . $this->GetID() . ') AND (GroupId = ' . $group_id . ')';
return $this->Conn->GetOne($sql);
}
/**
* Creates a record in the database table with current item' values
*
* @param mixed $force_id Set to TRUE to force creating of item's own ID or to value to force creating of passed id. Do not pass 1 for true, pass exactly TRUE!
* @param bool $system_create
* @return bool
* @access public
*/
public function Create($force_id = false, $system_create = false)
{
$ret = parent::Create($force_id, $system_create);
if ( $ret ) {
// find out how to synchronize user only when it's copied to live table
$sync_manager = $this->Application->recallObject('UsersSyncronizeManager', null, Array (), Array ('InPortalSyncronize'));
/* @var $sync_manager UsersSyncronizeManager */
$sync_manager->performAction('createUser', $this->FieldValues);
}
return $ret;
}
/**
* 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, $system_update = false)
+ public function Update($id = null, $update_fields = null, $system_update = false)
{
- $ret = parent::Update($id, $system_update);
+ $ret = parent::Update($id, $update_fields, $system_update);
if ( $ret ) {
// find out how to synchronize user only when it's copied to live table
$sync_manager = $this->Application->recallObject('UsersSyncronizeManager', null, Array (), Array ('InPortalSyncronize'));
/* @var $sync_manager UsersSyncronizeManager */
$sync_manager->performAction('updateUser', $this->FieldValues);
}
return $ret;
}
/**
* Deletes the record from database
*
* @param int $id
* @return bool
* @access public
*/
public function Delete($id = null)
{
$ret = parent::Delete($id);
if ( $ret ) {
$sync_manager = $this->Application->recallObject('UsersSyncronizeManager', null, Array (), Array ('InPortalSyncronize'));
/* @var $sync_manager UsersSyncronizeManager */
$sync_manager->performAction('deleteUser', $this->FieldValues);
}
return $ret;
}
function setName($full_name)
{
$full_name = explode(' ', $full_name);
if (count($full_name) > 2) {
$last_name = array_pop($full_name);
$first_name = implode(' ', $full_name);
}
else {
$last_name = $full_name[1];
$first_name = $full_name[0];
}
$this->SetDBField('FirstName', $first_name);
$this->SetDBField('LastName', $last_name);
}
/**
* Generates new password for given user
*
* @param int $length
* @return string
* @access public
*/
public function generatePassword($length = 10)
{
$password = kUtil::generatePassword($length);
$this->SetField('Password', $password);
$this->SetField('VerifyPassword', $password);
return $password;
}
}
\ No newline at end of file
Index: branches/5.2.x/core/units/images/image_event_handler.php
===================================================================
--- branches/5.2.x/core/units/images/image_event_handler.php (revision 15453)
+++ branches/5.2.x/core/units/images/image_event_handler.php (revision 15454)
@@ -1,496 +1,496 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class ImageEventHandler extends kDBEventHandler {
/**
* Allows to override standard permission mapping
*
* @return void
* @access protected
* @see kEventHandler::$permMapping
*/
protected function mapPermissions()
{
parent::mapPermissions();
$permissions = Array (
'OnCleanImages' => Array ('subitem' => true),
'OnCleanResizedImages' => Array ('subitem' => true),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
/**
* Define alternative event processing method names
*
* @return void
* @see kEventHandler::$eventMethods
* @access protected
*/
protected function mapEvents()
{
parent::mapEvents(); // ensure auto-adding of approve/decline and so on events
$image_events = Array (
'OnAfterCopyToTemp'=>'ImageAction',
'OnBeforeDeleteFromLive'=>'ImageAction',
'OnBeforeCopyToLive'=>'ImageAction',
'OnBeforeItemDelete'=>'ImageAction',
'OnAfterClone'=>'ImageAction',
);
$this->eventMethods = array_merge($this->eventMethods, $image_events);
}
/**
* Returns special of main item for linking with sub-item
*
* @param kEvent $event
* @return string
* @access protected
*/
protected function getMainSpecial(kEvent $event)
{
if ( $event->Special == 'list' && !$this->Application->isAdmin ) {
// ListImages aggregated tag uses this special
return '';
}
return parent::getMainSpecial($event);
}
/**
* Don't allow to delete primary category item image, when there are no more images
*
* @param kEvent $event
* @param string $type
* @return void
* @access protected
*/
protected function customProcessing(kEvent $event, $type)
{
$object = $event->getObject();
/* @var $object kDBItem */
if ( $event->Name == 'OnMassDelete' && $type == 'before' ) {
$ids = $event->getEventParam('ids');
$parent_info = $object->getLinkedInfo($event->Special);
$sql = 'SELECT ImageId
FROM ' . $object->TableName . '
WHERE DefaultImg = 1 AND ' . $parent_info['ForeignKey'] . ' = ' . $parent_info['ParentId'];
$primary_file_id = $this->Conn->GetOne($sql);
if ( $primary_file_id ) {
$file_id_index = array_search($primary_file_id, $ids);
if ( $file_id_index ) {
// allow deleting of primary product file, when there is another file to make primary
$sql = 'SELECT COUNT(*)
FROM ' . $object->TableName . '
WHERE DefaultImg = 0 AND ' . $parent_info['ForeignKey'] . ' = ' . $parent_info['ParentId'];
$non_primary_file_count = $this->Conn->GetOne($sql);
if ( $non_primary_file_count ) {
unset($ids[$file_id_index]);
}
}
}
$event->setEventParam('ids', $ids);
}
switch ($type) {
case 'before' :
// empty unused fields
$object->SetDBField($object->GetDBField('LocalImage') ? 'Url' : 'LocalPath', '');
$object->SetDBField($object->GetDBField('LocalThumb') ? 'ThumbUrl' : 'ThumbPath', '');
if ( $object->GetDBField('SameImages') ) {
$object->SetDBField('LocalImage', 1);
$object->SetDBField('LocalPath', '');
$object->SetDBField('Url', '');
}
break;
case 'after':
// make sure, that there is only one primary image for the item
if ( $object->GetDBField('DefaultImg') ) {
$sql = 'UPDATE ' . $object->TableName . '
SET DefaultImg = 0
WHERE ResourceId = ' . $object->GetDBField('ResourceId') . ' AND ImageId <> ' . $object->GetID();
$this->Conn->Query($sql);
}
break;
}
}
/**
* Performs temp-table related action on current image record
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function ImageAction($event)
{
$id = $event->getEventParam('id');
$object = $this->Application->recallObject($event->Prefix . '.-item', $event->Prefix, Array ('skip_autoload' => true));
/* @var $object kDBItem */
if ( in_array($event->Name, Array ('OnBeforeDeleteFromLive', 'OnAfterClone')) ) {
$object->SwitchToLive();
}
elseif ( $event->Name == 'OnBeforeItemDelete' ) {
// keep current table
}
else {
$object->SwitchToTemp();
}
$object->Load($id);
$file_helper = $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
$fields = Array ('LocalPath' => 'LocalImage', 'ThumbPath' => 'LocalThumb');
foreach ($fields as $a_field => $mode_field) {
$file = $object->GetDBField($a_field);
if ( !$file ) {
continue;
}
$source_file = FULL_PATH . $file;
switch ($event->Name) {
// Copy image files to pending dir and update corresponding fields in temp record
// Checking for existing files and renaming if necessary - two users may upload same pending files at the same time!
case 'OnAfterCopyToTemp':
$file = preg_replace('/^' . preg_quote(IMAGES_PATH, '/') . '/', IMAGES_PENDING_PATH, $file, 1);
$new_file = $file_helper->ensureUniqueFilename(FULL_PATH, $file);
$dst_file = FULL_PATH . $new_file;
copy($source_file, $dst_file);
$object->SetFieldOption($a_field, 'skip_empty', false);
$object->SetDBField($a_field, $new_file);
break;
// Copy image files to live dir (checking if file exists and renaming if necessary)
// and update corresponding fields in temp record (which gets copied to live automatically)
case 'OnBeforeCopyToLive':
if ( $object->GetDBField($mode_field) ) {
// if image is local -> rename file if it exists in live folder
$file = preg_replace('/^' . preg_quote(IMAGES_PENDING_PATH, '/') . '/', IMAGES_PATH, $file, 1);
$new_file = $file_helper->ensureUniqueFilename(FULL_PATH, $file);
$dst_file = FULL_PATH . $new_file;
rename($source_file, $dst_file);
}
else {
// if image is remote url - remove local file (if any), update local file field with empty value
if ( file_exists($source_file) ) {
@unlink($source_file);
}
$new_file = '';
}
$object->SetFieldOption($a_field, 'skip_empty', false);
$object->SetDBField($a_field, $new_file);
break;
case 'OnBeforeDeleteFromLive': // Delete image files from live folder before copying over from temp
case 'OnBeforeItemDelete': // Delete image files when deleting Image object
@unlink(FULL_PATH . $file);
break;
case 'OnAfterClone':
// Copy files when cloning objects, renaming it on the fly
$new_file = $file_helper->ensureUniqueFilename(FULL_PATH, $file);
$dst_file = FULL_PATH . $new_file;
copy($source_file, $dst_file);
$object->SetFieldOption($a_field, 'skip_empty', false);
$object->SetDBField($a_field, $new_file);
break;
}
}
if ( in_array($event->Name, Array ('OnAfterClone', 'OnBeforeCopyToLive', 'OnAfterCopyToTemp')) ) {
- $object->Update(null, true);
+ $object->Update(null, null, true);
}
}
/**
* Sets primary image of user/category/category item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSetPrimary($event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$object->SetDBField('DefaultImg', 1);
$object->Update();
}
/**
* Occurs before updating item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemUpdate(kEvent $event)
{
parent::OnBeforeItemUpdate($event);
$this->processImageStatus($event);
}
/**
* Occurs after creating item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemCreate(kEvent $event)
{
parent::OnAfterItemCreate($event);
$this->processImageStatus($event);
$object = $event->getObject();
/* @var $object kDBItem */
$object->Update();
}
/**
* Occurs before item changed
*
* @param kEvent $event
*/
function processImageStatus($event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$id = $object->GetDBField('ResourceId');
$sql = 'SELECT ImageId
FROM ' . $object->TableName . '
WHERE ResourceId = ' . $id . ' AND DefaultImg = 1';
$primary_image_id = $this->Conn->GetOne($sql);
if ( !$primary_image_id ) {
$object->SetDBField('DefaultImg', 1);
}
if ( $object->GetDBField('DefaultImg') && $object->Validate() ) {
$sql = 'UPDATE ' . $object->TableName . '
SET DefaultImg = 0
WHERE ResourceId = ' . $id . ' AND ImageId <> ' . $object->GetDBField('ImageId');
$this->Conn->Query($sql);
$object->SetDBField('Enabled', 1);
}
}
/**
* Apply any custom changes to list's sql query
*
* @param kEvent $event
* @return void
* @access protected
* @see kDBEventHandler::OnListBuild()
*/
protected function SetCustomQuery(kEvent $event)
{
parent::SetCustomQuery($event);
$object = $event->getObject();
/* @var $object kDBList */
if ( !$this->Application->isAdminUser ) {
$object->addFilter('active', '%1$s.Enabled = 1');
}
$product_id = $event->getEventParam('product_id');
if ( $product_id ) {
$object->removeFilter('parent_filter');
$sql = 'SELECT ResourceId
FROM ' . $this->Application->getUnitOption('p', 'TableName') . '
WHERE ProductId = ' . $product_id;
$resource_id = (int)$this->Conn->GetOne($sql);
$object->addFilter('product_images', '%1$s.ResourceId = ' . $resource_id);
}
$search_helper = $this->Application->recallObject('SearchHelper');
/* @var $search_helper kSearchHelper */
$types = $event->getEventParam('types');
$except_types = $event->getEventParam('except');
$type_clauses = $this->getTypeClauses($event);
$search_helper->SetComplexFilter($event, $type_clauses, $types, $except_types);
}
/**
* Return type clauses for list bulding on front
*
* @param kEvent $event
* @return Array
*/
function getTypeClauses($event)
{
$type_clauses = Array ();
$type_clauses['additional']['include'] = '%1$s.DefaultImg != 1';
$type_clauses['additional']['except'] = '%1$s.DefaultImg = 1';
$type_clauses['additional']['having_filter'] = false;
return $type_clauses;
}
/**
* [SCHEDULED TASK] Remove unused images from "/system/images" and "/system/images/pending" folders
*
* @param kEvent $event
*/
function OnCleanImages($event)
{
// 1. get images, that are currently in use
$active_images = $this->_getActiveImages( $this->Application->getUnitOption('img', 'TableName') );
$active_images[] = 'noimage.gif';
// 2. get images on disk
$this->_deleteUnusedImages(FULL_PATH . IMAGES_PATH, $active_images);
// 3. get images in use from "images/pending" folder
$active_images = $this->_getPendingImages();
// 4. get image on disk
$this->_deleteUnusedImages(FULL_PATH . IMAGES_PENDING_PATH, $active_images);
}
/**
* Gets image filenames (no path) from given table
*
* @param string $image_table
* @return Array
*/
function _getActiveImages($image_table)
{
$sql = 'SELECT LocalPath, ThumbPath
FROM ' . $image_table . '
WHERE COALESCE(LocalPath, "") <> "" OR COALESCE(ThumbPath) <> ""';
$images = $this->Conn->Query($sql);
$active_images = Array ();
foreach ($images as $image) {
if ($image['LocalPath']) {
$active_images[] = basename($image['LocalPath']);
}
if ($image['ThumbPath']) {
$active_images[] = basename($image['ThumbPath']);
}
}
return $active_images;
}
/**
* Gets active images, that are currently beeing edited inside temporary tables
*
* @return Array
*/
function _getPendingImages()
{
$tables = $this->Conn->GetCol('SHOW TABLES');
$mask_edit_table = '/'.TABLE_PREFIX.'ses_(.*)_edit_' . TABLE_PREFIX . 'CatalogImages/';
$active_images = Array ();
foreach ($tables as $table) {
if (!preg_match($mask_edit_table, $table)) {
continue;
}
$active_images = array_unique( array_merge($active_images, $this->_getActiveImages($table)) );
}
return $active_images;
}
/**
* Deletes all files in given path, except of given $active_images
*
* @param string $path
* @param Array $active_images
*/
function _deleteUnusedImages($path, &$active_images)
{
$images = glob($path . '*.*');
if ($images) {
$images = array_map('basename', $images);
// delete images, that are on disk, but are not mentioned in CatalogImages table
$delete_images = array_diff($images, $active_images);
foreach ($delete_images as $delete_image) {
unlink($path . $delete_image);
}
}
}
/**
* [SCHEDULED TASK] Remove all images from "/system/images/resized" and "/system/images/pending/resized" folders
*
* @param kEvent $event
*/
function OnCleanResizedImages($event)
{
$images = glob(FULL_PATH . IMAGES_PATH . 'resized/*.*');
if ($images) {
foreach ($images as $image) {
unlink($image);
}
}
$images = glob(FULL_PATH . IMAGES_PENDING_PATH . 'resized/*.*');
if ($images) {
foreach ($images as $image) {
unlink($image);
}
}
}
}
\ No newline at end of file

Event Timeline