Index: branches/RC/core/kernel/db/dbitem.php =================================================================== diff -u -N --- branches/RC/core/kernel/db/dbitem.php (revision 12006) +++ branches/RC/core/kernel/db/dbitem.php (revision 0) @@ -1,1291 +0,0 @@ -ErrorMsgs['required'] = '!la_err_required!'; //'Field is required'; - $this->ErrorMsgs['unique'] = '!la_err_unique!'; //'Field value must be unique'; - $this->ErrorMsgs['value_out_of_range'] = '!la_err_value_out_of_range!'; //'Field is out of range, possible values from %s to %s'; - $this->ErrorMsgs['length_out_of_range'] = '!la_err_length_out_of_range!'; //'Field is out of range'; - $this->ErrorMsgs['bad_type'] = '!la_err_bad_type!'; //'Incorrect data format, please use %s'; - $this->ErrorMsgs['invalid_format'] = '!la_err_invalid_format!'; //'Incorrect data format, please use %s'; - $this->ErrorMsgs['bad_date_format'] = '!la_err_bad_date_format!'; //'Incorrect date format, please use (%s) ex. (%s)'; - $this->ErrorMsgs['primary_lang_required'] = '!la_err_primary_lang_required!'; - } - - function SetDirtyField($field_name, $field_value) - { - $this->DirtyFieldValues[$field_name] = $field_value; - } - - function GetDirtyField($field_name) - { - return $this->DirtyFieldValues[$field_name]; - } - - 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; - } - - $options = $this->GetFieldOptions($field_name); - $res = $value; - if (array_key_exists('formatter', $options)) { - $formatter =& $this->Application->recallObject($options['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 - */ - function SetOriginalField($field_name, $field_value) - { - $this->OriginalFieldValues[$field_name] = $field_value; - } - - /** - * Set's default values for all fields - * - * @param bool $populate_ml_fields create all ml fields from db in config or not - * - * @access public - */ - function SetDefaultValues($populate_ml_fields = false) - { - parent::SetDefaultValues($populate_ml_fields); - if ($populate_ml_fields) { - $this->PopulateMultiLangFields(); - } - - foreach ($this->Fields as $field => $params) { - if ( isset($params['default']) ) { - $this->SetDBField($field, $params['default']); - } - else { - $this->SetDBField($field, NULL); - } - } - } - - /** - * 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 - */ - 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'); - $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 - */ - function SetDBField($name,$value) - { - $this->FieldValues[$name] = $value; - /*if (isset($this->Fields[$name]['formatter'])) { - $formatter =& $this->Application->recallObject($this->Fields[$name]['formatter']); - $formatter->UpdateSubFields($name, $value, $this->Fields[$name], $this); - }*/ - } - - /** - * Set's field error, if pseudo passed not found then create it with message text supplied. - * Don't owerrite existing pseudo translation. - * - * @param string $field - * @param string $pseudo - * @param string $error_label - */ - function SetError($field, $pseudo, $error_label = null, $error_params = null) - { - $error_field = isset($this->Fields[$field]['error_field']) ? $this->Fields[$field]['error_field'] : $field; - if (isset($this->FieldErrors[$error_field]['pseudo'])) { - // don't set more then one error on field - return ; - } - - $this->FieldErrors[$error_field]['pseudo'] = $pseudo; - - if (isset($error_params)) { - // additional params, that helps to determine error sources - $this->FieldErrors[$error_field]['params'] = $error_params; - } - - if (isset($error_label) && !isset($this->ErrorMsgs[$pseudo])) { - // label for error (only when not already set) - $this->ErrorMsgs[$pseudo] = (substr($error_label, 0, 1) == '+') ? substr($error_label, 1) : '!'.$error_label.'!'; - } - } - - /** - * Return current item' field value by field name - * (doesn't apply formatter) - * - * @access public - * @param string $name field name to return - * @return mixed - */ - function GetDBField($name) - { - return $this->FieldValues[$name]; - } - - function HasField($name) - { - return isset($this->FieldValues[$name]); - } - - 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 acutally setting the fields - * - * @access public - * @param Array $hash - * @param Array $set_fields Optional param, field names in target object to set, other fields will be skipped - * @return void - */ - function SetFieldsFromHash($hash, $set_fields = null) - { - // used in formatter which work with multiple fields together - foreach($hash as $field_name => $field_value) { - if (is_numeric($field_name) || !array_key_exists($field_name, $this->Fields)) { - continue; - } - - if (is_array($set_fields) && !in_array($field_name, $set_fields)) { - continue; - } - - $this->SetDirtyField($field_name, $field_value); - } - - // formats all fields using associated formatters - foreach ($hash as $field_name => $field_value) - { - if (is_numeric($field_name) || !array_key_exists($field_name, $this->Fields)) { - continue; - } - - if (is_array($set_fields) && !in_array($field_name, $set_fields)) { - continue; - } - - $this->SetField($field_name,$field_value); - } - } - - function SetDBFieldsFromHash($hash, $set_fields = null) - { - foreach ($hash as $field_name => $field_value) { - if (is_numeric($field_name) || !array_key_exists($field_name, $this->Fields)) { - continue; - } - - if (is_array($set_fields) && !in_array($field_name, $set_fields)) { - continue; - } - - $this->SetDBField($field_name, $field_value); - } - } - - /** - * 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( !isset($keys_hash) ) $keys_hash = Array($this->IDField => $this->ID); - - $ret = ''; - foreach($keys_hash as $field => $value) - { - if (!preg_match('/\./', $field)) { - $ret .= '(`'.$this->TableName.'`.'.$field.' = '.$this->Conn->qstr($value).') AND '; - } - else { - $ret .= '('.$field.' = '.$this->Conn->qstr($value).') AND '; - } - } - - return preg_replace('/(.*) AND $/', '\\1', $ret); - } - - /** - * 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 - * @return bool True if item has been loaded, false otherwise - */ - function Load($id, $id_field_name = null) - { - if ( isset($id_field_name) ) { - $this->SetIDField($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->setIDField( $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; - $field_values = $this->Conn->GetRow($q); - - if ($field_values) { - $this->FieldValues = array_merge_recursive2($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; - } - - /** - * Builds select sql, SELECT ... FROM parts only - * - * @access public - * @return string - */ - function GetSelectSQL() - { - $sql = $this->addCalculatedFields($this->SelectClause); - return parent::GetSelectSQL($sql); - } - - function UpdateFormattersMasterFields() - { - foreach ($this->Fields as $field => $options) { - if (isset($options['formatter'])) { - $formatter =& $this->Application->recallObject($options['formatter']); - $formatter->UpdateMasterFields($field, $this->GetDBField($field), $options, $this); - } - } - } - - /** - * Allows to skip certain fields from getting into sql queries - * - * @param string $field_name - * @param mixed $force_id - * @return bool - */ - 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; - $skip_empty = array_key_exists('skip_empty', $this->Fields[$field_name]) ? $this->Fields[$field_name]['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 Primery Key Id to update - * @return bool - */ - function Update($id = 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->raiseEvent('OnAfterItemValidate')) { - return false; - } - - if (!$this->FieldValues) { - // nothing to update - return true; - } - - $sql = ''; - - foreach ($this->FieldValues as $field_name => $field_value) { - if ($this->skipField($field_name)) { - 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 - $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 - return false; - } - - $affected = $this->Conn->getAffectedRows(); - if (!$system_update && $affected == 1) { - $this->setModifiedFlag(clUPDATE); - } - - $this->saveCustomFields(); - $this->raiseEvent('OnAfterItemUpdate'); - $this->Loaded = true; - - if ($this->mode != 't') { - $this->Application->resetCounters($this->TableName); - } - - return true; - } - - function ValidateField($field) - { - $options = $this->Fields[$field]; - - /*if (isset($options['formatter'])) { - $formatter =& $this->Application->recallObject($options['formatter']); - $formatter->UpdateMasterFields($field, $this->GetDBField($field), $options, $this); - }*/ - - $error_field = isset($options['error_field']) ? $options['error_field'] : $field; - $res = !isset($this->FieldErrors[$error_field]['pseudo']) || !$this->FieldErrors[$error_field]['pseudo']; - - $res = $res && $this->ValidateRequired($field, $options); - $res = $res && $this->ValidateType($field, $options); - $res = $res && $this->ValidateRange($field, $options); - $res = $res && $this->ValidateUnique($field, $options); - $res = $res && $this->CustomValidation($field, $options); - - return $res; - } - - /** - * Validate all item fields based on - * constraints set in each field options - * in config - * - * @return bool - * @access private - */ - function Validate() - { - $this->UpdateFormattersMasterFields(); //order is critical - should be called BEFORE checking errors - - if ($this->IgnoreValidation) { - return true; - } - - $global_res = true; - foreach ($this->Fields as $field => $params) { - $res = $this->ValidateField($field); - - - $global_res = $global_res && $res; - } - - if (!$global_res && $this->Application->isDebugMode()) { - $error_msg = ' Validation failed in prefix '.$this->Prefix.', - FieldErrors follow (look at items with "pseudo" key set)
- You may ignore this notice if submitted data really has a validation error'; - trigger_error(trim($error_msg), E_USER_NOTICE); - $this->Application->Debugger->dumpVars($this->FieldErrors); - } - - return $global_res; - } - - /** - * Check field value by user-defined alghoritm - * - * @param string $field field name - * @param Array $params field options from config - * @return bool - */ - function CustomValidation($field, $params) - { - return true; - } - - /** - * Check if item has errors - * - * @param Array $skip_fields fields to skip during error checking - * @return bool - */ - function HasErrors($skip_fields) - { - $global_res = false; - - foreach ($this->Fields as $field => $field_params) { - // If Formatter has set some error messages during values parsing - if ( !( in_array($field, $skip_fields) ) && - isset($this->FieldErrors[$field]['pseudo']) && $this->FieldErrors[$field] != '') { - $global_res = true; - } - } - return $global_res; - } - - /** - * Check if value in field matches field type specified in config - * - * @param string $field field name - * @param Array $params field options from config - * @return bool - */ - function ValidateType($field, $params) - { - $res = true; - $val = $this->FieldValues[$field]; - if ( $val != '' && - isset($params['type']) && - preg_match("#int|integer|double|float|real|numeric|string#", $params['type']) - ) { - if ($params['type'] == 'numeric') { - trigger_error('Invalid field type '.$params['type'].' (in ValidateType method), please use float instead', E_USER_NOTICE); - $params['type'] = 'float'; - } - $res = is_numeric($val); - if ($params['type']=='string' || $res) { - $f = 'is_'.$params['type']; - settype($val, $params['type']); - $res = $f($val) && ($val == $this->FieldValues[$field]); - } - if (!$res) { - $this->SetError($field, 'bad_type', null, $params['type']); - } - } - return $res; - } - - /** - * Check if value is set for required field - * - * @param string $field field name - * @param Array $params field options from config - * @return bool - * @access private - */ - function ValidateRequired($field, $params) - { - $res = true; - if (isset($params['required']) && $params['required']) { - $check_value = $this->FieldValues[$field]; - if ($this->Application->ConfigValue('TrimRequiredFields')) { - $check_value = trim($check_value); - } - $res = ((string)$check_value != ''); - } - - if (!$res) { - $this->SetError($field, 'required'); - } - - return $res; - } - - /** - * Validates that current record has unique field combination among other table records - * - * @param string $field field name - * @param Array $params field options from config - * @return bool - * @access private - */ - function ValidateUnique($field, $params) - { - $res = true; - $unique_fields = getArrayValue($params,'unique'); - if($unique_fields !== false) - { - $where = Array(); - array_push($unique_fields,$field); - foreach($unique_fields as $unique_field) - { - // if field is not empty or if it is required - we add where condition - if ((string)$this->GetDBField($unique_field) != '' || (isset($this->Fields[$unique_field]['required']) && $this->Fields[$unique_field]['required'])) { - $where[] = '`'.$unique_field.'` = '.$this->Conn->qstr( $this->GetDBField($unique_field) ); - } - else { - // not good if we check by less fields than indicated - return true; - } - } - // This can ONLY happen if all unique fields are empty and not required. - // In such case we return true, because if unique field is not required there may be numerous empty values -// if (!$where) return true; - - $sql = 'SELECT COUNT(*) FROM %s WHERE ('.implode(') AND (',$where).') AND ('.$this->IDField.' <> '.(int)$this->ID.')'; - - $res_temp = $this->Conn->GetOne( str_replace('%s', $this->TableName, $sql) ); - - $current_table_only = getArrayValue($params, 'current_table_only'); // check unique record only in current table - $res_live = $current_table_only ? 0 : $this->Conn->GetOne( str_replace('%s', $this->Application->GetLiveName($this->TableName), $sql) ); - - $res = ($res_temp == 0) && ($res_live == 0); - - if (!$res) { - $this->SetError($field, 'unique'); - } - } - return $res; - } - - /** - * Check if field value is in range specified in config - * - * @param string $field field name - * @param Array $params field options from config - * @return bool - * @access private - */ - function ValidateRange($field, $params) - { - $res = true; - $val = $this->FieldValues[$field]; - - if ( isset($params['type']) && preg_match("#int|integer|double|float|real#", $params['type']) && strlen($val) > 0 ) { - if ( isset($params['max_value_inc'])) { - $res = $res && $val <= $params['max_value_inc']; - $max_val = $params['max_value_inc'].' (inclusive)'; - } - if ( isset($params['min_value_inc'])) { - $res = $res && $val >= $params['min_value_inc']; - $min_val = $params['min_value_inc'].' (inclusive)'; - } - if ( isset($params['max_value_exc'])) { - $res = $res && $val < $params['max_value_exc']; - $max_val = $params['max_value_exc'].' (exclusive)'; - } - if ( isset($params['min_value_exc'])) { - $res = $res && $val > $params['min_value_exc']; - $min_val = $params['min_value_exc'].' (exclusive)'; - } - } - if (!$res) { - if ( !isset($min_val) ) $min_val = '-∞'; - if ( !isset($max_val) ) $max_val = '∞'; - - $this->SetError($field, 'value_out_of_range', null, Array ($min_val, $max_val)); - return $res; - } - if ( isset($params['max_len'])) { - $res = $res && mb_strlen($val) <= $params['max_len']; - } - if ( isset($params['min_len'])) { - $res = $res && mb_strlen($val) >= $params['min_len']; - } - if (!$res) { - $error_params = Array (getArrayValue($params, 'min_len'), getArrayValue($params, 'max_len')); - $this->SetError($field, 'length_out_of_range', null, $error_params); - return $res; - } - return $res; - } - - /** - * Return error message for field - * - * @param string $field - * @return string - * @access public - */ - function GetErrorMsg($field, $force_escape = null) - { - if( !isset($this->FieldErrors[$field]) ) return ''; - - $err = getArrayValue($this->FieldErrors[$field], 'pseudo'); - if (!$err) return ''; - // if special error msg defined in config - if( isset($this->Fields[$field]['error_msgs'][$err]) ) - { - $msg = $this->Fields[$field]['error_msgs'][$err]; - } - else //fall back to defaults - { - if( !isset($this->ErrorMsgs[$err]) ) { - trigger_error('No user message is defined for pseudo error '.$err.'
', E_USER_WARNING); - return $err; //return the pseudo itself - } - $msg = $this->ErrorMsgs[$err]; - } - $msg = $this->Application->ReplaceLanguageTags($msg, $force_escape); - - if ( isset($this->FieldErrors[$field]['params']) ) - { - return vsprintf($msg, $this->FieldErrors[$field]['params']); - } - return $msg; - } - - /** - * 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! - * @access public - * @return bool - */ - 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 (!$this->raiseEvent('OnAfterItemValidate')) { - 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)) { - // 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) { - 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); - - if (!$system_create){ - $this->setModifiedFlag(clCREATE); - } - - $this->saveCustomFields(); - if ($this->mode != 't') { - $this->Application->resetCounters($this->TableName); - } - $this->raiseEvent('OnAfterItemCreate'); - $this->Loaded = true; - - return true; - } - - /** - * Deletes the record from databse - * - * @access public - * @return bool - */ - 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(); - - $this->setModifiedFlag(clDELETE); // will change affected rows, so get it before this line - - if ($affected_rows > 0) { - // something was actually deleted - $this->raiseEvent('OnAfterItemDelete'); - } - - if ($this->mode != 't') { - $this->Application->resetCounters($this->TableName); - } - - return $ret; - } - - function PopulateMultiLangFields() - { - $ml_helper =& $this->Application->recallObject('kMultiLanguageHelper'); - /* @var $ml_helper kMultiLanguageHelper */ - - $lang_count = $ml_helper->getLanguageCount(); - foreach ($this->Fields as $field => $options) - { - // master field is set only for CURRENT language - if (isset($options['formatter']) && $options['formatter'] == 'kMultiLanguage' && isset($options['master_field'])) { - if (preg_match('/^l([0-9]+)_(.*)/', $field, $regs)) { - $l = $regs[1]; - $name = $regs[2]; - // 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 will not be displayed - $this->Fields['l' . $l . '_' . $name]['error_field'] = 'l' . $l . '_' . $name; - for ($i = 1; $i <= $lang_count; $i++) { - if ($i == $l || !$ml_helper->LanguageFound($i)) continue; - $f_options = $options; - $f_options['error_field'] = 'l' . $i . '_' . $name; // set error field back to itself - see comment above - if ($i != $this->Application->GetDefaultLanguageId()) { - unset($f_options['required']); // all non-primary language field set to non-required - } - $this->Fields['l' . $i . '_' . $name] = $f_options; - } - } - } - } - } - - /** - * Sets new name for item in case if it is beeing 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 private - */ - 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); - } - - function raiseEvent($name, $id = null, $additional_params = Array()) - { - if( !isset($id) ) $id = $this->GetID(); - $event = new kEvent( Array('name'=>$name,'prefix'=>$this->Prefix,'special'=>$this->Special) ); - $event->setEventParam('id', $id); - - if ($additional_params) { - foreach ($additional_params as $ap_name => $ap_value) { - $event->setEventParam($ap_name, $ap_value); - } - } - - $this->Application->HandleEvent($event); - return $event->status == erSUCCESS ? true : false; - } - - /** - * Set's new ID for item - * - * @param int $new_id - * @access public - */ - function setID($new_id) - { - $this->ID = $new_id; - $this->SetDBField($this->IDField, $new_id); - } - - /** - * Generate and set new temporary id - * - * @access private - */ - 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()) { - // 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()) { - $changes[$key]['ItemId'] = $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 - * - * @access private - * @author Alexey - */ - function setModifiedFlag($mode = null) - { - $main_prefix = $this->Application->GetTopmostPrefix($this->Prefix); - $this->Application->StoreVar($main_prefix.'_modified', '1'); - - if ($this->ShouldLogChanges()) { - $this->LogChanges($main_prefix, $mode); - if (!$this->IsTempTable()) { - $handler =& $this->Application->recallObject($this->Prefix.'_EventHandler'); - $ses_var_name = $main_prefix.'_changes_'.$this->Application->GetTopmostWid($this->Prefix); - $handler->SaveLoggedChanges($ses_var_name); - } - } - } - - /** - * Determines, that changes made to this item should be written to change log - * - * @return bool - */ - function ShouldLogChanges() - { - $log_changes = $this->Application->getUnitOption($this->Prefix, 'LogChanges') || $this->Application->ConfigValue('UseChangeLog'); - - return $log_changes && !$this->Application->getUnitOption($this->Prefix, 'ForceDontLogChanges'); - } - - 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(); - - $general = array( - 'Prefix' => $this->Prefix, - 'ItemId' => $this->GetID(), - 'OccuredOn' => adodb_mktime(), - 'MasterPrefix' => $main_prefix, - 'MasterId' => $this->Prefix == $main_prefix ? $this->GetID() : $this->Application->GetVar($main_prefix.'_id'), // is that correct (Kostja)?? - 'Action' => $mode, - ); - switch ($mode) { - case clUPDATE: - $changes[] = array_merge($general, Array( - 'Changes' => serialize(array_merge($this->GetTitleField(), $this->GetChangedFields())), - )); - break; - case clCREATE: - $changes[] = array_merge($general, Array( - 'Changes' => serialize($this->GetTitleField()), - )); - break; - case clDELETE: - $changes[] = array_merge($general, Array( - 'Changes' => serialize(array_merge($this->GetTitleField(), $this->GetRealFields())), - )); - } - - $this->Application->StoreVar($ses_var_name, serialize($changes)); - } - - function GetTitleField() - { - $title_field = $this->Application->getUnitOption($this->Prefix, 'TitleField'); - if ($title_field && $this->GetField($title_field)) { - return Array($title_field => $this->GetField($title_field)); - } - } - - function GetRealFields() - { - if (function_exists('array_diff_key')) { - $db_fields = array_diff_key($this->FieldValues, $this->VirtualFields, $this->CalculatedFields); - } - else { - $db_fields = array(); - foreach ($this->FieldValues as $key => $value) { - if (array_key_exists($key, $this->VirtualFields) || array_key_exists($key, $this->CalculatedFields)) continue; - $db_fields[$key] = $value; - } - } - return $db_fields; - } - - function GetChangedFields() - { - $changes = array(); - - $diff = array_diff_assoc($this->GetRealFields(), $this->OriginalFieldValues); - foreach ($diff as $field => $new_value) { - $changes[$field] = array('old' => $this->GetOriginalField($field, true), 'new' => $this->GetField($field)); - } - return $changes; - } - - /** - * Returns ID of currently processed record - * - * @return int - * @access public - */ - function GetID() - { - return $this->ID; - } - - /** - * Generates ID for new items before inserting into database - * - * @return int - * @access private - */ - function generateID() - { - return 0; - } - - /** - * Returns true if item was loaded successfully by Load method - * - * @return bool - */ - function isLoaded() - { - return $this->Loaded; - } - - /** - * Checks if field is required - * - * @param string $field - * @return bool - */ - function isRequired($field) - { - return getArrayValue( $this->Fields[$field], 'required' ); - } - - /** - * Sets new required flag to field - * - * @param string $field - * @param bool $is_required - */ - function setRequired($field, $is_required = true) - { - $this->Fields[$field]['required'] = $is_required; - } - - function Clear($new_id = null) - { - $this->setID($new_id); - $this->Loaded = false; - $this->FieldValues = Array(); - $this->OriginalFieldValues = Array (); - $this->SetDefaultValues(); - $this->FieldErrors = Array(); - return $this->Loaded; - } - - function Query($force = false) - { - if( $this->Application->isDebugMode() ) - { - $this->Application->Debugger->appendTrace(); - } - trigger_error('Query method is called in class '.get_class($this).' for prefix '.$this->getPrefixSpecial().'', E_USER_ERROR); - } - - function saveCustomFields() - { - if (!$this->customFields) { - return true; - } - - $cdata_key = rtrim($this->Prefix.'-cdata.'.$this->Special, '.'); - $cdata =& $this->Application->recallObject($cdata_key, null, Array('skip_autoload' => true, 'populate_ml_fields' => true)); - - $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 */ - - foreach ($this->customFields as $custom_id => $custom_name) { - $force_primary = isset($cdata->Fields['cust_'.$custom_id]['force_primary']) && $cdata->Fields['cust_'.$custom_id]['force_primary']; - $cdata->SetDBField($ml_formatter->LangFieldName('cust_'.$custom_id, $force_primary), $this->GetDBField('cust_'.$custom_name)); - } - - if ($cdata->isLoaded()) { - $ret = $cdata->Update(); - } - else { - $ret = $cdata->Create(); - if ($cdata->mode == 't') $cdata->setTempID(); - } - - return $ret; - } - - /** - * Returns specified field value from all selected rows. - * Don't affect current record index - * - * @param string $field - * @return Array - */ - function GetCol($field) - { - return Array (0 => $this->GetDBField($field)); - } -} - - - -?> \ No newline at end of file