Index: trunk/core/kernel/startup.php =================================================================== diff -u -N --- trunk/core/kernel/startup.php (revision 0) +++ trunk/core/kernel/startup.php (revision 1560) @@ -0,0 +1,92 @@ + \ No newline at end of file Index: trunk/core/kernel/parser/template_parser.php =================================================================== diff -u -N --- trunk/core/kernel/parser/template_parser.php (revision 0) +++ trunk/core/kernel/parser/template_parser.php (revision 1560) @@ -0,0 +1,480 @@ +Ses =& $this->Application->recallObject('Session'); + } + + function AddParam($pattern, $value, $dont_sort=0) + { + $this->ForSort[] = Array($pattern, $value); + if (!$dont_sort) //used when mass-adding params, to escape sorting after every new param + $this->SortParams(); //but do sort by default! + } + + //We need to sort params by its name length desc, so that params starting with same word get parsed correctly + function SortParams() + { + uasort($this->ForSort, array ("TemplateParser", "CmpParams")); + $this->Pattern = Array(); + $this->Values = Array(); + foreach($this->ForSort as $pair) + { + $this->Pattern[] = $pair[0]; + $this->Values[] = $pair[1]; + } + } + + function CmpParams($a, $b) + { + $a_len = strlen($a[0]); + $b_len = strlen($b[0]); + if ($a_len == $b_len) return 0; + return $a_len > $b_len ? -1 : 1; + } + + function SetParams($params) + { + if (!is_array($params)) $params = Array(); + $this->Params = $params; + foreach ($params as $key => $val) { + $this->AddParam('/[{]{0,1}\$'.$key.'[}]{0,1}/i', $val, 1); //Do not sort every time + } + $this->SortParams(); //Sort once after adding is done + } + + function GetParam($name) + { + //return isset($this->Params[strtolower($name)]) ? $this->Params[strtolower($name)] : false; + return isset($this->Params[$name]) ? $this->Params[$name] : false; + } + + function SetParam($name, $value) + { + $this->Params[strtolower($name)] = $value; + } + + function SetBuffer($body) + { + $this->Buffers[$this->RecursionIndex] = $body; + } + + function GetBuffer() + { + return $this->Buffers[$this->RecursionIndex]; + } + + function GetCode() + { + return $this->Code[$this->RecursionIndex]; + } + + function AppendBuffer($append) + { + $this->Buffers[$this->RecursionIndex] .= $append; + $this->AppendCode( $this->ConvertToCode($append) ); + } + + function AppendOutput($append, $append_code=false) + { + if ($this->SkipMode == parse) { + $this->Output .= $append; //append to Ouput only if we are parsing + if ($append_code) $this->AppendCompiledHTML($append); + } + elseif ($this->SkipMode == skip) { + if ($append_code) $this->AppendCompiledHTML($append); + } + elseif ($this->SkipMode == skip_tags) { + $this->AppendBuffer($append); //append to buffer if we are skipping tags + } + } + + function ConvertToCode($data) + { + $code = '$o .= \''. str_replace("'", "\'", $data) .'\';'; + $code = explode("\n", $code); + return $code; + } + + function AppendCode($code) + { + if (defined('EXPERIMENTAL_PRE_PARSE')) { + if (!isset($this->Code[$this->RecursionIndex])) { + $this->Code[$this->RecursionIndex] = Array(); + } + if (is_array($code)) { + foreach ($code as $line) { + $this->Code[$this->RecursionIndex][] = rtrim($line, "\n")."\n"; + } + } + else { + $this->Code[$this->RecursionIndex][] .= rtrim($code, "\n")."\n"; + } + } + } + + function AppendCompiledFunction($f_name, $f_body) + { + if (defined('EXPERIMENTAL_PRE_PARSE')) { + if ( isset($this->Application->CompiledFunctions[$f_name]) || + function_exists($f_name) + ) { + if (!isset($this->Application->CompiledFunctions[$f_name])) { + $real_name = $f_name.'_'; + } + else { + $real_name = $this->Application->CompiledFunctions[$f_name].'_'; + } + } + else { + $real_name = $f_name; + } + + $this->CompiledBuffer .= 'if (!function_exists(\''.$real_name.'\')) {'."\n"; + + + $this->CompiledBuffer .= "\t".'$application->PreParsedBlocks[\''.$f_name.'\'] = \''.$real_name.'\';'; + $this->CompiledBuffer .= "\n\t".'function '.$real_name.'($params)'."\n\t{\n"; + $this->CompiledBuffer .= $f_body; + $this->CompiledBuffer .= "\t}\n\n"; + + $this->CompiledBuffer .= '}'."\n"; + + $this->Application->CompiledFunctions[$f_name] = $real_name; + + } + } + + function AppendCompiledCode($code) + { + if (defined('EXPERIMENTAL_PRE_PARSE')) { + if (is_array($code)) { + foreach ($code as $line) { + $this->CompiledBuffer .= "\t".rtrim($line, "\n")."\n"; + } + } + else { + $this->CompiledBuffer .= $code; + } + $this->CompiledBuffer .= "\t".'echo $o;'."\n\t".'$o = \'\';'."\n"; + } + } + + function AppendCompiledHTML($append) + { + if (defined('EXPERIMENTAL_PRE_PARSE')) { + $this->CompiledBuffer .= '?'.'>'."\n"; + $this->CompiledBuffer .= rtrim($append, "\t"); + $this->CompiledBuffer .= '<'.'?php'."\n"; + } + } + + function ResetCode() + { + $this->Code[$this->RecursionIndex] = Array(); + } + + function FindTag2() + { + $openings = Array('<%' => '%>', ' Array('>', '/>'), ' '>', '' => ''); + + $tag_open_pos = false; + foreach ($openings as $an_opening => $closings) { + $pos = strpos($this->Template, $an_opening, $this->Position); + if ($pos !== false && ($tag_open_pos === false || (int) $pos <= (int) $tag_open_pos)) { + $tag_open_pos = $pos; + $open_len = strlen($an_opening); + $opening_tag = $an_opening; + $tag_closings = $closings; + } + } + + if ($tag_open_pos === false) { //If no tags left - adding all other data + $this->AppendOutput(substr($this->Template, $this->Position), true); + return false; + } + + //Adding all data before tag open + $this->AppendOutput(substr($this->Template, $this->Position, $tag_open_pos - $this->Position), true); + + + if (is_array($tag_closings)) { + $tag_close_pos = false; + foreach ($tag_closings as $a_closing) { + $pos = strpos($this->Template, $a_closing, $tag_open_pos); + if ($pos !== false && ($tag_close_pos === false || (int) $pos <= (int) $tag_close_pos)) { + $tag_close_pos = $pos; + $closing_tag = $a_closing; + } + } + } + elseif ($opening_tag == '') { + $closing_tag = ''; + $tag_close_pos = $tag_open_pos + $open_len; + } + else { + $closing_tag = $tag_closings; + $tag_close_pos = strpos($this->Template, $closing_tag, $tag_open_pos); + } + $close_len = strlen($closing_tag); + + //Cutting out the tag itself + $tag = substr($this->Template, $tag_open_pos + $open_len, $tag_close_pos - $tag_open_pos - $open_len); + + + if ($opening_tag == '') { //empty closing means old style in-portal if .... + $tag = 'm:endif'; + } + + if ($opening_tag == 'Params, 'PrefixSpecial'); + $tag = $prefix.$tag; + } + + // temporary - for backward compatability with in-portal style if + if ($opening_tag == '' && $tag_part != 'm_if' && $tag_part != 'm_DefineElement') { + if (strpos($the_tag, ' ') !== false) { + list($function, $params) = explode(' ', $the_tag, 2); + } + else { + $function = $the_tag; + $params = ''; + } + $tag = 'm:if prefix="'.$prefix.'" function="'.$function.'" '.$params; + } + + $this->Position = $tag_close_pos + $close_len; + return $tag; + } + + function CurrentLineNumber() + { + return substr_count(substr($this->Template, 0, $this->Position), "\n")+1; + } + + function SkipModeName() + { + switch ($this->SkipMode) { + case skip: return 'skip'; + case skip_tags: return 'skip_tags'; + case parse: return 'parse'; + } + } + + function Parse($template, $name='unknown') + { + $this->Template = $template; + $this->TemplateName = $name; + $this->Position = 0; + $this->Output = ''; + $this->TagHolder = new MyTagHolder(); + + if (defined('EXPERIMENTAL_PRE_PARSE')) { + $template_cache =& $this->Application->recallObject('TemplatesCache'); + $fname = $template_cache->GetRealFilename($this->TemplateName).'.php'; + $tname = $template_cache->GetRealFilename($this->TemplateName).'.tpl'; + + if (file_exists($fname) && filemtime($fname) > filemtime($tname)) { + ob_start(); + include($fname); + $output = ob_get_contents(); + ob_end_clean(); + return $output; + } + + $this->CompiledBuffer .= '<'.'?php'."\n"; + $this->CompiledBuffer .= 'global $application;'."\n"; + $this->CompiledBuffer .= '$o = \'\';'."\n"; + } + + if (!getArrayValue($this->Params, 'PrefixSpecial')) { + $this->Params['PrefixSpecial'] = '$PrefixSpecial'; + } + + //While we have more tags + while ($tag_data = $this->FindTag2()) + { + //Create tag object from passed tag data + if( $this->Application->isDebugMode() && dbg_ConstOn('DBG_SHOW_TAGS') ) + { + global $debugger; + $debugger->appendHTML('mode: '.$this->SkipModeName().' tag '.$debugger->highlightString($tag_data).' in '.$debugger->getFileLink($debugger->getLocalFile(DOC_ROOT.BASE_PATH.THEMES_PATH.'/'.$this->TemplateName).'.tpl', $this->CurrentLineNumber(), '', true)); + } +// $tag =& new MyTag($tag_data, $this); + $tag =& $this->TagHolder->GetTag($tag_data, $this); + + if (!$this->CheckRecursion($tag)) //we do NOT process closing tags + { + $tag->Process(); + } + } + + if ( !$this->GetParam('from_inportal') ) { + if ( strpos($this->Output, 'Application->recallObject('Inp1Parser'); + $this->Output = $inp1_parser->Parse($name, $this->Output); + } + } + + + if (defined('EXPERIMENTAL_PRE_PARSE')) { +// $this->CompiledBuffer .= 'echo $o;'."\n"; + $this->CompiledBuffer .= '?'.'>'."\n"; + + $compiled = fopen($fname, 'w'); + fwrite($compiled, $this->CompiledBuffer); + fclose($compiled); + } + + return $this->Output; + } + + function ParseBlock($params, $force_pass_params=0, $as_template=false) + { + if( $this->Application->isDebugMode() && dbg_ConstOn('DBG_SHOW_TAGS') ) + { + global $debugger; + $debugger->appendHTML('ParseBlock '.$params['name'].' pass_params is '.$params['pass_params'].' force is '.$force_pass_params.' in '.$debugger->getFileLink($debugger->getLocalFile(DOC_ROOT.BASE_PATH.THEMES_PATH.'/'.$this->TemplateName).'.tpl', $this->CurrentLineNumber(), '', true)); + + } + if ( $this->Application->isDebugMode() && dbg_ConstOn('DBG_PRE_PARSE') ) { + global $debugger; + $debugger->CurrentPreParsedBlock = $params['name']; + } + if (defined('EXPERIMENTAL_PRE_PARSE')) { + $this->MainParser = false; + if (isset($this->Application->PreParsedBlocks[$params['name']]) ) { + + if ($this->ParamsRecursionIndex == 0) { + $this->ParamsStack[$this->ParamsRecursionIndex] = $this->Params; + } + + if (isset($params['pass_params']) || $force_pass_params) { + $pass_params = array_merge($this->ParamsStack[$this->ParamsRecursionIndex], $params); + } + else { + $pass_params = $params; + } + + $this->ParamsStack[++$this->ParamsRecursionIndex] = $pass_params; + $this->Params = $pass_params; + + $f = $this->Application->PreParsedBlocks[$params['name']]; + +// $this->ParamsRecursionIndex--; + + //$this->SetParams($params); + $ret = $f($pass_params); + + unset($this->ParamsStack[$this->ParamsRecursionIndex--]); + $this->Params = $this->ParamsStack[$this->ParamsRecursionIndex]; + $this->MainParser = true; + return $ret; + } + } + + $BlockParser =& $this->Application->makeClass('TemplateParser'); + if (isset($params['pass_params']) || $force_pass_params) { + $BlockParser->SetParams(array_merge($this->Params, $params)); + } + else + $BlockParser->SetParams($params); + $this->Application->Parser =& $BlockParser; + if (!isset($params['name'])) trigger_error('***Error: Block name not passed to ParseBlock', E_USER_ERROR); + $templates_cache =& $this->Application->recallObject('TemplatesCache'); + + $template_name = $as_template ? $params['name'] : $templates_cache->GetTemplateFileName($params['name']) . '-block:'.$params['name']; + + $silent = getArrayValue($params, 'from_inportal') && !defined('DBG_TEMPLATE_FAILURE'); + + $o = $BlockParser->Parse( + $templates_cache->GetTemplateBody($params['name'], $silent), + $template_name + ); + if (getArrayValue($params, 'BlockNoData') && !$BlockParser->DataExists) { + $template_name = $as_template ? $params['BlockNoData'] : $templates_cache->GetTemplateFileName($params['BlockNoData']) . '-block:'.$params['BlockNoData']; + $o = $BlockParser->Parse( + $templates_cache->GetTemplateBody($params['BlockNoData'], $silent), + $template_name + ); + } + $this->Application->Parser =& $this; + $this->Application->Parser->DataExists = $this->Application->Parser->DataExists || $BlockParser->DataExists; + return $o; + } + + function Recurve(&$tag) + { + $this->Recursion[++$this->RecursionIndex] =& $tag; + } + + function CheckRecursion(&$tag) + { + if ($this->RecursionIndex > 0) { //If we are inside the recursion + if ($this->Recursion[$this->RecursionIndex]->CheckRecursion($tag)) { //If we can close this recursion + unset($this->Recursion[$this->RecursionIndex--]); //unsetting current recursion level and decreasing it at the same time + return true; //we should inform not to process closing tag + } + } + return false; + } + + function SetSkipMode($mode) + { + $this->SkipMode = $mode; + } +} + +?> \ No newline at end of file Index: trunk/core/kernel/db/dbitem.php =================================================================== diff -u -N --- trunk/core/kernel/db/dbitem.php (revision 0) +++ trunk/core/kernel/db/dbitem.php (revision 1560) @@ -0,0 +1,818 @@ +ErrorMsgs['required'] = 'Field is required'; + $this->ErrorMsgs['unique'] = 'Field value must be unique'; + $this->ErrorMsgs['value_out_of_range'] = 'Field is out of range, possible values from %s to %s'; + $this->ErrorMsgs['length_out_of_range'] = 'Field is out of range'; + $this->ErrorMsgs['bad_type'] = 'Incorrect data format, please use %s'; + $this->ErrorMsgs['bad_date_format'] = 'Incorrect date format, please use (%s) ex. (%s)'; + } + + function SetDirtyField($field_name, $field_value) + { + $this->DirtyFieldValues[$field_name] = $field_value; + } + + function GetDirtyField($field_name) + { + return $this->DirtyFieldValues[$field_name]; + } + + /** + * Set's default values for all fields + * + * @access public + */ + function SetDefaultValues() + { + 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; + if (isset($options['formatter'])) { + $formatter =& $this->Application->recallObject($options['formatter']); +// $parsed = $formatter->Parse($value, $options, $err); + $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 = '') + { + $error_msg = $this->Application->Phrase($error_label); + if( !($error_msg && getArrayValue($this->ErrorMsgs,$pseudo)) ) + { + $this->ErrorMsgs[$pseudo] = $error_msg; + } + $this->FieldErrors[$field]['pseudo'] = $pseudo; + } + + /** + * 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( eregi("^[0-9]+$", $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( eregi("^[0-9]+$", $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( eregi("^[0-9]+$", $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 + * @return void + * @see kDBItem::Load() + * @see kDBItem::Update() + * @see kDBItem::Delete() + */ + function GetKeyClause($method=null) + { + return '`'.$this->TableName.'`.'.$this->IDField.' = '.$this->Conn->qstr($this->ID); + } + + /** + * Loads item from the database by given id + * + * @access public + * @param int $id Primery Key Id to load + * @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 (is_array($id)) { + $keys = $id; + foreach ($keys as $field => $value) { + $sqls[] = '`'.$this->TableName.'`.'.$field.' = '.$this->Conn->qstr($value); + } + $keys_sql = '('.implode(') AND (', $sqls).')'; + } + + if (isset($id_field_name)) $this->SetIDField($id_field_name); + if (!isset($id) && !isset($keys_sql)) return false; + + if( !$this->raiseEvent('OnBeforeItemLoad',$id) ) return false; + + $this->ID = $id; + + $q = $this->GetSelectSQL().' WHERE '.(isset($keys_sql) ? $keys_sql : $this->GetKeyClause('load')); + if ($this->DisplayQueries) { + echo get_class($this)." Load SQL: $q
"; + } + $this->FieldValues = array_merge_recursive2( $this->FieldValues, $this->Conn->GetRow($q) ); + + if ($this->FieldValues === false) { + //Error handling could be here + return false; + } + if (isset($keys_sql)) { + $this->setID($this->FieldValues[$this->IDField]); + } + else { + $this->setID($id); + } + + $this->UpdateFormattersSubFields(); // used for updating separate virtual date/time fields from DB timestamp (for example) + + $this->raiseEvent('OnAfterItemLoad'); + $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); + } + } + } + + function SkipField($field_name, $force_id=false) + { + $skip = false; + $skip = $skip || ( isset($this->VirtualFields[$field_name]) ); //skipping 'virtual' field + $skip = $skip || ( !getArrayValue($this->FieldValues, $field_name) && getArrayValue($this->Fields[$field_name], 'skip_empty') ); //skipping 'virtual' field +// $skip = $skip || ($field_name == $this->IDField && !$force_id); //skipping Primary Key + +// $table_name = preg_replace("/^(.*)\./", "$1", $field_name); +// $skip = $skip || ($table_name && ($table_name != $this->TableName)); //skipping field from other tables + + $skip = $skip || ( !isset($this->Fields[$field_name]) ); //skipping field not in Fields (nor virtual, nor real) + + 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) ) return false; + + // Validate before updating + if( !$this->IgnoreValidation && !$this->Validate() ) return false; + if( !$this->raiseEvent('OnAfterItemValidate') ) return false; + + //Nothing to update + if(!$this->FieldValues) return true; + + $sql = sprintf('UPDATE %s SET ',$this->TableName); + foreach ($this->FieldValues as $field_name => $field_value) + { + if ($this->SkipField($field_name)) continue; + + $real_field_name = eregi_replace("^.*\.", '',$field_name); //removing table names from field names + + //Adding part of SET clause for current field, escaping data with ADODB' qstr + if (is_null( $this->FieldValues[$field_name] )) { + if (isset($this->Fields[$field_name]['not_null']) && $this->Fields[$field_name]['not_null']) { + $sql .= '`'.$real_field_name.'` = '.$this->Conn->qstr($this->Fields[$field_name]['default']).', '; + } + else { + $sql .= '`'.$real_field_name.'` = NULL, '; + } + } + else { + $sql.= sprintf('`%s`=%s, ', $real_field_name, $this->Conn->qstr($this->FieldValues[$field_name], 0)); + } + } + $sql = ereg_replace(", $", '', $sql); //Removing last comma and space + + $sql.= sprintf(' WHERE %s', $this->GetKeyClause('update')); //Adding WHERE clause with Primary Key + + if ($this->DisplayQueries) echo "Sql: $sql
"; + + if ($this->Conn->ChangeQuery($sql) === false) { //Executing query and checking results + if ($this->DisplayQueries) + { + echo "Error executing statement: ".$adodbConnection->ErrorMsg()."
"; + } + return false; + } + + $affected = $this->Conn->getAffectedRows(); + if (!$system_update && $affected == 1){ + $this->setModifiedFlag(); + } + + $this->raiseEvent('OnAfterItemUpdate'); + return true; + } + + /** + * 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 + $global_res = true; + foreach ($this->Fields as $field => $params) { + $res = true; + $res = $res && $this->ValidateType($field, $params); + $res = $res && $this->ValidateRange($field, $params); + $res = $res && $this->ValidateUnique($field, $params); + $res = $res && $this->ValidateRequired($field, $params); + + // If Formatter has set some error messages during values parsing + $error_field = isset($params['error_field']) ? $params['error_field'] : $field; + if (isset($this->FieldErrors[$error_field]['pseudo']) && $this->FieldErrors[$error_field] != '') { + $global_res = false; + } + + $global_res = $global_res && $res; + } + + if (!$global_res && $this->Application->isDebugMode() ) + { + global $debugger; + $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( $error_msg, E_USER_NOTICE); + $debugger->dumpVars($this->FieldErrors); + } + + return $global_res; + } + + function HasErrors() + { + $global_res = false; + foreach ($this->Fields as $field => $params) { + // If Formatter has set some error messages during values parsing + if (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]; + $error_field = isset($params['error_field']) ? $params['error_field'] : $field; + if ( $val != '' && + isset($params['type']) && + preg_match("#int|integer|double|float|real|numeric|string#", $params['type']) + ) { + $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->FieldErrors[$error_field]['pseudo'] = 'bad_type'; + $this->FieldErrors[$error_field]['params'] = $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; + $error_field = isset($params['error_field']) ? $params['error_field'] : $field; + if ( getArrayValue($params,'required') ) + { + $res = ( (string) $this->FieldValues[$field] != ''); + } + if (!$res) $this->FieldErrors[$error_field]['pseudo'] = '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; + $error_field = isset($params['error_field']) ? $params['error_field'] : $field; + $unique_fields = getArrayValue($params,'unique'); + if($unique_fields !== false) + { + $where = Array(); + array_push($unique_fields,$field); + foreach($unique_fields as $unique_field) + { + $where[] = '`'.$unique_field.'` = '.$this->Conn->qstr( $this->GetDBField($unique_field) ); + } + + $sql = 'SELECT COUNT(*) FROM %s WHERE ('.implode(') AND (',$where).') AND ('.$this->IDField.' <> '.(int)$this->ID.')'; + + $res_temp = $this->Conn->GetOne( sprintf($sql, $this->TableName ) ); + $res_live = $this->Conn->GetOne( sprintf($sql, kTempTablesHandler::GetLiveName($this->TableName) ) ); + $res = ($res_temp == 0) && ($res_live == 0); + + if(!$res) $this->FieldErrors[$error_field]['pseudo'] = '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]; + $error_field = isset($params['error_field']) ? $params['error_field'] : $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) { + $this->FieldErrors[$error_field]['pseudo'] = 'value_out_of_range'; + + if ( !isset($min_val) ) $min_val = '-∞'; + if ( !isset($max_val) ) $max_val = '∞'; + + $this->FieldErrors[$error_field]['params'] = Array( $min_val, $max_val ); + return $res; + } + if ( isset($params['max_len'])) { + $res = $res && strlen($val) <= $params['max_len']; + } + if ( isset($params['min_len'])) { + $res = $res && strlen($val) >= $params['min_len']; + } + if (!$res) { + $this->FieldErrors[$error_field]['pseudo'] = 'length_out_of_range'; + $this->FieldErrors[$error_field]['params'] = Array( getArrayValue($params,'min_len'), getArrayValue($params,'max_len') ); + return $res; + } + return $res; + } + + /** + * Return error message for field + * + * @param string $field + * @return string + * @access public + */ + function GetErrorMsg($field) + { + if( !isset($this->FieldErrors[$field]) ) return ''; + + $err = getArrayValue($this->FieldErrors[$field], 'pseudo'); + if( isset($this->Fields[$field]['error_msgs'][$err]) ) + { + $msg = $this->Fields[$field]['error_msgs'][$err]; + $msg = $this->Application->ReplaceLanguageTags($msg); + } + else + { + if( !isset($this->ErrorMsgs[$err]) ) return $err; + $msg = $this->ErrorMsgs[$err]; + } + + 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->IgnoreValidation && !$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; + + $fields_sql .= sprintf('`%s`, ',$field_name); //Adding field name to fields block of Insert statement + //Adding field' value to Values block of Insert statement, escaping it with ADODB' qstr + if (is_null( $this->FieldValues[$field_name] )) { + if (isset($this->Fields[$field_name]['not_null']) && $this->Fields[$field_name]['not_null']) { + $values_sql .= $this->Conn->qstr($this->Fields[$field_name]['default']).', '; + } + else { + $values_sql .= 'NULL, '; + } + } + else + { + $values_sql .= sprintf('%s, ',$this->Conn->qstr($this->FieldValues[$field_name], 0)); + } + + } + //Cutting last commas and spaces + $fields_sql = ereg_replace(", $", '', $fields_sql); + $values_sql = ereg_replace(", $", '', $values_sql); + $sql = sprintf('INSERT INTO %s (%s) VALUES (%s)', $this->TableName, $fields_sql, $values_sql); //Formatting query + + //Executing the query and checking the result + + if ($this->Conn->ChangeQuery($sql) === false) + { + if($this->DisplayQueries) + { + echo "Error executing statement: ".$this->Conn->getErrorMsg().'
'; + } + return false; + } + + $insert_id = $this->Conn->getInsertID(); + if($insert_id == 0) $insert_id = $this->FieldValues[$this->IDField]; + $this->setID($insert_id); + + if (!$system_create){ + $this->setModifiedFlag(); + } + + $this->raiseEvent('OnAfterItemCreate'); + 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; + + $q = 'DELETE FROM '.$this->TableName.' WHERE '.$this->GetKeyClause('Delete'); + if ($this->DisplayQueries) + { + echo get_class($this).' Delete SQL: '.$q.'
'; + } + $ret = $this->Conn->ChangeQuery($q); + + $this->setModifiedFlag(); + + $this->raiseEvent('OnAfterItemDelete'); + + return $ret; + } + + /** + * 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 + * @access private + */ + function NameCopy($master=null, $foreign_key=null) + { + $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("/Copy ([0-9]*)[ ]*of(.*)/", $new_name, $regs) ) { + $new_name = 'Copy '.($regs[1]+1).' of '.$regs[2]; + } + elseif ($original_checked) { + $new_name = 'Copy of '.$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); + + if (getArrayValue($master, 'ForeignKey') && isset($foreign_key)) { + $query .= ' AND '.$master['ForeignKey'].' = '.$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) + { + if( !isset($id) ) $id = $this->GetID(); + $event = new kEvent( Array('name'=>$name,'prefix'=>$this->Prefix,'special'=>$this->Special) ); + $event->setEventParam('id', $id); + $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()); + $this->SetID($new_id); + } + + /** + * Set's modification flag for main prefix of current prefix to true + * + * @access private + * @author Alexey + */ + function setModifiedFlag() + { + $main_prefix = $this->Application->GetTopmostPrefix($this->Prefix); + $this->Application->StoreVar($main_prefix.'_modified', '1'); + } + + /** + * 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; + } +} + + + +?> \ No newline at end of file Index: trunk/core/kernel/utility/http_query.php =================================================================== diff -u -N --- trunk/core/kernel/utility/http_query.php (revision 0) +++ trunk/core/kernel/utility/http_query.php (revision 1560) @@ -0,0 +1,478 @@ +Order = $order; + $this->Admin = defined('ADMIN') && ADMIN; + $this->AddAllVars(); + + $this->specialsToRemove = $this->Get('remove_specials'); + if($this->specialsToRemove) + { + $this->_Params = $this->removeSpecials($this->_Params); + } + ini_set('magic_quotes_gpc', 0); + } + + function removeSpecials($array) + { + $ret = Array(); + $removed = false; + foreach($this->specialsToRemove as $prefix_special => $flag) + { + if($flag) + { + $removed = true; + list($prefix,$special) = explode('.',$prefix_special, 2); + foreach ($array as $key => $val) { + $new_key = preg_match("/^".$prefix."[._]{1}".$special."(.*)/", $key, $regs) ? $prefix.$regs[1] : $key; + $ret[$new_key] = is_array($val) ? $this->removeSpecials($val) : $val; + } + } + } + return $removed ? $ret : $array; + } + + /** + * All all requested vars to + * common storage place + * + * @access private + */ + function AddAllVars() + { + for ($i=0; $i < strlen($this->Order); $i++) + { + $current = $this->Order[$i]; + switch ($current) { + case 'G': + $this->Get =$this->AddVars($_GET); + $this->processQueryString(); + break; + case 'P': + $this->Post = $this->AddVars($_POST); + $this->convertPostEvents(); + break; + case 'C': + $this->Cookie = $this->AddVars($_COOKIE); + break; + case 'E'; + $this->Env = $this->AddVars($_ENV); + break; + case 'S'; + $this->Server = $this->AddVars($_SERVER); + break; + case 'F'; + $this->convertFiles(); + $this->Files = $this->MergeVars($_FILES, false); //do not strip slashes! + break; + } + } + } + + function convertFiles() + { + if (!$_FILES) + { + return false; + } + + $file_keys = Array('error','name','size','tmp_name','type'); + + foreach($_FILES as $file_name => $file_info) + { + if( is_array($file_info['error']) ) + { + $tmp[$file_name] = $this->getArrayLevel( $file_info['error'], $file_name ); + } + else + { + $normal_files[$file_name] = $file_info; + } + } + + $files = $_FILES; + $_FILES = Array(); + + foreach($tmp as $prefix => $prefix_files) + { + $anchor =& $_FILES; + foreach($prefix_files['keys'] as $key) + { + $anchor =& $anchor[$key]; + } + foreach($prefix_files['value'] as $field_name) + { + unset($inner_anchor); + unset($copy); + $work_copy = $prefix_files['keys']; + foreach($file_keys as $file_key) + { + $inner_anchor =& $files[$prefix][$file_key]; + if (isset($copy)) + { + $work_copy = $copy; + } + else + { + $copy = $work_copy; + } + array_shift($work_copy); + foreach($work_copy as $prefix_file_key) + { + $inner_anchor =& $inner_anchor[$prefix_file_key]; + } + $anchor[$field_name][$file_key] = $inner_anchor[$field_name]; + } + } + } + + // keys: img_temp, 0, values: LocalPath, ThumbPath + } + + function getArrayLevel(&$level, $prefix='') + { + $ret['keys'] = $prefix ? Array($prefix) : Array(); + $ret['value'] = Array(); + + foreach($level as $level_key => $level_value) + { + if( is_array($level_value) ) + { + $ret['keys'][] = $level_key; + $tmp = $this->getArrayLevel($level_value); + + $ret['keys'] = array_merge($ret['keys'], $tmp['keys']); + $ret['value'] = array_merge($ret['value'], $tmp['value']); + } + else + { + $ret['value'][] = $level_key; + } + } + + return $ret; + } + + /** + * Owerwrites GET events with POST events in case if they are set and not empty + * + */ + function convertPostEvents() + { + $events = $this->Get('events'); + if( is_array($events) ) + { + foreach ($events as $prefix_special => $event_name) + { + if($event_name) $this->Set($prefix_special.'_event', $event_name); + } + } + } + + /** + * Process QueryString only, create + * events, ids, based on config + * set template name and sid in + * desired application variables. + * + * @access private + */ + function processQueryString() + { + // env=SID:TEMPLATE:m-1-1-1-1:l0-0-0:n-0-0-0:bb-0-0-1-1-1-0 + + $env_var =& $this->Get(ENV_VAR_NAME); + if($env_var) + { + $sid = $this->Get('sid'); + if (defined('MOD_REWRITE') && MOD_REWRITE && $sid) $env_var = rtrim($sid.$env_var, '/'); + $env_var = str_replace('\:','_&+$$+&_',$env_var); // replace escaped "=" with spec-chars :) + + $parts=explode(':',$env_var); + + if (defined('MOD_REWRITE') && MOD_REWRITE) $env_var = str_replace('/', ':', $env_var); + + if (defined('INPORTAL_ENV')) { + $sub_parts = array_shift($parts); + + list($sid, $t) = explode('-', $sub_parts, 2); + + + // Save Session ID + if($sid) { + $this->Set('sid',$sid); + $this->Get['sid'] = $sid; + } + + // Save Template Name + $t=$this->getTemplateName( $t ); + if(!$t) $t='index'; + $this->Set('t',$t); + } + else { + // Save Session ID + $sid=array_shift($parts); + if($sid) $this->Set('sid',$sid); + + // Save Template Name + $t=$this->getTemplateName( array_shift($parts) ); + if(!$t) $t='index'; + $this->Set('t',$t); + } + + if($parts) + { + $query_maps=Array(); + $event_manger =& $this->Application->recallObject('EventManager'); + + $passed = Array(); + + foreach($parts as $mixed_part) + { + //In-portal old style env conversion - adds '-' between prefix and first var + $mixed_part = str_replace('_&+$$+&_',':',$mixed_part); + $mixed_part = preg_replace("/^([a-zA-Z]+)([0-9]+)-(.*)/", "$1-$2-$3", $mixed_part); + + $escaped_part = str_replace('\-', '_&+$$+&_', $mixed_part); + $escaped_part = explode('-', $escaped_part); + + $mixed_part = array(); + foreach ($escaped_part as $escaped_val) { + $mixed_part[] = str_replace('_&+$$+&_', '-', $escaped_val); + } + + $prefix_special=array_shift($mixed_part); // l.pick, l + list($prefix)=explode('.',$prefix_special); + + $query_maps[$prefix_special]=$this->Application->getUnitOption($prefix,'QueryString'); + + // if config is not defined for prefix in QueryString, then don't process it + if( $query_maps[$prefix_special] ) + { + array_push($passed, $prefix); + foreach($query_maps[$prefix_special] as $index => $var_name) + { + // l_id, l_page, l_bla-bla-bla + $val = $mixed_part[$index-1]; + if ($val == '') $val = false; + $this->Set($prefix_special.'_'.$var_name, $val); + } + } + else + { + unset($query_maps[$prefix_special]); + } + + } + $this->Set('passed', implode(',', $passed)); + $event_manger->setQueryMaps($query_maps); + } + } + else + { + $t=$this->getTemplateName('index'); + $this->Set('t',$t); + } + } + + /** + * Decides what template name to + * use from $_GET or from $_POST + * + * @param string $querystring_template + * @return string + * @access private + */ + function getTemplateName($querystring_template) + { + $t_from_post = $this->Get('t'); + $t= $t_from_post ? $t_from_post : $querystring_template; + + if ( is_numeric($t) ) { + $t = $this->Application->DB->GetOne('SELECT CONCAT(FilePath, \'/\', FileName) FROM '.TABLE_PREFIX.'ThemeFiles + WHERE FileId = '.$t); + } + $t = preg_replace("/\.tpl$/", '', $t); + + return $t; + } + + /** + * Saves variables from array specified + * into common variable storage place + * + * @param Array $array + * @return Array + * @access private + */ + function AddVars($array) + { + $array = $this->StripSlashes($array); + foreach($array as $key => $value) + { + $this->Set($key,$value); + } + return $array; + } + + function MergeVars($array, $strip_slashes=true) + { + if ($strip_slashes) $array = $this->StripSlashes($array); + foreach($array as $key => $value) + { + $this->_Params = array_merge_recursive2($this->_Params, Array($key=>$value)); + } + return $array; + } + + function StripSlashes($array) + { + //if( !get_magic_quotes_gpc() ) return $array; + foreach($array as $key=>$value) + { + if( is_array($value) ) + { + $array[$key] = $this->StripSlashes($value); + } + else + { + if( get_magic_quotes_gpc() ) $value = stripslashes($value); + if(!$this->Admin) $value = htmlspecialchars($value); + $array[$key] = $value; + } + //$array[$key]=is_array($value)?$this->StripSlashes($value):stripslashes($value); + } + return $array; + } + + /** + * Returns the hash of http params + * matching the mask with values + * + * @param string $mask + * @return Array + * @access public + */ + function GetSelectedValues($mask) + { + return $this->Application->ExtractByMask($this->Vars, $mask); + } + + /** + * Returns the sprintf'ed by format list of + * http params matching the mask and set to on + * + * @param string $mask + * @param string $format + * @return string + * @access public + */ + function GetSelectedIDs($mask, $format) + { + if ($mask == '') return; + $result = ''; + foreach ($this->GetParams() as $name => $val) + { + if (eregi($mask, $name, $regs) && $val == 'on') { + + $result.= sprintf($format, $regs[1]); + } + } + return $result; + } + + /** + * Returns the sprintf'ed by format list of + * http params matching the mask and set to on + * + * @param string $mask + * @param string $value_mask + * @return Array + * @access public + */ + function GetSelectedIDsArray($mask, $value_mask="%s,") + { + $str = $this->GetSelectedIDs($mask, $value_mask); + $str = rtrim($str, ','); + if (!empty($str)) { + $ids = split(',', $str); + if ($ids !== false) + return $ids; + else return Array(); + } + else return Array(); + } +} + +?> \ No newline at end of file Index: trunk/core/kernel/event_manager.php =================================================================== diff -u -N --- trunk/core/kernel/event_manager.php (revision 0) +++ trunk/core/kernel/event_manager.php (revision 1560) @@ -0,0 +1,260 @@ +queryMaps=$new_query_maps; + } + + function registerBuildEvent($pseudo_class,$build_event_name) + { + $this->buildEvents[$pseudo_class]=$build_event_name; + } + + /** + * Returns build event by pseudo class + * name if any defined in config + * + * @param string $pseudo_class + * @return kEvent + * @access public + */ + function &getBuildEvent($pseudo_class) + { + if( !isset($this->buildEvents[$pseudo_class]) ) return false; + + $event = new kEvent(); + $event->Name=$this->buildEvents[$pseudo_class]; + $event->MasterEvent=null; + return $event; + } + + /** + * Allows to process any type of event + * + * @param kEvent $event + * @access public + */ + function HandleEvent(&$event) + { + if (!$event->SkipBeforeHooks) { + $this->processHooks($event, hBEFORE); + if ($event->status == erFATAL) return; + } + + $event_handler =& $this->Application->recallObject($event->Prefix.'_EventHandler'); + $event_handler->processEvent($event); + if ($event->status == erFATAL) return; + if (!$event->SkipAfterHooks) { + $this->processHooks($event, hAFTER); + } + } + + function ProcessRequest() + { + $this->processOpener(); + + // 1. get events from $_POST + $events=$this->Application->GetVar('events'); + if($events===false) $events=Array(); + // 2. if nothing there, then try to find them in $_GET + if($this->queryMaps && !$events) + { + // if we got $_GET type submit (links, not javascript) + foreach($this->queryMaps as $prefix_special => $query_map) + { + $query_map=array_flip($query_map); + if(isset($query_map['event'])) + { + $events[$prefix_special]=$this->Application->GetVar($prefix_special.'_event'); + } + } + $actions = $this->Application->GetVar('do'); + if ($actions) { + list($prefix, $event_name) = explode('_', $actions); + $events[$prefix] = $event_name; + } + } + + $passed = explode(',', $this->Application->GetVar('passed')); + foreach($events as $prefix_special => $event_name) + { + if(!$event_name) continue; + if( is_array($event_name) ) + { + $event_name = key($event_name); + $events[$prefix_special] = $event_name; + $this->Application->SetVar($prefix_special.'_event', $event_name); + } + + $event = new kEvent(); + $event->Name=$event_name; + $event->Prefix_Special=$prefix_special; + + $prefix_special=explode('.',$prefix_special); + $event->Prefix=$prefix_special[0]; + array_push($passed, $prefix_special[0]); + $event->Special=isset($prefix_special[1])?$prefix_special[1]:''; + + $event->redirect_params = Array('opener'=>'s', 'pass'=>'all'); + $event->redirect = true; + $this->HandleEvent($event); + + if($event->status==erSUCCESS && ($event->redirect === true || strlen($event->redirect) > 0) ) + { + $this->Application->Redirect($event->redirect, $event->redirect_params, null, $event->redirect_script); + } + } + + $this->Application->SetVar('events', $events); + $this->Application->SetVar('passed', implode(',', $passed)); + } + + function processOpener() + { + $opener_action=$this->Application->GetVar('m_opener'); + $opener_stack=$this->Application->RecallVar('opener_stack'); + $opener_stack=$opener_stack?unserialize($opener_stack):Array(); + switch($opener_action) + { + case 'r': // "reset" opener stack + $opener_stack=Array(); + break; + + case 'd': // "down/push" new template to opener stack, deeplevel++ + if ($this->Application->GetVar('front')) { + array_push($opener_stack, '../'.$this->Application->RecallVar('last_template') ); + } + else { + array_push($opener_stack, $this->Application->RecallVar('last_template') ); + } + break; + + case 'u': // "up/pop" last template from opener stack, deeplevel-- + array_pop($opener_stack); + break; + + default: // "s/0," stay on same deep level + break; + } + $this->Application->SetVar('m_opener','s'); + $this->Application->StoreVar('opener_stack',serialize($opener_stack)); + } + + function registerHook($hookto_prefix, $hookto_special, $hookto_event, $mode, $do_prefix, $do_special, $do_event, $conditional) + { + $hookto_prefix_special = rtrim($hookto_prefix.'.'.$hookto_special, '.'); + if ($mode == hBEFORE) { + $this->beforeHooks[strtolower($hookto_prefix_special.'.'.$hookto_event)][] = Array( + 'DoPrefix' => $do_prefix, + 'DoSpecial' => $do_special, + 'DoEvent' => $do_event, + 'Conditional' => $conditional, + ); + } + elseif ($mode == hAFTER) { + $this->afterHooks[strtolower($hookto_prefix_special.'.'.$hookto_event)][] = Array( + 'DoPrefix' => $do_prefix, + 'DoSpecial' => $do_special, + 'DoEvent' => $do_event, + 'Conditional' => $conditional, + ); + } + } + + /** + * Enter description here... + * + * @param kEvent $event + * @param int $mode hBEFORE or hAFTER + */ + function processHooks(&$event, $mode) + { + if ($mode == hBEFORE) { + $mode_hooks =& $this->beforeHooks; + } + else { + $mode_hooks =& $this->afterHooks; + } + if ( $hooks = getArrayValue($mode_hooks, strtolower($event->Prefix_Special.'.'.$event->Name)) ) { + foreach($hooks as $hook) + { + $prefix_special = rtrim($hook['DoPrefix'].'_'.$hook['DoSpecial'],'_'); + if( $hook['Conditional'] && !$this->Application->GetVar($prefix_special) ) continue; + $hook_event = new kEvent( Array('name'=>$hook['DoEvent'],'prefix'=>$hook['DoPrefix'],'special'=>$hook['DoSpecial']) ); + $hook_event->MasterEvent =& $event; + + $this->HandleEvent($hook_event); + } + } + } + + /** + * Set's new event for $prefix_special + * passed + * + * @param string $prefix_special + * @param string $event_name + * @access public + */ + function setEvent($prefix_special,$event_name) + { + $actions =& $this->Application->recallObject('kActions'); + $actions->Set('events['.$prefix_special.']',$event_name); + } + + } + + +?> \ No newline at end of file Index: trunk/core/kernel/parser/tags.php =================================================================== diff -u -N --- trunk/core/kernel/parser/tags.php (revision 0) +++ trunk/core/kernel/parser/tags.php (revision 1560) @@ -0,0 +1,380 @@ +_Tag)) { + $this->_Tag =& new Tag($tag_data, $parser, $inp_tag); + } + else { +// $this->_Tag->Parser =& $parser; + $this->_Tag->TagData = $tag_data; + if ($tag_data != '') $this->_Tag->ParseTagData($tag_data); + $this->_Tag->NP =& $this->_Tag->NamedParams; + } + return $this->_Tag; + } + +} + +class Tag extends kBase { + var $Processor; + var $Tag; + var $Params = Array(); + var $NamedParams = Array(); + var $NP; + /** + * Enter description here... + * + * @var TemplateParser + */ + var $Parser; + var $TagData = ''; + + function Tag($tag_data, &$parser, $inp_tag=0) + { + parent::kBase(); + $this->Parser =& $parser; + $this->TagData = $tag_data; + if ($tag_data != '') $this->ParseTagData($tag_data); + $this->NP =& $this->NamedParams; + } + + function CopyFrom(&$tag) + { + $this->Processor = $tag->Processor; + $this->Tag = $tag->Tag; + $this->TagData = $tag->TagData; + $this->Params = $tag->Params; + $this->NamedParams = $tag->NamedParams; + $this->Parser =& $tag->Parser; + } + + function GetFullTag() + { + return '<%'.$this->TagData.'%>'; + } + + function RebuildTagData() + { + $res = $this->Processor.':'.$this->Tag.' '; + foreach ($this->NamedParams as $name => $value) { + $res .= "$name='$value' "; + } + return $res; + } + + /** + * Escape chars in phrase translation, that could harm parser to process tag + * + * @param string $text + * @return string + * @access private + */ + function EscapeReservedChars($text) + { + $reserved = Array('"',"'"); // = + $replacement = Array('\"',"\'"); // \= + return str_replace($reserved,$replacement,$text); + } + + + function ReplaceParams($tag_data) + { + //print_pre($this->Parser->Pattern, $tag_data); + $values = $this->Parser->Values; + foreach($values as $param_name => $param_value) + { + $values[$param_name] = $this->EscapeReservedChars($param_value); + } + + if (is_array($this->Parser->Params)) { + $tag_data = preg_replace($this->Parser->Pattern, $values, $tag_data); + } + //echo "got: $tag_data
"; + return $tag_data; + } + + function PreParseReplaceParams($tag_data) + { + //print_pre($this->Parser->Pattern, $tag_data); + $values = $this->Parser->Values; + foreach($values as $param_name => $param_value) + { + $values[$param_name] = $this->EscapeReservedChars($param_value); + } + + /*$patterns = Array(); + if ( is_array($this->Parser->Args) ) { + foreach ($this->Parser->Args as $arg) { + + } + }*/ + + if ($this->Parser->SkipMode == parse) { + if (is_array($this->Parser->Params)) { + $tag_data = preg_replace($this->Parser->Pattern, $values, $tag_data); + } + } + //echo "got: $tag_data
"; + return $tag_data; + } + + function CmpParams($a, $b) + { + $a_len = strlen($a); + $b_len = strlen($b); + if ($a_len == $b_len) return 0; + return $a_len > $b_len ? -1 : 1; + } + + /** + * Set's Prefix and Special for Tag object + * based on ones from tagname + * + * @param string $tag_data + * @access protected + */ + function ParseTagData($tag_data) + { + if (defined('EXPERIMENTAL_PRE_PARSE') ) { + $tag_data = $this->PreParseReplaceParams($tag_data) . ' '; + } + else { + $tag_data = $this->ReplaceParams($tag_data) . ' '; +// $tag_data = $this->Application->ReplaceLanguageTags($tag_data); + } + + list ($key_data, $params) = split("[ \t\n]{1}", $tag_data, 2); + $key_data = trim($key_data); + + $tmp=explode(':',$key_data); + $this->Tag=$tmp[1]; + + $tmp=$this->Application->processPrefix($tmp[0]); + $this->Prefix=$tmp['prefix']; + $this->Special=$tmp['special']; + $this->Processor=$this->Prefix; + + if ($params != '') { + $this->ParseNamedParams($params); + } + else { + $this->NamedParams = array(); + } + } + + function ParseNamedParams($params_str) + { + $params =& new Params($params_str); + $this->NamedParams = $params->_Params; + } + + function GetParam($param) + { + if (isset($this->NP[$param])) + return $this->NP[$param]; + else + return false; + } + + /** + * Process IF tags in specific way + * + */ + function Process() + { + if ($this->Processor == 'm' || $this->Processor == 'm_TagProcessor') { //if we are procssing Main tags + if ($this->Tag == 'block' || $this->Tag == 'DefineElement') { + $tag =& new BlockTag('', $this->Parser); + $tag->CopyFrom($this); + $tag->Process(); + } + elseif ($this->Parser->SkipMode == skip_tags) { + return; + } + elseif ( + $this->Tag == 'if' || + $this->Tag == 'ifnot' || + $this->Tag == 'else' || + $this->Tag == 'elseif' + ) + { + if ( defined('EXPERIMENTAL_PRE_PARSE') ) { + $this->Parser->AppendCompiledCode( $this->GetCode() ); + } + $tag =& new ConstructTag('', $this->Parser); + $tag->CopyFrom($this); + $tag->Process(); + } + else { + if ($this->Parser->SkipMode == skip) { + if ( defined('EXPERIMENTAL_PRE_PARSE') ) { + $this->Parser->AppendCompiledCode( $this->GetCode() ); + } + return; + } + $this->ProcessTag(); + if ( defined('EXPERIMENTAL_PRE_PARSE') ) { + $this->Parser->AppendCompiledCode( $this->GetCode() ); + } + } + } + else { //normal tags - processors other than main + if ($this->Parser->SkipMode == skip) { // inside if - add statements inside if to compiled code + if ( defined('EXPERIMENTAL_PRE_PARSE') ) { + $this->Parser->AppendCompiledCode( $this->GetCode() ); + } + return; + } + elseif ($this->Parser->SkipMode == skip_tags) return; //do not parse if we skipping tags + $this->ProcessTag(); + if ( defined('EXPERIMENTAL_PRE_PARSE') ) { + $this->Parser->AppendCompiledCode( $this->GetCode() ); + } + } + } + + /** + * Set's Prefix and Special for TagProcessor + * based on tag beeing processed + * + * @return string + * @access protected + */ + function DoProcessTag() + { + // $tag->Prefix - l_TagProcessor + $tmp = $this->Application->processPrefix($this->Processor); + + $processor =& $this->Application->recallObject($tmp['prefix'].'_TagProcessor'); // $this->Processor + + $tmp=explode('_',$tmp['prefix'],2); + $processor->Prefix=$tmp[0]; + $processor->Special=$this->Special; + + // pass_params for non ParseBlock tags :) + $parser_params = $this->Application->Parser->Params; + if( getArrayValue($this->NamedParams,'pass_params') ) + { + unset( $this->NamedParams['pass_params'] ); + $this->NamedParams = array_merge_recursive2($parser_params, $this->NamedParams); + } + + return $processor->ProcessTag($this); + } + + function ProcessTag() + { + $o = $this->DoProcessTag(); + if ($o !== false) + { + $this->Parser->AppendOutput($o); + } + else + { + trigger_error('can\'t process tag '.$this->Tag,E_USER_WARNING); + } + } + + function GetCode($echo=false) + { + $pass_params = $this->NP; + + $code = Array(); + + $to_pass = 'Array('; + foreach ($pass_params as $name => $val) { + $to_pass .= '"'.$name.'" => "'.str_replace('"', '\"', $val).'",'; + } + $to_pass .= ')'; + + if ($echo) $code[] = '$o = '."'';\n"; + + switch ( $this->Tag ) { + case 'param': + $code[] = '$o .= $params["'.$this->NP['name'].'"];'; + return $code; + case 'if': + if (isset($this->NP['_closing_tag_'])) { + $code[] = ' }'; + } + else { + + $check = $this->GetParam('check'); + if ($check) { + if (strpos($check, '_') !== false) { + list($prefix, $function) = explode('_', $check, 2); + } + else { + $function = $check; + $prefix = $this->Parser->GetParam('PrefixSpecial'); + } + } + else { + $prefix = $this->GetParam('prefix'); + $function = $this->GetParam('function'); + } + + $code[] = '$tmp = $application->processPrefix("'.$prefix.'");'."\n"; + $code[] = '$__tag_processor = $tmp[\'prefix\'].\'_TagProcessor\';'."\n"; + $code[] = '$processor =& $application->recallObject($__tag_processor);'."\n"; + $code[] = '$processor->Prefix = $tmp[\'prefix\'];'."\n"; + $code[] = '$processor->Special = $tmp[\'special\'];'."\n"; + $code[] = '$if_result = $processor->ProcessParsedTag(\''.$function.'\', '.$to_pass.', "'.$prefix.'");'."\n"; + if (isset($pass_params['inverse'])) { + $code[] = 'if (!$if_result) {'; + } + else { + $code[] = 'if ($if_result) {'; + } + } + return $code; + + case 'endif': + $code[] = ' }'; + return $code; + + case 'else': + $code[] = ' }'; + $code[] = ' else {'; + return $code; + } + + if (!$this->Processor) { + echo ''; + } + $code[] = '$tmp = $application->processPrefix("'.$this->getPrefixSpecial().'");'."\n"; + $code[] = '$__tag_processor = $tmp[\'prefix\'].\'_TagProcessor\';'."\n"; + $code[] = '$processor =& $application->recallObject($__tag_processor);'."\n"; + $code[] = '$processor->Prefix = $tmp[\'prefix\'];'."\n"; + $code[] = '$processor->Special = $tmp[\'special\'];'."\n"; + + + $code[] = '$o .= $processor->ProcessParsedTag(\''.$this->Tag.'\', '.$to_pass.', "'.$this->Processor.'");'."\n"; + + /*$code = ' $processor =& $application->recallObject(\''.$this->Processor.'_TagProcessor\'); + $o .= $processor->ProcessParsedTag(\''.$this->Tag.'\', unserialize(\''.serialize($this->NP).'\'));';*/ + + if ($echo) $code[] = ' echo $o;'."\n"; + + return $code; + //return '$o .= \'tag:'. $this->Tag .'\''; + } +} + +?> \ No newline at end of file Index: trunk/core/kernel/db/dblist.php =================================================================== diff -u -N --- trunk/core/kernel/db/dblist.php (revision 0) +++ trunk/core/kernel/db/dblist.php (revision 1560) @@ -0,0 +1,686 @@ + null, FLT_NORMAL => null, FLT_SEARCH => null, FLT_VIEW => null); + + /** + * Holds list HAVING filter object + * + * @var kMultipleFilter + * @access private + */ + var $HavingFilter = Array(FLT_SYSTEM => null, FLT_NORMAL => null, FLT_SEARCH => null, FLT_VIEW => null); + + var $GroupByFields = Array(); + + var $Queried = false; + var $Counted = false; + + /** + * Creates kDBList + * + * @return kDBList + */ + function kDBList() { + parent::kDBBase(); + $this->OrderFields = Array(); + + $this->WhereFilter[FLT_SYSTEM] =& $this->Application->makeClass('kMultipleFilter', FLT_TYPE_AND); + $this->WhereFilter[FLT_NORMAL] =& $this->Application->makeClass('kMultipleFilter', FLT_TYPE_OR); + + $this->WhereFilter[FLT_SEARCH] =& $this->Application->makeClass('kMultipleFilter', FLT_TYPE_OR); + $this->WhereFilter[FLT_SEARCH]->setType(FLT_TYPE_OR); + + $this->WhereFilter[FLT_VIEW] =& $this->Application->makeClass('kMultipleFilter', FLT_TYPE_AND); + + $this->HavingFilter[FLT_SYSTEM] =& $this->Application->makeClass('kMultipleFilter', FLT_TYPE_AND); + $this->HavingFilter[FLT_NORMAL] =& $this->Application->makeClass('kMultipleFilter', FLT_TYPE_OR); + + $this->HavingFilter[FLT_SEARCH] =& $this->Application->makeClass('kMultipleFilter', FLT_TYPE_OR); + $this->HavingFilter[FLT_SEARCH]->setType(FLT_TYPE_OR); + + $this->HavingFilter[FLT_VIEW] =& $this->Application->makeClass('kMultipleFilter', FLT_TYPE_AND); + + $this->PerPage = -1; + } + + /** + * Adds new or replaces old filter with same name + * + * @param string $name filter name (for internal use) + * @param string $clause where/having clause part (no OR/AND allowed) + * @param int $filter_type is filter having filter or where filter + * @param int $filter_scope filter subtype: FLT_NORMAL,FLT_SYSTEM,FLT_SEARCH,FLT_VIEW + * @access public + */ + function addFilter($name, $clause, $filter_type = WHERE_FILTER, $filter_scope = FLT_SYSTEM) + { + $filter_name = ($filter_type == WHERE_FILTER) ? 'WhereFilter' : 'HavingFilter'; + + $filter =& $this->$filter_name; + $filter =& $filter[$filter_scope]; + $filter->addFilter($name,$clause); + } + + /** + * Removes specified filter from filters list + * + * @param string $name filter name (for internal use) + * @param int $filter_type is filter having filter or where filter + * @param int $filter_scope filter subtype: FLT_NORMAL,FLT_SYSTEM,FLT_SEARCH,FLT_VIEW + * @access public + */ + function removeFilter($name, $filter_type = WHERE_FILTER, $filter_scope = FLT_SYSTEM) + { + $filter_name = ($filter_type == WHERE_FILTER) ? 'WhereFilter' : 'HavingFilter'; + + $filter =& $this->$filter_name; + $filter =& $filter[$filter_scope]; + $filter->removeFilter($name); + } + + /** + * Clear list filters + * + * @param bool $user clear user filters + * @param bool $system clear system filters + */ + function clearFilters($user=true,$system=true,$search=true,$view=true) + { + if($system) + { + $this->WhereFilter[FLT_SYSTEM]->clearFilters(); + $this->HavingFilter[FLT_SYSTEM]->clearFilters(); + } + if($user) + { + $this->WhereFilter[FLT_NORMAL]->clearFilters(); + $this->HavingFilter[FLT_NORMAL]->clearFilters(); + } + if($search) + { + $this->WhereFilter[FLT_SEARCH]->clearFilters(); + $this->HavingFilter[FLT_SEARCH]->clearFilters(); + } + if($view) + { + $this->WhereFilter[FLT_VIEW]->clearFilters(); + $this->HavingFilter[FLT_VIEW]->clearFilters(); + } + } + + /** + * Counts the total number of records base on the query resulted from {@link kDBList::GetSelectSQL()} + * + * The method modifies the query to substitude SELECT part (fields listing) with COUNT(*). + * Special care should be applied when working with lists based on grouped queries, all aggregate function fields + * like SUM(), AVERAGE() etc. should be added to CountedSQL by using {@link kDBList::SetCountedSQL()} + * + * @access public + * @param string + * @return void + */ + function CountRecs() + { + $all_sql = $this->GetSelectSQL(true,false); + $sql = $this->getCountSQL($all_sql); + $this->RecordsCount = (int)$this->Conn->GetOne($sql); + + $system_sql = $this->GetSelectSQL(true,true); + if ($system_sql == $all_sql) { //no need to query the same again + $this->NoFilterCount = $this->RecordsCount; + return; + } + $sql = $this->getCountSQL($system_sql); + $this->NoFilterCount = (int)$this->Conn->GetOne($sql); + $this->Counted = true; + } + + function getCountSQL($sql) + { + if ( preg_match("/DISTINCT(.*?)FROM(?!_)/is",$sql,$regs ) ) + { + return preg_replace("/^.*SELECT DISTINCT(.*?)FROM(?!_)/is", "SELECT COUNT(DISTINCT ".$regs[1].") AS count FROM", $sql); + } + else + { + return preg_replace("/^.*SELECT(.*?)FROM(?!_)/is", "SELECT COUNT(*) AS count FROM ", $sql); + } + } + + /** + * Queries the database with SQL resulted from {@link kDBList::GetSelectSQL()} and stores result in {@link kDBList::SelectRS} + * + * All the sorting, pagination, filtration of the list should be set prior to calling Query(). + * + * @access public + * @param string + * @return void + */ + function Query($force=false) + { + if (!$force && $this->Queried) return true; + $q = $this->GetSelectSQL(); + if ($this->DisplayQueries) { + echo get_class($this)." Query SQL: $q LIMIT ".$this->PerPage." OFFSET ".$this->Offset." Page: ".$this->Page."
"; + } + //$rs = $this->Conn->SelectLimit($q, $this->PerPage, $this->Offset); + + //in case we have not counted records try to select one more item to find out if we have something more than perpage + $limit = $this->Counted ? $this->PerPage : $this->PerPage+1; + + $sql = $q.' '.$this->Conn->getLimitClause($this->Offset,$limit); + + $this->Records = $this->Conn->Query($sql); + $this->SelectedCount = count($this->Records); + if (!$this->Counted) $this->RecordsCount = $this->SelectedCount; + if (!$this->Counted && $this->SelectedCount > $this->PerPage && $this->PerPage != -1) $this->SelectedCount--; + + if ($this->Records === false) { + //handle errors here + return false; + } + $this->Queried = true; + return true; + } + + /** + * Builds full select query except for LIMIT clause + * + * @access public + * @return string + */ + function GetSelectSQL($for_counting=false,$system_filters_only=false) + { + $q = parent::GetSelectSQL($this->SelectClause); + if(!$for_counting) $q = $this->addCalculatedFields($q); + + $where = $this->GetWhereClause($for_counting,$system_filters_only); + $having = $this->GetHavingClause($for_counting,$system_filters_only); + $order = $this->GetOrderClause(); + $group = $this->GetGroupClause(); + + if (!empty($where)) $q .= ' WHERE ' . $where; + if (!empty($group)) $q .= ' GROUP BY ' . $group; + if (!empty($having)) $q .= ' HAVING ' . $having; + if ( !$for_counting && !empty($order) ) $q .= ' ORDER BY ' . $order; + + return str_replace('%1$s',$this->TableName,$q); + } + + function extractCalculatedFields($clause) + { + if ( is_array($this->CalculatedFields) ) { + foreach($this->CalculatedFields as $field_name => $field_expression) + { + $clause = preg_replace('/`'.$field_name.'`/', $field_expression, $clause); + } + } + return $clause; + } + + /** + * Returns WHERE clause of the query + * + * @access public + * @param bool $for_counting merge where filters with having filters + replace field names for having fields with their values + * @return string + */ + function GetWhereClause($for_counting=false,$system_filters_only=false) + { + $where =& $this->Application->makeClass('kMultipleFilter'); + + $where->addFilter('system_where', $this->WhereFilter[FLT_SYSTEM] ); + + if (!$system_filters_only) { + $where->addFilter('view_where', $this->WhereFilter[FLT_VIEW] ); + $search_w = $this->WhereFilter[FLT_SEARCH]->getSQL(); + if( $search_w || $for_counting ) // move search_having to search_where in case search_where isset or we are counting + { + $search_h = $this->extractCalculatedFields( $this->HavingFilter[FLT_SEARCH]->getSQL() ); + $search_w = ($search_w && $search_h) ? $search_w.' OR '.$search_h : $search_w.$search_h; + $where->addFilter('search_where', $search_w ); + } + } + + if( $for_counting ) // add system_having and view_having to where + { + $where->addFilter('system_having', $this->extractCalculatedFields( $this->HavingFilter[FLT_SYSTEM]->getSQL() ) ); + if (!$system_filters_only) $where->addFilter('view_having', $this->extractCalculatedFields( $this->HavingFilter[FLT_VIEW]->getSQL() ) ); + } + + return $where->getSQL(); + } + + /** + * Depricated method + * + * @param string $clause + * @todo REMOVE + */ + function SetWhereClause($clause) + { + if( $this->Application->isDebugMode() ) + { + global $debugger; + $debugger->appendTrace(); + } + trigger_error('Depricated method kDBList->SetWhereClause. Use kDBList->addFilter instead.', E_USER_ERROR); + } + + /** + * Returns HAVING clause of the query + * + * @param bool $for_counting don't return having filter in case if this is counting sql + * @return string + * @access public + */ + function GetHavingClause($for_counting=false, $system_filters_only=false) + { + if( $for_counting ) return ''; + + $having =& $this->Application->makeClass('kMultipleFilter'); + + $having->addFilter('system_having', $this->HavingFilter[FLT_SYSTEM] ); + if (!$system_filters_only) { + $having->addFilter('view_having', $this->HavingFilter[FLT_VIEW] ); + $search_w = $this->WhereFilter[FLT_SEARCH]->getSQL(); + if (!$search_w) { + $having->addFilter('search_having', $this->HavingFilter[FLT_SEARCH] ); + } + } + + return $having->getSQL(); + } + + /** + * Returns GROUP BY clause of the query + * + * @access public + * @return string + */ + function GetGroupClause() + { + return $this->GroupByFields ? implode(',', $this->GroupByFields) : ''; + } + + function AddGroupByField($field) + { + $this->GroupByFields[$field] = $field; + } + + function RemoveGroupByField($field) + { + unset($this->GroupByFields[$field]); + } + + /** + * Adds order field to ORDER BY clause + * + * @access public + * @param string $field Field name + * @param string $direction Direction of ordering (asc|desc) + * @return void + */ + function AddOrderField($field, $direction) + { + // original multilanguage field - convert to current lang field + if (getArrayValue($this->Fields, $field, 'formatter') == 'kMultiLanguage' && !getArrayValue($this->Fields, $field, 'master_field')) { + $lang = $this->Application->GetVar('m_lang'); + $field = 'l'.$lang.'_'.$field; + } + + $this->OrderFields[] = Array($field, $direction); + } + + /** + * Removes all order fields + * + * @access public + * @return void + */ + function ClearOrderFields() + { + $this->OrderFields = Array(); + } + + /** + * Returns ORDER BY Clause of the query + * + * The method builds order by clause by iterating {@link kDBList::OrderFields} array and concatenating it. + * + * @access public + * @return string + */ + function GetOrderClause() + { + $ret = ''; + foreach ($this->OrderFields as $field) { + + $name = $field[0]; + $ret .= isset($this->Fields[$name]) && !isset($this->VirtualFields[$name]) ? '`'.$this->TableName.'`.' : ''; + if ($field[0] == 'RAND()') { + $ret .= $field[0].' '.$field[1].','; + } + else { + $ret .= '`'.$field[0] . '` ' . $field[1] . ','; + } + } + $ret = rtrim($ret, ','); + return $ret; + } + + function GetOrderField($pos=NULL) + { + if(!(isset($this->OrderFields[$pos]) && $this->OrderFields[$pos]) ) + { + $pos = 0; + } + return isset($this->OrderFields[$pos][0]) ? $this->OrderFields[$pos][0] : ''; + } + + function GetOrderDirection($pos=NULL) + { + if(!$this->OrderFields[$pos]) + $pos = 0; + + return $this->OrderFields[$pos][1]; + } + + /** + * Return unformatted field value + * + * @param string + * @return mixed + * @access public + */ + function GetDBField($name) + { + $row =& $this->getCurrentRecord(); + return $row[$name]; + } + + function HasField($name) + { + $row =& $this->getCurrentRecord(); + return isset($row[$name]); + } + + function GetFieldValues() + { + return $this->getCurrentRecord(); + } + + function &getCurrentRecord() + { + return $this->Records[$this->CurrentIndex]; + } + + /** + * Description + * + * @access public + * @param string + * @return void + */ + function GoFirst() + { + $this->CurrentIndex = 0; + } + + /** + * Description + * + * @access public + * @return void + */ + function GoNext() + { + $this->CurrentIndex++; + } + + /** + * Description + * + * @access public + * @return void + */ + function GoPrev() + { + if ($this->CurrentIndex>0) + $this->CurrentIndex--; + } + + /** + * Description + * + * @access public + * @return bool + */ + function EOL() + { + return ($this->CurrentIndex >= $this->SelectedCount); + } + + /** + * Description + * + * @access public + * @param string + * @return void + */ + function GetTotalPages() + { + if (!$this->Counted) $this->CountRecs(); + if ($this->PerPage == -1) return 1; + $this->TotalPages = (($this->RecordsCount - ($this->RecordsCount % $this->PerPage)) / $this->PerPage) // integer part of division + + (($this->RecordsCount % $this->PerPage) != 0); // adds 1 if there is a reminder + return $this->TotalPages; + } + + /** + * Sets number of records to query per page + * + * @access public + * @param int $per_page Number of records to display per page + * @return void + */ + function SetPerPage($per_page) + { + $this->PerPage = $per_page; + } + + function GetPerPage() + { + return $this->PerPage == -1 ? $this->RecordsCount : $this->PerPage; + } + + /** + * Description + * + * @access public + * @param int $page + * @return void + */ + function SetPage($page) + { + if ($this->PerPage == -1) { + $this->Page = 1; + return; + } + if ($page < 1) $page = 1; + $this->Offset = ($page-1)*$this->PerPage; + if ($this->Counted && $this->Offset > $this->RecordsCount) { + $this->SetPage(1); + } + else { + $this->Page = $page; + } + //$this->GoFirst(); + } + + /** + * 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->Records[$this->CurrentIndex][$name] = $value; + } + + /** + * Apply where clause, that links this object to it's parent item + * + * @param string $special + * @access public + */ + function linkToParent($special) + { + $parent_prefix = $this->Application->getUnitOption($this->Prefix, 'ParentPrefix'); + if($parent_prefix) + { + $parent_table_key = $this->Application->getUnitOption($this->Prefix, 'ParentTableKey'); + $foreign_key_field = $this->Application->getUnitOption($this->Prefix, 'ForeignKey'); + + $parent_object =& $this->Application->recallObject($parent_prefix.'.'.$special); + $parent_id = $parent_object->GetDBField($parent_table_key); + + $this->addFilter('parent_filter', '`'.$this->TableName.'`.`'.$foreign_key_field.'` = '.$parent_id); // only for list in this case + } + } +} + +?> \ No newline at end of file Index: trunk/core/kernel/application.php =================================================================== diff -u -N --- trunk/core/kernel/application.php (revision 0) +++ trunk/core/kernel/application.php (revision 1560) @@ -0,0 +1,1280 @@ + +* The class incapsulates the main run-cycle of the script, provide access to all other objects in the framework.
+*
+* The class is a singleton, which means that there could be only one instance of KernelApplication in the script.
+* This could be guranteed by NOT calling the class constuctor directly, but rather calling KernelApplication::Instance() method, +* which returns an instance of the application. The method gurantees that it will return exactly the same instance for any call.
+* See singleton pattern by GOF. +* @package kernel4 +*/ + +class kApplication { + + /** + * Holds internal TemplateParser object + * @access private + * @var TemplateParser + */ + var $Parser; + + var $Profiler; + + /** + * Holds parser output buffer + * @access private + * @var string + */ + var $HTML; + + var $DocRoot; + var $BasePath; + var $KernelPath; + var $Server; + + + /** + * Prevents request from beeing proceeded twice in case if application init is called mere then one time + * + * @var bool + * @todo This is not good anyway (by Alex) + */ + var $RequestProcessed = false; + + /** + * The main Factory used to create + * almost any class of kernel and + * modules + * + * @access private + * @var kFactory + */ + var $Factory; + + var $XMLFactory; // in use? + + /** + * Holds all phrases used + * in code and template + * + * @var PhrasesCache + */ + var $Phrases; + + /** + * Holds DBConnection + * + * @var kDBConnection + */ + var $DB; + + /** + * Constucts KernelApplication - constructor is PRIVATE + * + * The constuructor of KernelApplication should NOT be called directly + * To create KernelApplication, call its Instance() method + * @see KerenelApplication::Instance + * @access private + */ + function kApplication() + { + global $doc_root, $base_path, $kernel_path, $protocol, $server; + $this->DocRoot = $doc_root; + $this->BasePath = $base_path; + $this->KernelPath = $kernel_path; + $this->Protocol = $protocol; + $this->Server = $server; + } + + /** + * Returns kApplication instance anywhere in the script. + * + * This method should be used to get single kApplication object instance anywhere in the + * Kernel-based application. The method is guranteed to return the SAME instance of kApplication. + * Anywhere in the script you could write: + * + * $application =& kApplication::Instance(); + * + * or in an object: + * + * $this->Application =& kApplication::Instance(); + * + * to get the instance of kApplication. Note that we call the Instance method as STATIC - directly from the class. + * To use descendand of standard kApplication class in your project you would need to define APPLICATION_CLASS constant + * BEFORE calling kApplication::Instance() for the first time. If APPLICATION_CLASS is not defined the method would + * create and return default KernelApplication instance. + * @static + * @access public + * @return kApplication + */ + function &Instance() + { + static $instance = false; + + if (!$instance) { + if (!defined('APPLICATION_CLASS')) define('APPLICATION_CLASS', 'kApplication'); + $class = APPLICATION_CLASS; + $instance = new $class(); + } + return $instance; + } + + /** + * Initializes the Application + * + * Creates Utilites instance, HTTPQuery, Session, Profiler, TemplatesCache, Parser + * @access public + * @see HTTPQuery + * @see Session + * @see TemplatesCache + * @return void + */ + function Init() + { + if (defined('DEBUG_MODE') && DEBUG_MODE && dbg_ConstOn('DBG_PROFILE_MEMORY') ) { + global $debugger; + $debugger->appendMemoryUsage('Application before Init:'); + } + + if( !$this->isDebugMode() ) set_error_handler( Array(&$this,'handleError') ); + + $this->DB = new kDBConnection(SQL_TYPE, Array(&$this,'handleSQLError') ); + $this->DB->Connect(SQL_SERVER, SQL_USER, SQL_PASS, SQL_DB); + $this->DB->debugMode = $this->isDebugMode(); + + $this->SetDefaultConstants(); + + $this->Factory = new kFactory(); + + $this->registerDefaultClasses(); + + // 1. to read configs before doing any recallObject + $config_reader =& $this->recallObject('kUnitConfigReader'); + + if( !$this->GetVar('m_lang') ) $this->SetVar('m_lang', $this->GetDefaultLanguageId() ); + if( !$this->GetVar('m_theme') ) $this->SetVar('m_theme', $this->GetDefaultThemeId()); + $this->Phrases = new PhrasesCache( $this->GetVar('m_lang') ); + + $this->SetVar('lang.current_id', $this->GetVar('m_lang') ); + $language =& $this->recallObject('lang.current', null, Array('live_table'=>true) ); + + if( !$this->GetVar('m_theme') ) $this->SetVar('m_theme', $this->GetDefaultThemeId() ); + $this->SetVar('theme.current_id', $this->GetVar('m_theme') ); + + if ( $this->GetVar('m_cat_id') === false ) $this->SetVar('m_cat_id', 2); //need to rewrite + + if( !$this->RecallVar('UserGroups') ) + { + $this->StoreVar('UserGroups', $this->ConfigValue('User_GuestGroup')); + } + + if( !$this->RecallVar('curr_iso') ) $this->StoreVar('curr_iso', $this->GetPrimaryCurrency() ); + + $this->ValidateLogin(); // TODO: write that method + + if( $this->isDebugMode() ) + { + global $debugger; + $debugger->profileFinish('kernel4_startup'); + } + } + + function GetDefaultLanguageId() + { + $table = $this->getUnitOption('lang','TableName'); + $id_field = $this->getUnitOption('lang','IDField'); + return $this->DB->GetOne('SELECT '.$id_field.' FROM '.$table.' WHERE PrimaryLang = 1'); + } + + function GetDefaultThemeId() + { + if (defined('DBG_FORCE_THEME') && DBG_FORCE_THEME){ + return DBG_FORCE_THEME; + } + $table = $this->getUnitOption('theme','TableName'); + $id_field = $this->getUnitOption('theme','IDField'); + return $this->DB->GetOne('SELECT '.$id_field.' FROM '.$table.' WHERE PrimaryTheme = 1'); + } + + function GetPrimaryCurrency() + { + $table = $this->getUnitOption('curr','TableName'); + return $this->DB->GetOne('SELECT ISO FROM '.$table.' WHERE IsPrimary = 1'); + } + + /** + * Registers default classes such as ItemController, GridController and LoginController + * + * Called automatically while initializing Application + * @access private + * @return void + */ + function RegisterDefaultClasses() + { + //$this->registerClass('Utilites',KERNEL_PATH.'/utility/utilities.php'); + $this->registerClass('HTTPQuery',KERNEL_PATH.'/utility/http_query.php'); + $this->registerClass('Session',KERNEL_PATH.'/session/session.php'); + $this->registerClass('SessionStorage',KERNEL_PATH.'/session/session.php'); + $this->registerClass('LoginEventHandler',KERNEL_PATH.'/session/login_event_handler.php','login_EventHandler'); + $this->registerClass('kEventManager',KERNEL_PATH.'/event_manager.php','EventManager'); + + $this->registerClass('kUnitConfigReader',KERNEL_PATH.'/utility/unit_config_reader.php'); + + + $this->registerClass('Params',KERNEL_PATH.'/utility/params.php','kActions'); + $this->registerClass('kArray',KERNEL_PATH.'/utility/params.php','kArray'); + $this->registerClass('kFormatter', KERNEL_PATH.'/utility/formatters.php'); + $this->registerClass('kOptionsFormatter', KERNEL_PATH.'/utility/formatters.php'); + $this->registerClass('kPictureFormatter', KERNEL_PATH.'/utility/formatters.php'); + $this->registerClass('kDateFormatter', KERNEL_PATH.'/utility/formatters.php'); + $this->registerClass('kLEFTFormatter', KERNEL_PATH.'/utility/formatters.php'); + $this->registerClass('kMultiLanguage', KERNEL_PATH.'/utility/formatters.php'); + $this->registerClass('kPasswordFormatter', KERNEL_PATH.'/utility/formatters.php'); + $this->registerClass('kCCDateFormatter', KERNEL_PATH.'/utility/formatters.php'); + + $this->registerClass('kTempTablesHandler', KERNEL_PATH.'/utility/temp_handler.php'); + + $event_manager =& $this->recallObject('EventManager'); + $event_manager->registerBuildEvent('kTempTablesHandler','OnTempHandlerBuild'); + //$this->registerClass('Configuration',KERNEL_PATH.'/utility/configuration.php'); + + $this->registerClass('TemplatesCache',KERNEL_PATH.'/parser/template.php'); + $this->registerClass('Template',KERNEL_PATH.'/parser/template.php'); + $this->registerClass('TemplateParser',KERNEL_PATH.'/parser/template_parser.php'); + + $this->registerClass('MainProcessor', KERNEL_PATH.'/processors/main_processor.php','m_TagProcessor'); + + $this->registerClass('kMultipleFilter', KERNEL_PATH.'/utility/filters.php'); + $this->registerClass('kDBList', KERNEL_PATH.'/db/dblist.php'); + $this->registerClass('kDBItem', KERNEL_PATH.'/db/dbitem.php'); + $this->registerClass('kDBEventHandler', KERNEL_PATH.'/db/db_event_handler.php'); + $this->registerClass('kDBTagProcessor', KERNEL_PATH.'/db/db_tag_processor.php'); + + $this->registerClass('kTagProcessor', KERNEL_PATH.'/processors/tag_processor.php'); + $this->registerClass('kEmailMessage',KERNEL_PATH.'/utility/email.php'); + $this->registerClass('kSmtpClient',KERNEL_PATH.'/utility/smtp_client.php'); + + if (file_exists(MODULES_PATH.'/in-commerce/units/rates/currency_rates.php')) { + $this->registerClass('kCurrencyRates',MODULES_PATH.'/in-commerce/units/rates/currency_rates.php'); + } + + /*$this->RegisterClass('LoginController', KERNEL_PATH.'/users/login_controller.php');*/ + } + + /** + * Defines default constants if it's not defined before - in config.php + * + * Called automatically while initializing Application and defines: + * LOGIN_CONTROLLER, XML_FACTORY etc. + * @access private + * @return void + */ + function SetDefaultConstants() + { + if (!defined('SERVER_NAME')) define('SERVER_NAME', $_SERVER['SERVER_NAME']); + if (!defined('LOGIN_CONTROLLER')) define('LOGIN_CONTROLLER', 'LoginController'); + if (!defined('XML_FACTORY')) define('XML_FACTORY', 'XMLFactory'); + if (!defined('ADMINS_LIST')) define('ADMINS_LIST', '/users/users.php'); + if (!defined('USER_MODEL')) define('USER_MODEL', 'Users'); + if (!defined('DEFAULT_LANGUAGE_ID')) define('DEFAULT_LANGUAGE_ID', 1); + } + + function ProcessRequest() + { + $event_manager =& $this->recallObject('EventManager'); + + if( $this->isDebugMode() && dbg_ConstOn('DBG_SHOW_HTTPQUERY') ) + { + global $debugger; + $http_query =& $this->recallObject('HTTPQuery'); + $debugger->appendHTML('HTTPQuery:'); + $debugger->dumpVars($http_query->_Params); + } + + $event_manager->ProcessRequest(); + $this->RequestProcessed = true; + } + + /** + * Actually runs the parser against current template and stores parsing result + * + * This method gets t variable passed to the script, loads the template given in t variable and + * parses it. The result is store in {@link $this->HTML} property. + * @access public + * @return void + */ + function Run() + { + if (defined('DEBUG_MODE') && DEBUG_MODE && dbg_ConstOn('DBG_PROFILE_MEMORY') ) { + global $debugger; + $debugger->appendMemoryUsage('Application before Run:'); + } + + if (!$this->RequestProcessed) $this->ProcessRequest(); + + $this->InitParser(); + $template_cache =& $this->recallObject('TemplatesCache'); + $t = $this->GetVar('t'); + + + if (defined('CMS') && CMS) { + if (!$template_cache->TemplateExists($t)) { + $cms_handler =& $this->recallObject('cms_EventHandler'); + $t = $cms_handler->GetDesignTemplate(); + } + } + + if (defined('DEBUG_MODE') && DEBUG_MODE && dbg_ConstOn('DBG_PROFILE_MEMORY') ) { + global $debugger; + $debugger->appendMemoryUsage('Application before Parsing:'); + } + + $this->HTML = $this->Parser->Parse( $template_cache->GetTemplateBody($t), $t ); + + if (defined('DEBUG_MODE') && DEBUG_MODE && dbg_ConstOn('DBG_PROFILE_MEMORY') ) { + global $debugger; + $debugger->appendMemoryUsage('Application after Parsing:'); + } + } + + function InitParser() + { + if( !is_object($this->Parser) ) $this->Parser =& $this->recallObject('TemplateParser'); + } + + /** + * Send the parser results to browser + * + * Actually send everything stored in {@link $this->HTML}, to the browser by echoing it. + * @access public + * @return void + */ + function Done() + { + if (defined('DEBUG_MODE') && DEBUG_MODE && dbg_ConstOn('DBG_PROFILE_MEMORY') ) { + global $debugger; + $debugger->appendMemoryUsage('Application before Done:'); + } + + //eval("?".">".$this->HTML); + echo $this->HTML; + $this->Phrases->UpdateCache(); + + $session =& $this->recallObject('Session'); + $session->SaveData(); + //$this->SaveBlocksCache(); + } + + function SaveBlocksCache() + { + if (defined('EXPERIMENTAL_PRE_PARSE')) { + $data = serialize($this->PreParsedCache); + + $this->DB->Query('REPLACE '.TABLE_PREFIX.'Cache (VarName, Data, Cached) VALUES ("blocks_cache", '.$this->DB->qstr($data).', '.time().')'); + } + } + + // Facade + + /** + * Returns current session id (SID) + * @access public + * @return longint + */ + function GetSID() + { + $session =& $this->recallObject('Session'); + return $session->GetID(); + } + + function DestroySession() + { + $session =& $this->recallObject('Session'); + $session->Destroy(); + } + + /** + * Returns variable passed to the script as GET/POST/COOKIE + * + * @access public + * @param string $var Variable name + * @return mixed + */ + function GetVar($var,$mode=FALSE_ON_NULL) + { + $http_query =& $this->recallObject('HTTPQuery'); + return $http_query->Get($var,$mode); + } + + /** + * Returns ALL variables passed to the script as GET/POST/COOKIE + * + * @access public + * @return array + */ + function GetVars() + { + $http_query =& $this->recallObject('HTTPQuery'); + return $http_query->GetParams(); + } + + /** + * Set the variable 'as it was passed to the script through GET/POST/COOKIE' + * + * This could be useful to set the variable when you know that + * other objects would relay on variable passed from GET/POST/COOKIE + * or you could use SetVar() / GetVar() pairs to pass the values between different objects.
+ * + * This method is formerly known as $this->Session->SetProperty. + * @param string $var Variable name to set + * @param mixed $val Variable value + * @access public + * @return void + */ + function SetVar($var,$val) + { + $http_query =& $this->recallObject('HTTPQuery'); + $http_query->Set($var,$val); + } + + /** + * Deletes Session variable + * + * @param string $var + */ + function RemoveVar($var) + { + $session =& $this->recallObject('Session'); + return $session->RemoveVar($var); + } + + /** + * Deletes HTTPQuery variable + * + * @param string $var + * @todo think about method name + */ + function DeleteVar($var) + { + $http_query =& $this->recallObject('HTTPQuery'); + return $http_query->Remove($var); + } + + /** + * Returns session variable value + * + * Return value of $var variable stored in Session. An optional default value could be passed as second parameter. + * + * @see SimpleSession + * @access public + * @param string $var Variable name + * @param mixed $default Default value to return if no $var variable found in session + * @return mixed + */ + function RecallVar($var,$default=false) + { + $session =& $this->recallObject('Session'); + return $session->RecallVar($var,$default); + } + + /** + * Stores variable $val in session under name $var + * + * Use this method to store variable in session. Later this variable could be recalled. + * @see RecallVar + * @access public + * @param string $var Variable name + * @param mixed $val Variable value + */ + function StoreVar($var, $val) + { + $session =& $this->recallObject('Session'); + $session->StoreVar($var, $val); + } + + function StoreVarDefault($var, $val) + { + $session =& $this->recallObject('Session'); + $session->StoreVarDefault($var, $val); + } + + /** + * Links HTTP Query variable with session variable + * + * If variable $var is passed in HTTP Query it is stored in session for later use. If it's not passed it's recalled from session. + * This method could be used for making sure that GetVar will return query or session value for given + * variable, when query variable should overwrite session (and be stored there for later use).
+ * This could be used for passing item's ID into popup with multiple tab - + * in popup script you just need to call LinkVar('id', 'current_id') before first use of GetVar('id'). + * After that you can be sure that GetVar('id') will return passed id or id passed earlier and stored in session + * @access public + * @param string $var HTTP Query (GPC) variable name + * @param mixed $ses_var Session variable name + * @param mixed $default Default variable value + */ + function LinkVar($var, $ses_var=null, $default='') + { + if (!isset($ses_var)) $ses_var = $var; + if ($this->GetVar($var) !== false) + { + $this->StoreVar($ses_var, $this->GetVar($var)); + } + else + { + $this->SetVar($var, $this->RecallVar($ses_var, $default)); + } + } + + /** + * Returns variable from HTTP Query, or from session if not passed in HTTP Query + * + * The same as LinkVar, but also returns the variable value taken from HTTP Query if passed, or from session if not passed. + * Returns the default value if variable does not exist in session and was not passed in HTTP Query + * + * @see LinkVar + * @access public + * @param string $var HTTP Query (GPC) variable name + * @param mixed $ses_var Session variable name + * @param mixed $default Default variable value + * @return mixed + */ + function GetLinkedVar($var, $ses_var=null, $default='') + { + if (!isset($ses_var)) $ses_var = $var; + $this->LinkVar($var, $ses_var, $default); + return $this->GetVar($var); + } + + /*function ExtractByMask($array, $mask, $key_id=1, $ret_mode=1) + { + $utils =& $this->recallObject('Utilities'); + return $utils->ExtractByMask($array, $mask, $key_id, $ret_mode); + } + + function GetSelectedIDs($mask, $format) + { + $http_query =& $this->recallObject('HTTPQuery'); + return $http_query->GetSelectedIDs($mask, $format); + } + + function GetSelectedIDsArray($mask, $value_mask="%s,") + { + $http_query =& $this->recallObject('HTTPQuery'); + return $http_query->GetSelectedIDsArray($mask, $value_mask); + }*/ + + /** + * Returns configurtion option + * + * @param string $option + * @return string + * @access public + */ + /*function ConfigOption($option) + { + $config =& $this->recallObject('Configuration'); + return $config->Get($option); + }*/ + + /** + * Sets configuration option + * + * @param string $option + * @param string $value + * @return bool + * @access public + */ + /*function SetConfigOption($option,$value) + { + $config =& $this->recallObject('Configuration'); + return $config->Set($option, $value); + }*/ + + function AddBlock($name, $tpl) + { + $this->cache[$name] = $tpl; + } + + function SetTemplateBody($title,$body) + { + $templates_cache =& $this->recallObject('TemplatesCache'); + $templates_cache->SetTemplateBody($title,$body); + } + + function ProcessTag($tag_data) + { + $a_tag = new Tag($tag_data,$this->Parser); + return $a_tag->DoProcessTag(); + } + + function ProcessParsedTag($prefix, $tag, $params) + { + $a_tag = new Tag('',$this->Parser); + $a_tag->Tag = $tag; + $a_tag->Processor = $prefix; + $a_tag->NamedParams = $params; + return $a_tag->DoProcessTag(); + } + + /* DEFINETLY NEEDS TO BE MOVED AWAY!!!!! */ + /*var $email_body; + function Email($params) + { + $this->email_body = $this->ParseBlock($params); + $from = $this->GetVar('email_from'); + $to = $this->GetVar('email_to'); + $replay = $this->GetVar('email_replay'); + if ( $replay == "" ) $replay = $from; + $subject = $this->GetVar('email_subject'); + $charset = $this->GetVar('email_charset'); + // $display = $this->GetVar('email_display'); + $display = 0; + if (!isset($charset) || $charset == '') $charset = 'US-ASCII'; + $mime = $this->GetVar('email_mime'); + + if ($mime == 'yes') { + $mime_mail = new MIMEMail($to, $from, $subject, $charset); + $mime_mail->mailbody($this->email_body); + + if ($f_name = $this->GetVar('email_attach')) { + $full_path = DOC_ROOT.BASE_PATH.'/'.$f_name; + $data = ''; + if(file_exists($full_path)) { + $fd = fopen($full_path, "r"); + $data = fread($fd, filesize($full_path)); + fclose($fd); + } + else exit; + + $filename = $this->GetVar('email_attach_filename'); + $type = $this->GetVar('email_attach_type'); + + $mime_mail->attachfile_raw($data, $filename, $type); + $mime_mail->send(); + } + } + else { + $headers.="From: $from\n"; + $headers.="Reply-To: $replay\n"; + $headers.="Content-Type: text/html; charset=\"$charset\"\n"; + if ( $display == 1 ) { + echo "
";
+	 			echo " from : $from 
"; + echo " to : $to
"; + echo " replay : $replay
"; + echo " subject : $subject
"; + echo " this->email_body : $this->email_body
"; + echo " headers : $headers
"; + echo "
"; + } + mail($to, $subject, $this->email_body, $headers); + } + }*/ + + /** + * Return ADODB Connection object + * + * Returns ADODB Connection object already connected to the project database, configurable in config.php + * @access public + * @return ADODBConnection + */ + function &GetADODBConnection() + { + return $this->DB; + } + + function ParseBlock($params,$pass_params=0,$as_template=false) + { + if (substr($params['name'], 0, 5) == 'html:') return substr($params['name'], 6); + return $this->Parser->ParseBlock($params, $pass_params, $as_template); + } + + function &GetXMLFactory() + { + return $this->XMLFactory; + } + + /** + * Return href for template + * + * @access public + * @param string $t Template path + * @var string $prefix index.php prefix - could be blank, 'admin' + */ + function HREF($t, $prefix='', $params=null, $index_file=null) + { + global $HTTP_SERVER_VARS; + if (defined('ADMIN') && $prefix == '') $prefix='/admin'; + if (defined('ADMIN') && $prefix == '_FRONT_END_') $prefix = ''; + $index_file = isset($index_file) ? $index_file : (defined('INDEX_FILE') ? INDEX_FILE : basename($_SERVER['PHP_SELF'])); + + if( isset($params['index_file']) ) $index_file = $params['index_file']; + + if (getArrayValue($params, 'opener') == 'u') { + $opener_stack=$this->RecallVar('opener_stack'); + if($opener_stack) { + $opener_stack=unserialize($opener_stack); + if (count($opener_stack) > 0) { + list($index_file, $env) = explode('|', $opener_stack[count($opener_stack)-1]); + $ret = $this->BaseURL($prefix).$index_file.'?'.ENV_VAR_NAME.'='.$env; + if( getArrayValue($params,'escape') ) $ret = addslashes($ret); + return $ret; + } + else { + //define('DBG_REDIRECT', 1); + $t = $this->GetVar('t'); + } + } + else { + //define('DBG_REDIRECT', 1); + $t = $this->GetVar('t'); + } + } + + $pass = isset($params['pass']) ? $params['pass'] : ''; + $pass_events = isset($params['pass_events']) ? $params['pass_events'] : false; // pass events with url + + + + if (defined('MOD_REWRITE') && MOD_REWRITE) { + $env = $this->BuildEnv('', $params, $pass, $pass_events, false); + $env = ltrim($env, ':-'); + + $session =& $this->recallObject('Session'); + $sid = $session->NeedQueryString() ? '?sid='.$this->GetSID() : ''; +// $env = str_replace(':', '/', $env); + $ret = $this->BaseURL($prefix).$t.'.html/'.$env.'/'.$sid; + + } + else { + $env = $this->BuildEnv($t, $params, $pass, $pass_events); + $ret = $this->BaseURL($prefix).$index_file.'?'.$env; + } + + return $ret; + } + + function BuildEnv($t, $params, $pass='all', $pass_events=false, $env_var=true) + { + $session =& $this->recallObject('Session'); + $sid = $session->NeedQueryString() && !(defined('MOD_REWRITE') && MOD_REWRITE) ? $this->GetSID() : ''; + $ret = ''; + if ($env_var) { + $ret = ENV_VAR_NAME.'='; + } + $ret .= defined('INPORTAL_ENV') ? $sid.'-'.$t : $sid.':'.$t; + + $pass = str_replace('all', trim($this->GetVar('passed'), ','), $pass); + + if(strlen($pass) > 0) + { + $pass_info = array_unique( explode(',',$pass) ); // array( prefix[.special], prefix[.special] ... + foreach($pass_info as $pass_element) + { + $ret.=':'; + list($prefix)=explode('.',$pass_element); + $query_vars = $this->getUnitOption($prefix,'QueryString'); + + //if pass events is off and event is not implicity passed + if(!$pass_events && !isset($params[$pass_element.'_event'])) { + $params[$pass_element.'_event'] = ''; // remove event from url if requested + //otherwise it will use value from get_var + } + + if($query_vars) + { + $tmp_string=Array(0=>$pass_element); + foreach($query_vars as $index => $var_name) + { + //if value passed in params use it, otherwise use current from application + $tmp_string[$index] = isset( $params[$pass_element.'_'.$var_name] ) ? $params[$pass_element.'_'.$var_name] : $this->GetVar($pass_element.'_'.$var_name); + if ( isset($params[$pass_element.'_'.$var_name]) ) { + unset( $params[$pass_element.'_'.$var_name] ); + } + } + + $escaped = array(); + foreach ($tmp_string as $tmp_val) { + $escaped[] = str_replace(Array('-',':'), Array('\-','\:'), $tmp_val); + } + + if ($this->getUnitOption($prefix, 'PortalStyleEnv') == true) { + $ret.= array_shift($escaped).array_shift($escaped).'-'.implode('-',$escaped); + } + else { + $ret.=implode('-',$escaped); + } + } + } + } + unset($params['pass']); + unset($params['opener']); + unset($params['m_event']); + + if ($this->GetVar('admin') && !isset($params['admin'])) { + $params['admin'] = 1; + } + + foreach ($params as $param => $value) + { + $ret .= '&'.$param.'='.$value; + } + if( getArrayValue($params,'escape') ) $ret = addslashes($ret); + return $ret; + } + + function BaseURL($prefix='') + { + return PROTOCOL.SERVER_NAME.(defined('PORT')?':'.PORT : '').BASE_PATH.$prefix.'/'; + } + + function Redirect($t='', $params=null, $prefix='', $index_file=null) + { + if ($t == '' || $t === true) $t = $this->GetVar('t'); + + // pass prefixes and special from previous url + if (!isset($params['pass'])) $params['pass'] = 'all'; + + $location = $this->HREF($t, $prefix, $params, $index_file); + $a_location = $location; + $location = "Location: $location"; + //echo " location : $location
"; + + if (headers_sent() != '' || ($this->isDebugMode() && dbg_ConstOn('DBG_REDIRECT')) ) { + /*$GLOBALS['debugger']->appendTrace(); + echo "Debug output above!!! Proceed to redirect: $a_location
";*/ + echo ''; + } + else { + header("$location"); + } + + $session =& $this->recallObject('Session'); + $session->SaveData(); + $this->SaveBlocksCache(); + exit; + } + + function Phrase($label) + { + return $this->Phrases->GetPhrase($label); + } + + /** + * Replace language tags in exclamation marks found in text + * + * @param string $text + * @param bool $force_escape force escaping, not escaping of resulting string + * @return string + * @access public + */ + function ReplaceLanguageTags($text, $force_escape=null) + { + return $this->Phrases->ReplaceLanguageTags($text,$force_escape); + } + + /** + * Validtates user in session if required + * + */ + function ValidateLogin() + { + if (defined('LOGIN_REQUIRED')) + { + // Original Kostja call + //$login_controller =& $this->Factory->MakeClass(LOGIN_CONTROLLER, Array('model' => USER_MODEL, 'prefix' => 'login')); + + // Call proposed by Alex + //$login_controller =& $this->RecallObject(LOGIN_CONTROLLER, Array('model' => USER_MODEL, 'prefix' => 'login')); + + //$login_controller->CheckLogin(); + } + } + + /** + * Returns configuration option value by name + * + * @param string $name + * @return string + */ + function ConfigValue($name) + { + return $this->DB->GetOne('SELECT VariableValue FROM '.TABLE_PREFIX.'ConfigurationValues WHERE VariableName = '.$this->DB->qstr($name) ); + } + + /** + * Allows to process any type of event + * + * @param kEvent $event + * @access public + * @author Alex + */ + function HandleEvent(&$event, $params=null, $specificParams=null) + { + if ( isset($params) ) { + $event = new kEvent( $params, $specificParams ); + } + $event_manager =& $this->recallObject('EventManager'); + $event_manager->HandleEvent($event); + } + + /** + * Registers new class in the factory + * + * @param string $real_class + * @param string $file + * @param string $pseudo_class + * @access public + * @author Alex + */ + function registerClass($real_class,$file,$pseudo_class=null) + { + $this->Factory->registerClass($real_class,$file,$pseudo_class); + } + + /** + * Registers Hook from subprefix event to master prefix event + * + * @param string $hookto_prefix + * @param string $hookto_special + * @param string $hookto_event + * @param string $mode + * @param string $do_prefix + * @param string $do_special + * @param string $do_event + * @param string $conditional + * @access public + * @todo take care of a lot parameters passed + * @author Kostja + */ + function registerHook($hookto_prefix, $hookto_special, $hookto_event, $mode, $do_prefix, $do_special, $do_event, $conditional) + { + $event_manager =& $this->recallObject('EventManager'); + $event_manager->registerHook($hookto_prefix, $hookto_special, $hookto_event, $mode, $do_prefix, $do_special, $do_event, $conditional); + } + + /** + * Allows one TagProcessor tag act as other TagProcessor tag + * + * @param Array $tag_info + * @author Kostja + */ + function registerAggregateTag($tag_info) + { + $aggregator =& $this->recallObject('TagsAggregator', 'kArray'); + $aggregator->SetArrayValue($tag_info['AggregateTo'], $tag_info['AggregatedTagName'], Array($tag_info['LocalPrefix'], $tag_info['LocalTagName'])); + } + + /** + * Returns object using params specified, + * creates it if is required + * + * @param string $name + * @param string $pseudo_class + * @param Array $event_params + * @return Object + * @author Alex + */ + function &recallObject($name,$pseudo_class=null,$event_params=Array()) + { + $o1 =& $this->Factory->getObject($name,$pseudo_class,$event_params); + + //$o1->param1 = 'one'; + + /*$func_args = func_get_args(); + $factory =& $this->Factory; + $o2 =& call_user_func_array( Array(&$factory, 'getObject'), $func_args );*/ + + //$o2->param1 = 'two'; + return $o1; + } + + /** + * Checks if object with prefix passes was already created in factory + * + * @param string $name object presudo_class, prefix + * @return bool + * @author Kostja + */ + function hasObject($name) + { + return isset($this->Factory->Storage[$name]); + } + + /** + * Removes object from storage by given name + * + * @param string $name Object's name in the Storage + * @author Kostja + */ + function removeObject($name) + { + $this->Factory->DestroyObject($name); + } + + /** + * Get's real class name for pseudo class, + * includes class file and creates class + * instance + * + * @param string $pseudo_class + * @return Object + * @access public + * @author Alex + */ + function &makeClass($pseudo_class) + { + $func_args = func_get_args(); + return call_user_func_array( Array(&$this->Factory, 'makeClass'), $func_args); + } + + /** + * Checks if application is in debug mode + * + * @return bool + * @access public + * @author Alex + */ + function isDebugMode() + { + return defined('DEBUG_MODE') && DEBUG_MODE; + } + + /** + * Checks if it is admin + * + * @return bool + * @author Alex + */ + function IsAdmin() + { + return defined('ADMIN') && ADMIN; + } + + /** + * Reads unit (specified by $prefix) + * option specified by $option + * + * @param string $prefix + * @param string $option + * @return string + * @access public + * @author Alex + */ + function getUnitOption($prefix,$option) + { + $unit_config_reader =& $this->recallObject('kUnitConfigReader'); + return $unit_config_reader->getUnitOption($prefix,$option); + } + + /** + * Set's new unit option value + * + * @param string $prefix + * @param string $name + * @param string $value + * @author Alex + * @access public + */ + function setUnitOption($prefix,$option,$value) + { + $unit_config_reader =& $this->recallObject('kUnitConfigReader'); + return $unit_config_reader->setUnitOption($prefix,$option,$value); + } + + /** + * Read all unit with $prefix options + * + * @param string $prefix + * @return Array + * @access public + * @author Alex + */ + function getUnitOptions($prefix) + { + $unit_config_reader =& $this->recallObject('kUnitConfigReader'); + return $unit_config_reader->getUnitOptions($prefix); + } + + /** + * Splits any mixing of prefix and + * special into correct ones + * + * @param string $prefix_special + * @return Array + * @access public + * @author Alex + */ + function processPrefix($prefix_special) + { + return $this->Factory->processPrefix($prefix_special); + } + + /** + * Set's new event for $prefix_special + * passed + * + * @param string $prefix_special + * @param string $event_name + * @access public + */ + function setEvent($prefix_special,$event_name) + { + $event_manager =& $this->recallObject('EventManager'); + $event_manager->setEvent($prefix_special,$event_name); + } + + + /** + * SQL Error Handler + * + * @param int $code + * @param string $msg + * @param string $sql + * @return bool + * @access private + * @author Alex + */ + function handleSQLError($code,$msg,$sql) + { + global $debugger; + if($debugger) + { + $errorLevel=defined('DBG_SQL_FAILURE') && DBG_SQL_FAILURE ? E_USER_ERROR : E_USER_WARNING; + $debugger->dumpVars($_REQUEST); + $debugger->appendTrace(); + + $error_msg = ''.$msg.' ('.$code.')
SQL: '.$debugger->formatSQL($sql); + $long_id=$debugger->mapLongError($error_msg); + trigger_error( substr($msg.' ('.$code.') ['.$sql.']',0,1000).' #'.$long_id, $errorLevel); + return true; + } + else + { + $errorLevel = defined('IS_INSTALL') && IS_INSTALL ? E_USER_WARNING : E_USER_ERROR; + trigger_error('SQL Error in sql: '.$sql.', code '.$code.' ('.$msg.')', $errorLevel); + /*echo 'xProcessing SQL: '.$sql.'
'; + echo 'Error ('.$code.'): '.$msg.'
';*/ + return $errorLevel == E_USER_ERROR ? false : true; + } + } + + /** + * Default error handler + * + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + * @param Array $errcontext + */ + function handleError($errno, $errstr, $errfile = '', $errline = '', $errcontext = '') + { + $fp = fopen(DOC_ROOT.BASE_PATH.'/silent_log.txt','a'); + $time = date('d/m/Y H:i:s'); + fwrite($fp, '['.$time.'] #'.$errno.': '.strip_tags($errstr).' in ['.$errfile.'] on line '.$errline."\n"); + fclose($fp); + } + + /** + * Returns & blocks next ResourceId available in system + * + * @return int + * @access public + * @author Eduard + */ + function NextResourceId() + { + $this->DB->Query('LOCK TABLES '.TABLE_PREFIX.'IdGenerator WRITE'); + $this->DB->Query('UPDATE '.TABLE_PREFIX.'IdGenerator SET lastid = lastid+1'); + $id = $this->DB->GetOne("SELECT lastid FROM ".TABLE_PREFIX."IdGenerator"); + $this->DB->Query('UNLOCK TABLES'); + return $id; + } + + /** + * Returns main prefix for subtable prefix passes + * + * @param string $current_prefix + * @return string + * @access public + * @author Kostja + */ + function GetTopmostPrefix($current_prefix) + { + while ( $parent_prefix = $this->getUnitOption($current_prefix, 'ParentPrefix') ) + { + $current_prefix = $parent_prefix; + } + return $current_prefix; + } + + function EmailEventAdmin($email_event_name, $to_user_id = -1, $send_params = false) + { + return $this->EmailEvent($email_event_name, 1, $to_user_id, $send_params); + } + + function EmailEventUser($email_event_name, $to_user_id = -1, $send_params = false) + { + return $this->EmailEvent($email_event_name, 0, $to_user_id, $send_params); + } + + function EmailEvent($email_event_name, $email_event_type, $to_user_id = -1, $send_params = false) + { + $event = new kEvent('emailevents:OnEmailEvent'); + $event->setEventParam('EmailEventName', $email_event_name); + $event->setEventParam('EmailEventToUserId', $to_user_id); + $event->setEventParam('EmailEventType', $email_event_type); + if ($send_params){ + $event->setEventParam('DirectSendParams', $send_params); + } + $this->HandleEvent($event); + + return $event; + } + + + function LoggedIn() + { + $user =& $this->recallObject('u'); + return ($user->GetDBField('PortalUserId') > 0); + } + + function CheckPermission($name, $cat_id = null) + { + if(!$cat_id) + { + $cat_id = $this->GetVar('m_cat_id'); + } + $sql = 'SELECT ParentPath FROM '.$this->getUnitOption('c', 'TableName').' + WHERE CategoryId = '.$cat_id; + $cat_hierarchy = $this->DB->GetOne($sql); + $cat_hierarchy = explode('|', $cat_hierarchy); + array_shift($cat_hierarchy); + array_pop($cat_hierarchy); + $cat_hierarchy = array_reverse($cat_hierarchy); + + $groups = $this->RecallVar('UserGroups'); + + foreach($cat_hierarchy as $category_id) + { + $sql = 'SELECT PermissionValue FROM '.TABLE_PREFIX.'Permissions + WHERE Permission = "'.$name.'" + AND CatId = '.$category_id.' + AND GroupId IN ('.$groups.')'; + $res = $this->DB->GetOne($sql); + if( $res !== false ) + { + return $res; + } + } + + return 0; + } +} + +?> \ No newline at end of file Index: trunk/core/kernel/utility/temp_handler.php =================================================================== diff -u -N --- trunk/core/kernel/utility/temp_handler.php (revision 0) +++ trunk/core/kernel/utility/temp_handler.php (revision 1560) @@ -0,0 +1,581 @@ +Conn =& $this->Application->GetADODBConnection(); + } + + function SetTables($tables) + { + // set tablename as key for tables array + $ret = Array(); + $this->Tables = $tables; + $this->MasterTable = $tables['TableName']; + } + + /** + * Get temp table name + * + * @param string $table + * @return string + */ + function GetTempName($table) + { + // function is sometimes called as static, so we CAN'T use $this->GetTempTablePrefix() here + return TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_edit_'.$table; + } + + function GetTempTablePrefix() + { + return TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_edit_'; + } + + /** + * Return live table name based on temp table name + * + * @param string $temp_table + * @return string + */ + function GetLiveName($temp_table) + { + if( preg_match('/'.TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_edit_(.*)/',$temp_table,$rets) ) + { + return $rets[1]; + } + else + { + return $temp_table; + } + } + + function IsTempTable($table) + { + return strpos($table, TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_edit_') !== false; + } + + /** + * Return temporary table name for master table + * + * @return string + * @access public + */ + function GetMasterTempName() + { + return $this->GetTempName($this->MasterTable); + } + + function CreateTempTable($table) + { + $query = sprintf("CREATE TABLE %s SELECT * FROM %s WHERE 0", + $this->GetTempName($table), + $table); + $this->Conn->Query($query); + } + + function BuildTables($prefix, $ids) + { + $tables = Array( + 'TableName' => $this->Application->getUnitOption($prefix,'TableName'), + 'IdField' => $this->Application->getUnitOption($prefix,'IDField'), + 'IDs' => $ids, + 'Prefix' => $prefix, + ); + + $SubItems = $this->Application->getUnitOption($prefix,'SubItems'); + if (is_array($SubItems)) { + foreach ($SubItems as $prefix) { + $this->AddTables($prefix, $tables); + } + } + $this->SetTables($tables); + } + + function AddTables($prefix, &$tables) + { + $tmp = Array( + 'TableName' => $this->Application->getUnitOption($prefix,'TableName'), + 'IdField' => $this->Application->getUnitOption($prefix,'IDField'), + 'ForeignKey' => $this->Application->getUnitOption($prefix,'ForeignKey'), + 'ParentTableKey' => $this->Application->getUnitOption($prefix,'ParentTableKey'), + 'Prefix' => $prefix, + 'AutoClone' => $this->Application->getUnitOption($prefix,'AutoClone'), + 'AutoDelete' => $this->Application->getUnitOption($prefix,'AutoDelete'), + ); + + $constrain = $this->Application->getUnitOption($prefix,'Constrain'); + if ($constrain) $tmp['Constrain'] = $constrain; + + $SubItems = $this->Application->getUnitOption($prefix,'SubItems'); + $same_sub_counter = 1; + if (is_array($SubItems)) { + foreach ($SubItems as $prefix) { + if (preg_match("/^SAME:(.*)/", $prefix, $regs)) { + $same_sub = $tmp; + $same_sub['Prefix'] = $tmp['Prefix'].'.'.$same_sub_counter; + $same_sub['Constrain'] = $regs[1]; + $same_sub['ParentTableKey'] = $tmp['IdField']; + $same_sub['ForeignKey'] = $this->Application->getUnitOption($tmp['Prefix'],'ForeignKey'.$same_sub_counter); + $tmp['SubTables'][] = $same_sub; + $same_sub_counter++; + } + else { + $this->AddTables($prefix, $tmp); + } + } + } + + if ( !is_array(getArrayValue($tables, 'SubTables')) ) { + $tables['SubTables'] = array(); + } + + $tables['SubTables'][] = $tmp; + } + + function CloneItems($prefix, $special, $ids, $master=null, $foreign_key=null, $parent_prefix=null) + { + if (!isset($master)) $master = $this->Tables; + if( strpos($prefix,'.') !== false ) list($prefix,$special) = explode('.', $prefix, 2); + + $prefix_special = rtrim($prefix.'.'.$special, '.'); + + //recalling by different name, because we may get kDBList, if we recall just by prefix + $recall_prefix = $prefix_special.($special ? '' : '.').'-item'; + $this->Application->setUnitOption($prefix, 'AutoLoad', false); + + $object =& $this->Application->recallObject($recall_prefix, $prefix); + + foreach ($ids as $id) + { + $mode = 'create'; + if ( $cloned_ids = getArrayValue($this->AlreadyProcessed, $master['TableName']) ) { + // if we have already cloned the id, replace it with cloned id and set mode to update + // update mode is needed to update second ForeignKey for items cloned by first ForeignKey + if ( getArrayValue($cloned_ids, $id) ) { + $id = $cloned_ids[$id]; + $mode = 'update'; + } + } + + $object->Load($id); + $original_values = $object->FieldValues; + + $object->NameCopy($master, $foreign_key); + + if (isset($foreign_key)) { + $master_foreign_key_field = is_array($master['ForeignKey']) ? $master['ForeignKey'][$parent_prefix] : $master['ForeignKey']; + $object->SetDBField($master_foreign_key_field, $foreign_key); + } + + if ($mode == 'create') { + $this->RaiseEvent('OnBeforeClone', $master['Prefix'], Array($object->GetId()) ); + } + + $res = $mode == 'update' ? $object->Update() : $object->Create(); + + if( $res ) + { + if ( $mode == 'create' && is_array( getArrayValue($master, 'ForeignKey')) ) { + // remember original => clone mapping for dual ForeignKey updating + $this->AlreadyProcessed[$master['TableName']][$id] = $object->GetId(); + } + if($object->mode == 't') $object->setTempID(); + if ($mode == 'create') { + $this->RaiseEvent('OnAfterClone', $master['Prefix'], Array($object->GetId()) ); + } + + if ( is_array(getArrayValue($master, 'SubTables')) ) { + foreach($master['SubTables'] as $sub_table) { + if (!getArrayValue($sub_table, 'AutoClone')) continue; + $sub_TableName = ($object->mode == 't') ? $this->GetTempName($sub_table['TableName']) : $sub_table['TableName']; + + $foreign_key_field = is_array($sub_table['ForeignKey']) ? $sub_table['ForeignKey'][$master['Prefix']] : $sub_table['ForeignKey']; + $parent_key_field = is_array($sub_table['ParentTableKey']) ? $sub_table['ParentTableKey'][$master['Prefix']] : $sub_table['ParentTableKey']; + + $query = 'SELECT '.$sub_table['IdField'].' FROM '.$sub_TableName.' + WHERE '.$foreign_key_field.' = '.$original_values[$parent_key_field]; + + $sub_ids = $this->Conn->GetCol($query); + + if ( is_array(getArrayValue($sub_table, 'ForeignKey')) ) { + // $sub_ids could containt newly cloned items, we need to remove it here + // to escape double cloning + + $cloned_ids = getArrayValue($this->AlreadyProcessed, $sub_table['TableName']); + if ( !$cloned_ids ) $cloned_ids = Array(); + $new_ids = array_values($cloned_ids); + $sub_ids = array_diff($sub_ids, $new_ids); + } + + $parent_key = $object->GetDBField($parent_key_field); + + $this->CloneItems($sub_table['Prefix'], '', $sub_ids, $sub_table, $parent_key, $master['Prefix']); + } + } + } + } + } + + function DeleteItems($prefix, $special, $ids, $master=null, $foreign_key=null) + { + if (!isset($master)) $master = $this->Tables; + if( strpos($prefix,'.') !== false ) list($prefix,$special) = explode('.', $prefix, 2); + + $prefix_special = rtrim($prefix.'.'.$special, '.'); + + //recalling by different name, because we may get kDBList, if we recall just by prefix + $recall_prefix = $prefix_special.($special ? '' : '.').'-item'; + $this->Application->setUnitOption($prefix,'AutoLoad',false); + $object =& $this->Application->recallObject($recall_prefix, $prefix); + + foreach ($ids as $id) + { + $object->Load($id); + $original_values = $object->FieldValues; + $object->Delete($id); + + if ( is_array(getArrayValue($master, 'SubTables')) ) { + foreach($master['SubTables'] as $sub_table) { + if (!getArrayValue($sub_table, 'AutoDelete')) continue; + $sub_TableName = ($object->mode == 't') ? $this->GetTempName($sub_table['TableName']) : $sub_table['TableName']; + + $foreign_key_field = is_array($sub_table['ForeignKey']) ? $sub_table['ForeignKey'][$master['Prefix']] : $sub_table['ForeignKey']; + $parent_key_field = is_array($sub_table['ParentTableKey']) ? $sub_table['ParentTableKey'][$master['Prefix']] : $sub_table['ParentTableKey']; + + $query = 'SELECT '.$sub_table['IdField'].' FROM '.$sub_TableName.' + WHERE '.$foreign_key_field.' = '.$original_values[$parent_key_field]; + + $sub_ids = $this->Conn->GetCol($query); + + $parent_key = $object->GetDBField($sub_table['ParentTableKey']); + + $this->DeleteItems($sub_table['Prefix'], '', $sub_ids, $sub_table, $parent_key); + } + } + + } + } + + function DoCopyLiveToTemp($master, $ids, $parent_prefix=null) + { + // when two tables refers the same table as sub-sub-table, and ForeignKey and ParentTableKey are arrays + // the table will be first copied by first sub-table, then dropped and copied over by last ForeignKey in the array + // this should not do any problems :) + if ( !preg_match("/.*\.[0-9]+/", $master['Prefix']) ) { + $this->DropTempTable($master['TableName']); + $this->CreateTempTable($master['TableName']); + } + + if (is_array($ids)) { + $ids = join(',', $ids); + } + + if ($ids != '') { + if ( getArrayValue($master, 'ForeignKey') ) { + if ( is_array($master['ForeignKey']) ) { + $key_field = $master['ForeignKey'][$parent_prefix]; + } + else { + $key_field = $master['ForeignKey']; + } + } + else { + $key_field = $master['IdField']; + } + + $query = 'INSERT INTO '.$this->GetTempName($master['TableName']).' + SELECT * FROM '.$master['TableName'].' + WHERE '.$key_field.' IN ('.$ids.')'; + if (isset($master['Constrain'])) $query .= ' AND '.$master['Constrain']; + $this->Conn->Query($query); + + $query = 'SELECT '.$master['IdField'].' FROM '.$master['TableName'].' + WHERE '.$key_field.' IN ('.$ids.')'; + if (isset($master['Constrain'])) $query .= ' AND '.$master['Constrain']; + $this->RaiseEvent( 'OnAfterCopyToTemp', $master['Prefix'], $this->Conn->GetCol($query) ); + } + + if ( getArrayValue($master, 'SubTables') ) { + foreach ($master['SubTables'] as $sub_table) { + + $parent_key = is_array($sub_table['ParentTableKey']) ? $sub_table['ParentTableKey'][$master['Prefix']] : $sub_table['ParentTableKey']; + + if ( $ids != '' && $parent_key != $key_field ) { + $query = 'SELECT '.$parent_key.' FROM '.$master['TableName'].' + WHERE '.$key_field.' IN ('.$ids.')'; + $sub_foreign_keys = join(',', $this->Conn->GetCol($query)); + } + else { + $sub_foreign_keys = $ids; + } + $this->DoCopyLiveToTemp($sub_table, $sub_foreign_keys, $master['Prefix']); + } + } + } + + function GetForeignKeys($master, $sub_table, $live_id, $temp_id=null) + { + $mode = 1; //multi + if (!is_array($live_id)) { + $live_id = Array($live_id); + $mode = 2; //single + } + if (isset($temp_id) && !is_array($temp_id)) $temp_id = Array($temp_id); + + if ( isset($sub_table['ParentTableKey']) ) { + if ( is_array($sub_table['ParentTableKey']) ) { + $parent_key_field = $sub_table['ParentTableKey'][$master['Prefix']]; + } + else { + $parent_key_field = $sub_table['ParentTableKey']; + } + } + else { + $parent_key_field = $master['IdField']; + } + + if ( $cached = getArrayValue($this->FKeysCache, $master['TableName'].'.'.$parent_key_field) ) { + if ( array_key_exists(serialize($live_id), $cached) ) { + list($live_foreign_key, $temp_foreign_key) = $cached[serialize($live_id)]; + if ($mode == 1) { + return $live_foreign_key; + } + else { + return Array($live_foreign_key[0], $temp_foreign_key[0]); + } + } + } + + if ($parent_key_field != $master['IdField']) { + $query = 'SELECT '.$parent_key_field.' FROM '.$master['TableName'].' + WHERE '.$master['IdField'].' IN ('.join(',', $live_id).')'; + $live_foreign_key = $this->Conn->GetCol($query); + + if (isset($temp_id)) { + $query = 'SELECT '.$parent_key_field.' FROM '.$this->GetTempName($master['TableName']).' + WHERE '.$master['IdField'].' IN ('.join(',', $temp_id).')'; + $temp_foreign_key = $this->Conn->GetCol($query); + } + else { + $temp_foreign_key = Array(); + } + } + else { + $live_foreign_key = $live_id; + $temp_foreign_key = $temp_id; + } + + $this->FKeysCache[$master['TableName'].'.'.$parent_key_field][serialize($live_id)] = Array($live_foreign_key, $temp_foreign_key); + + if ($mode == 1) { + return $live_foreign_key; + } + else { + return Array($live_foreign_key[0], $temp_foreign_key[0]); + } + } + + function DoCopyTempToOriginal($master, $parent_prefix=null) + { + $query = 'SELECT '.$master['IdField'].' FROM '.$this->GetTempName($master['TableName']); + if (isset($master['Constrain'])) $query .= ' WHERE '.$master['Constrain']; + $current_ids = $this->Conn->GetCol($query); + + if ($current_ids) { + // delete all ids from live table - for MasterTable ONLY! + // because items from Sub Tables get deteleted in CopySubTablesToLive !BY ForeignKey! + if ($master['TableName'] == $this->MasterTable) { + $this->RaiseEvent( 'OnBeforeDeleteFromLive', $master['Prefix'], $current_ids ); + + $query = 'DELETE FROM '.$master['TableName'].' WHERE '.$master['IdField'].' IN ('.join(',', $current_ids).')'; + $this->Conn->Query($query); + } + + if ( getArrayValue($master, 'SubTables') ) { + foreach ($current_ids AS $id) { + $this->RaiseEvent( 'OnBeforeCopyToLive', $master['Prefix'], Array($id) ); + + //reset negative ids to 0, so autoincrement in live table works fine + if ($id < 0) { + $query = 'UPDATE '.$this->GetTempName($master['TableName']).' + SET '.$master['IdField'].' = 0 + WHERE '.$master['IdField'].' = '.$id; + $this->Conn->Query($query); + $id_to_copy = 0; + } + else { + $id_to_copy = $id; + } + + //copy current id_to_copy (0 for new or real id) to live table + $query = 'INSERT INTO '.$master['TableName'].' + SELECT * FROM '.$this->GetTempName($master['TableName']).' + WHERE '.$master['IdField'].' = '.$id_to_copy; + $this->Conn->Query($query); + $insert_id = $id_to_copy == 0 ? $this->Conn->getInsertID() : $id_to_copy; + + $this->RaiseEvent( 'OnAfterCopyToLive', $master['Prefix'], Array($insert_id) ); + + $this->UpdateForeignKeys($master, $insert_id, $id); + + //delete already copied record from master temp table + $query = 'DELETE FROM '.$this->GetTempName($master['TableName']).' + WHERE '.$master['IdField'].' = '.$id_to_copy; + $this->Conn->Query($query); + } + // when all of ids in current master has been processed, copy all sub-tables data + $this->CopySubTablesToLive($master, $current_ids); + } + else { //If current master doesn't have sub-tables - we could use mass operations + // We don't need to delete items from live here, as it get deleted in the beggining of the method for MasterTable + // or in parent table processing for sub-tables + + $this->RaiseEvent('OnBeforeCopyToLive', $master['Prefix'], $current_ids); + + // reset ALL negative IDs to 0 so it get inserted into live table with autoincrement + $query = 'UPDATE '.$this->GetTempName($master['TableName']).' + SET '.$master['IdField'].' = 0 + WHERE '.$master['IdField'].' < 0'; + if (isset($master['Constrain'])) $query .= ' AND '.$master['Constrain']; + $this->Conn->Query($query); + + // copy ALL records to live table + $query = 'INSERT INTO '.$master['TableName'].' + SELECT * FROM '.$this->GetTempName($master['TableName']); + if (isset($master['Constrain'])) $query .= ' WHERE '.$master['Constrain']; + $this->Conn->Query($query); + + /* + + !!! WE NEED TO FIND A WAY TO DETERMINE IF OnAfterCopyToLive is not an empty method, and do on-by-one insert + and pass Ids to OnAfterCopyToLive, otherwise it's not smart to do on-by-one insert for any object + OR WE COULD FIND A WAY TO GET ALL INSERTED IDS as an array and iterate them !!! + + $this->RaiseEvent('OnAfterCopyToLive', IDS ??? ); + + */ + + // no need to clear temp table - it will be dropped by next statement + } + } + if ( is_array(getArrayValue($master, 'ForeignKey')) ) { //if multiple ForeignKeys + if ( $master['ForeignKey'][$parent_prefix] != end($master['ForeignKey']) ) { + return; // Do not delete temp table if not all ForeignKeys have been processed (current is not the last) + } + } + $this->DropTempTable($master['TableName']); + } + + function UpdateForeignKeys($master, $live_id, $temp_id) { + foreach ($master['SubTables'] as $sub_table) { + list ($live_foreign_key, $temp_foreign_key) = $this->GetForeignKeys($master, $sub_table, $live_id, $temp_id); + + $foreign_key_field = is_array($sub_table['ForeignKey']) ? $sub_table['ForeignKey'][$master['Prefix']] : $sub_table['ForeignKey']; + + //Update ForeignKey in sub TEMP table + if ($live_foreign_key != $temp_foreign_key) { + $query = 'UPDATE '.$this->GetTempName($sub_table['TableName']).' + SET '.$foreign_key_field.' = '.$live_foreign_key.' + WHERE '.$foreign_key_field.' = '.$temp_foreign_key; + $this->Conn->Query($query); + } + } + } + + function CopySubTablesToLive($master, $current_ids) { + foreach ($master['SubTables'] as $sub_table) { + + // delete records from live table by foreign key, so that records deleted from temp table + // get deleted from live + if (count($current_ids) > 0) { + $foreign_keys = $this->GetForeignKeys($master, $sub_table, $current_ids); + $foreign_key_field = is_array($sub_table['ForeignKey']) ? $sub_table['ForeignKey'][$master['Prefix']] : $sub_table['ForeignKey']; + if (count($foreign_keys) > 0) { + $query = 'SELECT '.$sub_table['IdField'].' FROM '.$sub_table['TableName'].' + WHERE '.$foreign_key_field.' IN ('.join(',', $foreign_keys).')'; + if (isset($sub_table['Constrain'])) $query .= ' AND '.$sub_table['Constrain']; + + $this->RaiseEvent( 'OnBeforeDeleteFromLive', $sub_table['Prefix'], $this->Conn->GetCol($query) ); + + $query = 'DELETE FROM '.$sub_table['TableName'].' + WHERE '.$foreign_key_field.' IN ('.join(',', $foreign_keys).')'; + if (isset($sub_table['Constrain'])) $query .= ' AND '.$sub_table['Constrain']; + $this->Conn->Query($query); + } + } + + //sub_table passed here becomes master in the method, and recursively updated and copy its sub tables + $this->DoCopyTempToOriginal($sub_table, $master['Prefix']); + } + } + + function RaiseEvent($name, $prefix, $ids) + { + if ( !is_array($ids) ) return; + foreach ($ids as $id) { + $event = new kEvent( Array('name'=>$name, 'prefix'=>$prefix, 'special'=>'') ); + $event->setEventParam('id', $id); + $this->Application->HandleEvent($event); + } + } + + function DropTempTable($table) + { + $query = sprintf("DROP TABLE IF EXISTS %s", + $this->GetTempName($table) + ); + $this->Conn->Query($query); + } + + function PrepareEdit() + { + $this->DoCopyLiveToTemp($this->Tables, $this->Tables['IDs']); + } + + function SaveEdit($skip_master=0) + { + $this->DoCopyTempToOriginal($this->Tables); + } + + function CancelEdit($master=null) + { + if (!isset($master)) $master = $this->Tables; + $this->DropTempTable($master['TableName']); + if ( getArrayValue($master, 'SubTables') ) { + foreach ($master['SubTables'] as $sub_table) { + $this->CancelEdit($sub_table); + } + } + } +} + +?> \ No newline at end of file Index: trunk/core/kernel/utility/debugger.php =================================================================== diff -u -N --- trunk/core/kernel/utility/debugger.php (revision 0) +++ trunk/core/kernel/utility/debugger.php (revision 1560) @@ -0,0 +1,922 @@ +$rq_value) + { + if( substr($rq_name,0,6)=='debug_' ) + { + define('DBG_ZEND_PRESENT',1); + break; + } + } + + dbg_safeDefine('DBG_ZEND_PRESENT',0); + + // set default values for debugger constants + $dbg_constMap=Array('DBG_OPTIONS'=>0, + 'DBG_USE_HIGHLIGHT'=>1, + 'DBG_USE_SHUTDOWN_FUNC'=>DBG_ZEND_PRESENT?0:1, + 'DBG_HANDLE_ERRORS'=>DBG_ZEND_PRESENT?0:1, + 'DBG_SHOW_MEMORY_USAGE'=>1, + 'DBG_IGNORE_STRICT_ERRORS'=>1, + 'DOC_ROOT'=>$_SERVER['DOCUMENT_ROOT'], + 'DBG_LOCAL_BASE_PATH'=>'w:'); + + foreach($dbg_constMap as $dbg_constName=>$dbg_constValue) + { + dbg_safeDefine($dbg_constName,$dbg_constValue); + } + + // only for IE, in case if no windows php script editor defined + /*if(!defined('WINDOWS_EDITOR')) + { + $dbg_editor = 0; + $dbg_editors[0] = Array('editor' => 'c:\Program Files\UltraEdit\uedit32.exe', 'params' => '%F/%L'); + $dbg_editors[1] = Array('editor' => 'c:\Program Files\Zend\ZendStudio-4.0Beta\bin\ZDE.exe', 'params' => '%F'); + define('WINDOWS_EDITOR',$dbg_editors[$dbg_editor]['editor'].' '.$dbg_editors[$dbg_editor]['params']); + unset($dbg_editors,$dbg_editor); + }*/ + + class Debugger + { + /** + * Debugger data for building report + * + * @var Array + */ + var $Data = Array(); + var $ProfilerData = Array(); + var $ProfilerTotals = Array(); + var $RecursionStack = Array(); // prevent recursion when processing debug_backtrace() function results + + var $TraceNextError=false; + + var $Options = 0; + var $OptionsMap = Array('shutdown_func' => 1, 'error_handler' => 2, + 'output_buffer' => 4, 'highlight_output' => 8); + + + var $longErrors=Array(); + + /** + * Amount of memory used by debugger itself + * + * @var Array + * @access private + */ + var $memoryUsage=Array(); + + var $IncludesData=Array(); + var $IncludeLevel=0; + + function Debugger() + { + $this->profileStart('kernel4_startup', 'Startup and Initialization of kernel4'); + $this->profileStart('script_runtime', 'Script runtime'); + ini_set('display_errors',dbg_ConstOn('DBG_ZEND_PRESENT')?0:1); + $this->memoryUsage['error_handling']=0; // memory amount used by error handler + $this->appendRequest(); + } + + function initOptions() + { + + } + + function mapLongError($msg) + { + $key=$this->generateID(); + $this->longErrors[$key]=$msg; + return $key; + } + + function setOption($name,$value) + { + if( !isset($this->OptionsMap[$name]) ) die('undefined debugger option: ['.$name.']
'); + if($value) + { + $this->Options|=$this->OptionsMap[$name]; + } + else + { + $this->Options=$this->Options&~$this->OptionsMap[$name]; + } + } + + function getOption($name) + { + if( !isset($this->OptionsMap[$name]) ) die('undefined debugger option: ['.$name.']
'); + return ($this->Options & $this->OptionsMap[$name]) == $this->OptionsMap[$name]; + } + + /** + * Set's flag, that next error that occurs will + * be prepended by backtrace results + * + */ + function traceNext() + { + $this->TraceNextError=true; + } + + function dumpVars() + { + $dumpVars = func_get_args(); + foreach($dumpVars as $varValue) + { + $this->Data[] = Array('value' => $varValue, 'debug_type' => 'var_dump'); + } + } + + function prepareHTML($dataIndex) + { + $Data =& $this->Data[$dataIndex]; + if($Data['debug_type'] == 'html') return $Data['html']; + + switch($Data['debug_type']) + { + case 'error': + $fileLink = $this->getFileLink($Data['file'],$Data['line']); + $ret = ''.$this->getErrorNameByCode($Data['no']).': '.$Data['str']; + $ret .= ' in '.$fileLink.' on line '.$Data['line'].''; + return $ret; + break; + + case 'var_dump': + return $this->highlightString( print_r($Data['value'], true) ); + break; + + case 'trace': + ini_set('memory_limit','500M'); + $trace =& $Data['trace']; + + //return 'sorry'; + //return $this->highlightString(print_r($trace,true)); + + + $i = 0; $traceCount = count($trace); + $ret = ''; + while($i < $traceCount) + { + $traceRec =& $trace[$i]; + $argsID = 'trace_args_'.$dataIndex.'_'.$i; + if(isset($traceRec['file'])) + { + $func_name=isset($traceRec['class'])?$traceRec['class'].$traceRec['type'].$traceRec['function']:$traceRec['function']; + $ret .= 'Function: '.$this->getFileLink($traceRec['file'],$traceRec['line'],$func_name); + $ret .= ' in '.basename($traceRec['file']).' on line '.$traceRec['line'].'
'; + } + else + { + $ret .= 'no file information available'; + } + + // ensure parameter value is not longer then 200 symbols + $this->processTraceArguments($traceRec['args']); + $args = $this->highlightString(print_r($traceRec['args'], true)); + $ret .= ''; + $i++; + } + return $ret; + break; + + case 'profiler': + $profileKey = $Data['profile_key']; + $Data =& $this->ProfilerData[$profileKey]; + $runtime = ($Data['ends'] - $Data['begins']); // in seconds + return 'Name: '.$Data['description'].'
Runtime: '.$runtime.'s'; + break; + + default: + return 'incorrect debug data'; + break; + } + } + + function processTraceArguments(&$traceArgs) + { + if(!$traceArgs) return ''; + foreach ($traceArgs as $argID => $argValue) + { + if( is_array($argValue) || is_object($argValue) ) + { + if(is_object($argValue) && !in_array(get_class($argValue),$this->RecursionStack) ) + { + // object & not in stack - ok + array_push($this->RecursionStack, get_class($argValue)); + settype($argValue,'array'); + $this->processTraceArguments($argValue); + array_pop($this->RecursionStack); + } + elseif(is_object($argValue) && in_array(get_class($argValue),$this->RecursionStack) ) + { + // object & in stack - recursion + $traceArgs[$argID] = '**** RECURSION ***'; + } + else + { + // normal array here + $this->processTraceArguments($argValue); + } + } + else + { + $traceArgs[$argID] = $this->cutStringForHTML($traceArgs[$argID]); + } + } + } + + function cutStringForHTML($string) + { + if( strlen($string) > 200 ) $string = substr($string,0,50).' ...'; + return $string; + } + + /** + * Format SQL Query using predefined formatting + * and highlighting techniques + * + * @param string $sql + * @return string + */ + function formatSQL($sql) + { + $sql = preg_replace('/(\n|\t| )+/is',' ',$sql); + $sql = preg_replace('/(CREATE TABLE|DROP TABLE|SELECT|UPDATE|SET|REPLACE|INSERT|DELETE|VALUES|FROM|LEFT JOIN|INNER JOIN|LIMIT|WHERE|HAVING|GROUP BY|ORDER BY) /is', "\n\t$1 ",$sql); + return $this->highlightString($sql); + } + + function highlightString($string) + { + if( dbg_ConstOn('DBG_USE_HIGHLIGHT') ) + { + $string = str_replace('\\','_no_match_string_',$string); + $string = highlight_string('', true); + $string = str_replace('_no_match_string_','\\',$string); + return preg_replace('/<\?(.*)php (.*)\?>/s','$2',$string); + } + else + { + return $string; + } + } + + function getFileLink($file, $lineno = 1, $title = '') + { + if(!$title) $title = $file; + $is_mozilla=strpos(strtolower($_SERVER['HTTP_USER_AGENT']),'firefox')!==false?true:false; + if($is_mozilla) + { + return ''.$title.''; + } + else + { + return ''.$title.''; + } + + } + + function getLocalFile($remoteFile) + { + return str_replace(DOC_ROOT, DBG_LOCAL_BASE_PATH, $remoteFile); + } + + function appendTrace() + { + $trace = debug_backtrace(); + array_shift($trace); + $this->Data[] = Array('trace' => $trace, 'debug_type' => 'trace'); + } + + function appendMemoryUsage($msg, $used=null) + { + if (!isset($used)) $used = round(memory_get_usage()/1024); + $this->appendHTML('Memory usage '.$msg.' '.$used.'Kb'); + } + + function appendHTML($html) + { + $this->Data[] = Array('html' => $html,'debug_type' => 'html'); + } + + /** + * Change debugger info that was already generated before. + * Returns true if html was set. + * + * @param int $index + * @param string $html + * @param string $type = {'append','prepend','replace'} + * @return bool + */ + function setHTMLByIndex($index,$html,$type='append') + { + if( !isset($this->Data[$index]) || $this->Data[$index]['debug_type'] != 'html' ) + { + return false; + } + + switch ($type) + { + case 'append': + $this->Data[$index]['html'] .= '
'.$html; + break; + case 'prepend': + $this->Data[$index]['html'] = $this->Data[$index]['html'].'
'.$html; + break; + case 'replace': + $this->Data[$index]['html'] = $html; + break; + } + return true; + } + + /** + * Move $debugLineCount lines of input from debug output + * end to beginning. + * + * @param int $debugLineCount + */ + function moveToBegin($debugLineCount) + { + $lines = array_splice($this->Data,count($this->Data)-$debugLineCount,$debugLineCount); + $this->Data = array_merge($lines,$this->Data); + } + + function moveAfterRow($new_row, $debugLineCount) + { + $lines = array_splice($this->Data,count($this->Data)-$debugLineCount,$debugLineCount); + $rows_before = array_splice($this->Data,0,$new_row,$lines); + $this->Data = array_merge($rows_before,$this->Data); + } + + function appendRequest() + { + $script = $_SERVER['PATH_TRANSLATED']; + $this->appendHTML('ScriptName: '.$this->getFileLink($script,1,basename($script)).' ('.dirname($script).')'); + ob_start(); + ?> + + + + + $value) + { + if( !is_array($value) && trim($value) == '' ) + { + $value = 'no value'; + } + else + { + $value = htmlspecialchars(print_r($value, true)); + } + $src = isset($_GET[$key]) ? 'GE' : (isset($_POST[$key]) ? 'PO' : (isset($_COOKIE[$key]) ? 'CO' : '?') ); + echo ''; + } + ?> +
SrcNameValue
'.$src.''.$key.''.$value.'
+ appendHTML( ob_get_contents() ); + ob_end_clean(); + } + + function appendSession() + { + if( isset($_SESSION)&&$_SESSION ) + { + $this->appendHTML('PHP Session: ['.ini_get('session.name').']'); + $this->dumpVars($_SESSION); + $this->moveToBegin(2); + } + } + + function profileStart($key, $description) + { + $timeStamp = $this->getMoment(); + $this->ProfilerData[$key] = Array('begins' => $timeStamp, 'ends' => 5000, 'debuggerRowID' => count($this->Data), 'description' => $description); + $this->Data[] = array('profile_key' => $key, 'debug_type' => 'profiler'); + } + + function profileFinish($key) + { + $this->ProfilerData[$key]['ends'] = $this->getMoment(); + } + + function profilerAddTotal($total_key, $key=null, $value=null) + { + if (!isset($this->ProfilerTotals[$total_key])) { + $this->ProfilerTotals[$total_key] = 0; + } + if (!isset($value)) { + $value = $this->ProfilerData[$key]['ends'] - $this->ProfilerData[$key]['begins']; + } + $this->ProfilerTotals[$total_key] += $value; + } + + function getMoment() + { + list($usec, $sec) = explode(' ', microtime()); + return ((float)$usec + (float)$sec); + } + + function generateID() + { + list($usec, $sec) = explode(" ",microtime()); + + $id_part_1 = substr($usec, 4, 4); + $id_part_2 = mt_rand(1,9); + $id_part_3 = substr($sec, 6, 4); + $digit_one = substr($id_part_1, 0, 1); + if ($digit_one == 0) { + $digit_one = mt_rand(1,9); + $id_part_1 = ereg_replace("^0","",$id_part_1); + $id_part_1=$digit_one.$id_part_1; + } + return $id_part_1.$id_part_2.$id_part_3; + } + + + function getErrorNameByCode($errorCode) + { + switch($errorCode) + { + case E_USER_ERROR: + return 'Fatal Error'; + break; + + case E_WARNING: + case E_USER_WARNING: + return 'Warning'; + break; + + case E_NOTICE: + case E_USER_NOTICE: + return 'Notice'; + break; + + case E_STRICT: + return 'PHP5 Strict'; + break; + + default: + return ''; + break; + } + } + + /** + * Generates report + * + */ + function printReport($returnResult = false) + { + $this->profileFinish('script_runtime'); + if( dbg_ConstOn('DBG_ZEND_PRESENT') ) return; + dbg_safeDefine('DBG_RAISE_ON_WARNINGS',0); + dbg_safeDefine('DBG_WINDOW_WIDTH', 700); + + $this->memoryUsage['debugger_start']=memory_get_usage(); + + // show php session if any + $this->appendSession(); + + // ensure, that 1st line of debug output always is this one: + $this->appendHTML('Hide Debugger'); + $this->moveToBegin(1); + + if ( dbg_ConstOn('DBG_SQL_PROFILE') ) { + $this->appendHTML('SQL Total time: '.$this->ProfilerTotals['sql']); + } + + if ( dbg_ConstOn('DBG_PROFILE_MEMORY') ) { + $this->appendHTML('Memory used by Objects: '.round($this->ProfilerTotals['objects']/1024, 2).'Kb'); + } + + if ( dbg_ConstOn('DBG_INCLUDED_FILES') ) { + $files = get_included_files(); + $this->appendHTML('Included files:'); + foreach ($files as $file) + { + $this->appendHTML($this->getFileLink($this->getLocalFile($file)).' ('.round(filesize($file)/1024, 2).'Kb)'); + } + } + + if ( dbg_ConstOn('DBG_PROFILE_INCLUDES') ) { + $this->appendHTML('Included files statistics:'.( dbg_ConstOn('DBG_SORT_INCLUDES_MEM') ? ' (sorted by memory usage)':'')); + $totals = Array( 'mem' => 0, 'time' => 0); + $totals_configs = Array( 'mem' => 0, 'time' => 0); + if ( dbg_ConstOn('DBG_SORT_INCLUDES_MEM') ) { + array_multisort($this->IncludesData['mem'], SORT_DESC, $this->IncludesData['file'], $this->IncludesData['time'], $this->IncludesData['level']); + } + foreach ($this->IncludesData['file'] as $key => $file_name) { + $this->appendHTML( str_repeat(' -> ', ($this->IncludesData['level'][$key] >= 0 ? $this->IncludesData['level'][$key] : 0)).$file_name.' Mem: '.sprintf("%.4f Kb", $this->IncludesData['mem'][$key]/1024).' Time: '.sprintf("%.4f", $this->IncludesData['time'][$key])); + if ($this->IncludesData['level'][$key] == 0) { + $totals['mem'] += $this->IncludesData['mem'][$key]; + $totals['time'] += $this->IncludesData['time'][$key]; + } + else if ($this->IncludesData['level'][$key] == -1) { + $totals_configs['mem'] += $this->IncludesData['mem'][$key]; + $totals_configs['time'] += $this->IncludesData['time'][$key]; + } + } + $this->appendHTML('Sub-Total classes: '.' Mem: '.sprintf("%.4f Kb", $totals['mem']/1024).' Time: '.sprintf("%.4f", $totals['time'])); + $this->appendHTML('Sub-Total configs: '.' Mem: '.sprintf("%.4f Kb", $totals_configs['mem']/1024).' Time: '.sprintf("%.4f", $totals_configs['time'])); + $this->appendHTML('Grand Total: '.' Mem: '.sprintf("%.4f Kb", ($totals['mem']+$totals_configs['mem'])/1024).' Time: '.sprintf("%.4f", $totals['time']+$totals_configs['time'])); + + + } + + + $i = 0; $lineCount = count($this->Data); + ob_start(); + ?> + + + + memoryUsage['debugger_finish']=memory_get_usage(); + $this->memoryUsage['print_report']=$this->memoryUsage['debugger_finish']-$this->memoryUsage['debugger_start']; + $this->memoryUsage['total']=$this->memoryUsage['print_report']+$this->memoryUsage['error_handling']; + $this->memoryUsage['application']=memory_get_usage()-$this->memoryUsage['total']; + if($returnResult) + { + $ret = ob_get_contents(); + ob_clean(); + if( dbg_ConstOn('DBG_SHOW_MEMORY_USAGE') ) $ret.=$this->getMemoryUsageReport(); + return $ret; + } + else + { + ob_end_flush(); + if( dbg_ConstOn('DBG_SHOW_MEMORY_USAGE') ) echo $this->getMemoryUsageReport(); + } + } + + /** + * Format's memory usage report by debugger + * + * @return string + * @access private + */ + function getMemoryUsageReport() + { + $info=Array('printReport'=>'print_report', + 'saveError'=>'error_handling', + 'Total'=>'total', + 'Application'=>'application'); + $ret=Array(); + foreach($info as $title => $value_key) + { + $ret[]=''.$title.':'.$this->formatSize($this->memoryUsage[$value_key]).''; + } + return ''.implode('',$ret).'
'; + } + + + /** + * User-defined error handler + * + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + * @param array $errcontext + */ + function saveError($errno, $errstr, $errfile = '', $errline = '', $errcontext = '') + { + $memory_used=Array(); + $memory_used['begin']=memory_get_usage(); + + $errorType = $this->getErrorNameByCode($errno); + if(!$errorType) + { + trigger_error('Unknown error type ['.$errno.']', E_USER_ERROR); + return false; + } + + if( dbg_ConstOn('DBG_IGNORE_STRICT_ERRORS') && defined('E_STRICT') && ($errno == E_STRICT) ) return; + + $long_id_pos=strrpos($errstr,'#'); + if($long_id_pos!==false) + { + // replace short message with long one (due triger_error limitations on message size) + $long_id=substr($errstr,$long_id_pos+1,strlen($errstr)); + $errstr=$this->longErrors[$long_id]; + unset($this->longErrors[$long_id]); + } + + + /*in /www/kostja/in-commerce4/kernel/kernel4/parser/construct_tags.php(177) : runtime-created function on line + + [PRE-PARSED block, $line 13]: Undefined variable: IdField*/ + + if( strpos($errfile,'runtime-created') !== false ) { + $errfile = ' PRE-PARSED block '.$this->CurrentPreParsedBlock.' '; + } + + if( strpos($errfile,'eval()\'d code') !== false ) + { + $errstr = '[EVAL, line '.$errline.']: '.$errstr; + $tmpStr = $errfile; + $pos = strpos($tmpStr,'('); + $errfile = substr($tmpStr,0,$pos); + $pos++; + $errline = substr($tmpStr,$pos,strpos($tmpStr,')',$pos)-$pos); + } + // if($this->TraceNextError || $errno == E_USER_ERROR) + if($this->TraceNextError) + { + $this->appendTrace(); + $this->TraceNextError=false; + } + $this->Data[] = Array('no' => $errno, 'str' => $errstr, 'file' => $errfile, 'line' => $errline, 'context' => $errcontext, 'debug_type' => 'error'); + $memory_used['end']=memory_get_usage(); + $this->memoryUsage['error_handling']+=$memory_used['end']-$memory_used['begin']; + if( substr($errorType,0,5) == 'Fatal') + { + echo ''; + exit; + } + } + + function saveToFile($msg) + { + $fp = fopen($_SERVER['DOCUMENT_ROOT'].'/vb_debug.txt', 'a'); + fwrite($fp,$msg."\n"); + fclose($fp); + } + + /** + * Formats file/memory size in nice way + * + * @param int $bytes + * @return string + * @access public + */ + function formatSize($bytes) + { + if ($bytes >= 1099511627776) { + $return = round($bytes / 1024 / 1024 / 1024 / 1024, 2); + $suffix = "TB"; + } elseif ($bytes >= 1073741824) { + $return = round($bytes / 1024 / 1024 / 1024, 2); + $suffix = "GB"; + } elseif ($bytes >= 1048576) { + $return = round($bytes / 1024 / 1024, 2); + $suffix = "MB"; + } elseif ($bytes >= 1024) { + $return = round($bytes / 1024, 2); + $suffix = "KB"; + } else { + $return = $bytes; + $suffix = "Byte"; + } + $return .= ' '.$suffix; + return $return; + } + + } + + if( !function_exists('memory_get_usage') ) + { + function memory_get_usage(){ return -1; } + } + + $debugger = new Debugger(); + if(dbg_ConstOn('DBG_HANDLE_ERRORS')) set_error_handler( array(&$debugger,'saveError') ); + if(dbg_ConstOn('DBG_USE_SHUTDOWN_FUNC')) { + register_shutdown_function( array(&$debugger,'printReport') ); + } + } +?> \ No newline at end of file Index: trunk/core/kernel/utility/configuration.php =================================================================== diff -u -N --- trunk/core/kernel/utility/configuration.php (revision 0) +++ trunk/core/kernel/utility/configuration.php (revision 1560) @@ -0,0 +1,87 @@ +Application =& KernelApplication::Instance(); + $this->id_field = 'name'; + $this->table_name = 'config'; + $this->CreateField('name','',1,1); + $this->CreateField('value','',1,1); + $this->CreateField('config_description','', 0, 0); + $this->DisplayErrors = 0; + $this->DisplayQueries = 0; + parent::DBItem($Id); + } + + function Update() + { + $ret = parent::Update(); + // echo " update ".$this->GetDBField('name')."
"; + if ( $ret AND ereg("(per_page)",$this->GetDBField('name'))) { + $this->Application->StoreVar( + $this->GetDBField('name'), + $this->GetDBField('value') + ); + } + return $ret; + } +} + +class ConfigList extends kDBList +{ + function ConfigList($sql, $query_now=0, $owner=null) + { + parent::kDBList($sql, $query_now, $owner); + $this->Special = $owner->Params['special']; + switch ($this->Special) + { + default: + $this->sql = " SELECT * FROM config "; + }; + $this->DisplayQueries = 0; + } + + function &NewItem () + { + $new_item = new ConfigItem(NULL); + return $new_item; + } + + function GetOption($name) + { + if ($this->Find('name', $name)) + return $this->GetCurrentFieldValue('value'); + else + return false; + } + + function SetOption($name, $value) + { + if ($this->Find('name', $name)) {; + $this->SetCurrentFieldValue('value', $value); + $tmp =& $this->GetCurrentRec(); + $tmp->Update(); + } + else { + $push_hash = Array('name'=>$name, 'value'=>$value); + $id = array_push($this->Records, $push_hash); + if (count($this->IndexFields) > 0) { + foreach ($this->IndexFields as $key) { + if (is_string($push_hash[$key])) $store_key = strtolower($push_hash[$key]); + else $store_key = $push_hash[$key]; + $this->Indexes[$key][$store_key] = $id-1; + } + } + $this->last_rec++; + } + } +} + +?> \ No newline at end of file Index: trunk/core/kernel/db/db_event_handler.php =================================================================== diff -u -N --- trunk/core/kernel/db/db_event_handler.php (revision 0) +++ trunk/core/kernel/db/db_event_handler.php (revision 1560) @@ -0,0 +1,1423 @@ +getPrefixSpecial(true) instead of + * $event->Prefix_Special as usual. This is due PHP + * is converting "." symbols in variable names during + * submit info "_". $event->getPrefixSpecial optional + * 1st parameter returns correct corrent Prefix_Special + * for variables beeing submitted such way (e.g. variable + * name that will be converted by PHP: "users.read_only_id" + * will be submitted as "users_read_only_id". + * + * 2. When using $this->Application-LinkVar on variables submitted + * from form which contain $Prefix_Special then note 1st item. Example: + * LinkVar($event->getPrefixSpecial(true).'_varname',$event->Prefix_Special.'_varname') + * + */ + + + /** + * EventHandler that is used to process + * any database related events + * + */ + class kDBEventHandler extends kEventHandler { + + /** + * Description + * + * @var DBConnection + * @access public + */ + var $Conn; + + /** + * Adds ability to address db connection + * + * @return kDBEventHandler + * @access public + */ + function kDBEventHandler() + { + parent::kBase(); + $this->Conn =& $this->Application->GetADODBConnection(); + } + + function mapEvents() + { + $events_map = Array('OnRemoveFilters' => 'FilterAction', + 'OnApplyFilters' => 'FilterAction'); + + $this->eventMethods = array_merge($this->eventMethods, $events_map); + } + + /** + * Returns ID of current item to be edited + * by checking ID passed in get/post as prefix_id + * or by looking at first from selected ids, stored. + * Returned id is also stored in Session in case + * it was explicitly passed as get/post + * + * @param kEvent $event + * @return int + */ + function getPassedID(&$event) + { + //$ret = $this->Application->GetLinkedVar($event->getPrefixSpecial(true).'_id', $event->getPrefixSpecial().'_id'); + + // ?? We don't need to store selected id in session, as long as we have pass=all by default, which + // means that main item id will be passed to all sub-item screens by default + // another prove of that is that sub-items relay on main item '_mode' = 't' for switching to temp tables + // Also having id in session raised problems with the id of deleted item stuck in session + + + // 1. get id from post (used in admin) + $ret = $this->Application->GetVar($event->getPrefixSpecial(true).'_id'); + if($ret) return $ret; + + // 2. get id from env (used in front) + $ret = $this->Application->GetVar($event->getPrefixSpecial().'_id'); + if($ret) return $ret; + + // recall selected ids array and use the first one + $ids=$this->Application->GetVar($event->getPrefixSpecial().'_selected_ids'); + if ($ids != '') { + $ids=explode(',',$ids); + if($ids) $ret=array_shift($ids); + } + else { // if selected ids are not yet stored + $this->StoreSelectedIDs($event); + return $this->Application->GetVar($event->getPrefixSpecial(true).'_id'); // StoreSelectedIDs sets this variable + } + return $ret; + } + + /** + * Prepares and stores selected_ids string + * in Session and Application Variables + * by getting all checked ids from grid plus + * id passed in get/post as prefix_id + * + * @param kEvent $event + */ + function StoreSelectedIDs(&$event) + { + $ret = Array(); + + // May be we don't need this part: ? + $passed = $this->Application->GetVar($event->getPrefixSpecial(true).'_id'); + if($passed !== false && $passed != '') + { + array_push($ret, $passed); + } + + $ids = Array(); + + // get selected ids from post & save them to session + $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); + if($items_info) + { + $id_field = $this->Application->getUnitOption($event->Prefix,'IDField'); + foreach($items_info as $id => $field_values) + { + if( getArrayValue($field_values,$id_field) ) array_push($ids,$id); + } + //$ids=array_keys($items_info); + } + + $ret = array_unique(array_merge($ret, $ids)); + + $this->Application->SetVar($event->getPrefixSpecial().'_selected_ids',implode(',',$ret)); + $this->Application->LinkVar($event->getPrefixSpecial().'_selected_ids'); + + // This is critical - otherwise getPassedID will return last ID stored in session! (not exactly true) + // this smells... needs to be refactored + $first_id = getArrayValue($ret,0); + if($first_id === false) trigger_error('Requested ID for prefix '.$event->getPrefixSpecial().' not passed',E_USER_NOTICE); + $this->Application->SetVar($event->getPrefixSpecial(true).'_id', $first_id); + } + + /** + * Returns stored selected ids as an array + * + * @param kEvent $event + * @return array + */ + function getSelectedIDs(&$event) + { + return explode(',', $this->Application->GetVar($event->getPrefixSpecial().'_selected_ids')); + } + + /** + * Returs associative array of submitted fields for current item + * Could be used while creating/editing single item - + * meaning on any edit form, except grid edit + * + * @param kEvent $event + */ + function getSubmittedFields(&$event) + { + $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); + $field_values = $items_info ? array_shift($items_info) : Array(); + return $field_values; + } + + /** + * Removes any information about current/selected ids + * from Application variables and Session + * + * @param kEvent $event + */ + function clearSelectedIDs(&$event) + { + $prefix_special = $event->getPrefixSpecial(); + $ids = $this->Application->RecallVar($prefix_special.'_selected_ids'); + $event->setEventParam('ids', $ids); + + $this->Application->RemoveVar($prefix_special.'_selected_ids'); + $this->Application->SetVar($prefix_special.'_selected_ids', ''); + + $this->Application->SetVar($prefix_special.'_id', ''); // $event->getPrefixSpecial(true).'_id' too may be + } + + /*function SetSaveEvent(&$event) + { + $this->Application->SetVar($event->Prefix_Special.'_SaveEvent','OnUpdate'); + $this->Application->LinkVar($event->Prefix_Special.'_SaveEvent'); + }*/ + + /** + * Common builder part for Item & List + * + * @param kDBBase $object + * @param kEvent $event + * @access private + */ + function dbBuild(&$object,&$event) + { + $object->Configure(); + + $live_table = $event->getEventParam('live_table'); + + if( $this->UseTempTables($event) && !$live_table ) + { + $object->SwitchToTemp(); + } + + // This strange constuction creates hidden field for storing event name in form submit + // It pass SaveEvent to next screen, otherwise after unsuccsefull create it will try to update rather than create + $current_event = $this->Application->GetVar($event->Prefix_Special.'_event'); +// $this->Application->setEvent($event->Prefix_Special, $current_event); + $this->Application->setEvent($event->Prefix_Special, ''); + + $save_event = $this->UseTempTables($event) && $this->Application->GetTopmostPrefix($event->Prefix) == $event->Prefix ? 'OnSave' : 'OnUpdate'; + $this->Application->SetVar($event->Prefix_Special.'_SaveEvent',$save_event); + } + + /** + * Builds item (loads if needed) + * + * @param kEvent $event + * @access protected + */ + function OnItemBuild(&$event) + { + $object =& $event->getObject(); + $this->dbBuild($object,$event); + + $sql = $this->ItemPrepareQuery($event); + $sql = $this->Application->ReplaceLanguageTags($sql); + $object->setSelectSQL($sql); + + // 2. loads if allowed + $auto_load = $this->Application->getUnitOption($event->Prefix,'AutoLoad'); + if($auto_load) $this->LoadItem($event); + + $actions =& $this->Application->recallObject('kActions'); + $actions->Set($event->Prefix_Special.'_GoTab', ''); + + $actions->Set($event->Prefix_Special.'_GoId', ''); + } + + /** + * Build subtables array from configs + * + * @param kEvent $event + */ + function OnTempHandlerBuild(&$event) + { + $object =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler'); + $object->BuildTables( $event->Prefix, $this->getSelectedIDs($event) ); + } + + /** + * Enter description here... + * + * @param kEvent $event + * @return unknown + */ + function UseTempTables(&$event) + { + $object = &$event->getObject(); + $top_prefix = $this->Application->GetTopmostPrefix($event->Prefix); + return $this->Application->GetVar($top_prefix.'_mode') == 't'; + } + + function TablePrefix(&$event) + { + return $this->UseTempTables(&$event) ? kTempTablesHandler::GetTempTablePrefix().TABLE_PREFIX : TABLE_PREFIX; + } + + function LoadItem(&$event) + { + $object =& $event->getObject(); + if ( $event->getEventParam('ByParent') ) { + $parent_prefix = $this->Application->getUnitOption($event->Prefix, 'ParentPrefix'); + $parent_table_key = $this->Application->getUnitOption($event->Prefix, 'ParentTableKey'); + $parent_object =& $this->Application->recallObject($parent_prefix); + $id = $parent_object->GetDBField($parent_table_key); + $id_field = $this->Application->getUnitOption($event->Prefix, 'ForeignKey'); + } + else { + $id = $this->getPassedID($event); + $id_field = null; + } + if ($object->Load($id, $id_field)) { + $actions =& $this->Application->recallObject('kActions'); + $actions->Set($event->Prefix_Special.'_id', $object->GetId()); + } + } + + /** + * Builds list + * + * @param kEvent $event + * @access protected + */ + function OnListBuild(&$event) + { + //$event->setPseudoClass('_List'); + $object =& $event->getObject(); + + $this->dbBuild($object,$event); + + $sql = $this->ListPrepareQuery($event); + $sql = $this->Application->ReplaceLanguageTags($sql); + $object->setSelectSQL($sql); + + $object->linkToParent( $this->getMainSpecial($event) ); + +// $object->linkToParent($special); + + $this->AddFilters($event); + $this->SetCustomQuery($event); // new!, use this for dynamic queries based on specials for ex. + $this->SetPagination($event); + $this->SetSorting($event); + + $actions =& $this->Application->recallObject('kActions'); + $actions->Set('remove_specials['.$event->Prefix_Special.']', '0'); + $actions->Set($event->Prefix_Special.'_GoTab', ''); + } + + + /** + * Get's special of main item for linking with subitem + * + * @param kEvent $event + * @return string + */ + function getMainSpecial(&$event) + { + $special = $event->getEventParam('main_special'); + if($special === false || $special == '$main_special') + { + $special = $event->Special; + } + return $special; + } + + /** + * Apply any custom changes to list's sql query + * + * @param kEvent $event + * @access protected + * @see OnListBuild + */ + function SetCustomQuery(&$event) + { + + } + + /** + * Set's new perpage for grid + * + * @param kEvent $event + */ + function OnSetPerPage(&$event) + { + $per_page = $this->Application->GetVar($event->getPrefixSpecial(true).'_PerPage'); + $this->Application->StoreVar( $event->getPrefixSpecial().'_PerPage', $per_page ); + } + + /** + * Set's correct page for list + * based on data provided with event + * + * @param kEvent $event + * @access private + * @see OnListBuild + */ + function SetPagination(&$event) + { + // get PerPage (forced -> session -> config -> 10) + $per_page = $event->getEventParam('per_page'); + + $config_mapping = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping'); + + if ( $config_mapping ) { + switch ( $per_page ){ + case 'short_list' : + $per_page = $this->Application->ConfigValue($config_mapping['ShortListPerPage']); + break; + case 'default' : + $per_page = $this->Application->ConfigValue($config_mapping['PerPage']); + break; + } + } + + if(!$per_page) + { + $per_page_var = $event->getPrefixSpecial().'_PerPage'; + + $per_page = $this->Application->RecallVar($per_page_var); + if(!$per_page) + { + + if ( $config_mapping ) { + $per_page = $this->Application->ConfigValue($config_mapping['PerPage']); + } + if(!$per_page) $per_page = 10; + } + } + + $object =& $event->getObject(); + $object->SetPerPage($per_page); + $this->Application->StoreVarDefault($event->getPrefixSpecial().'_Page', 1); + + $page = $this->Application->GetVar($event->getPrefixSpecial().'_Page'); + if (!$page) { + $page = $this->Application->GetVar($event->getPrefixSpecial(true).'_Page'); + } + if (!$page) { + $page = $this->Application->RecallVar($event->getPrefixSpecial().'_Page'); + } + else { + $this->Application->StoreVar($event->getPrefixSpecial().'_Page', $page); + } + +// $page = $this->Application->GetLinkedVar($event->getPrefixSpecial(true).'_Page', $event->getPrefixSpecial().'_Page'); + if( !$event->getEventParam('skip_counting') ) + { + $pages = $object->GetTotalPages(); + if($page > $pages) + { + $this->Application->StoreVar($event->getPrefixSpecial().'_Page', 1); + $page = 1; + } + } + $object->SetPage($page); + + } + + /** + * Set's correct sorting for list + * based on data provided with event + * + * @param kEvent $event + * @access private + * @see OnListBuild + */ + function SetSorting(&$event) + { + $event->setPseudoClass('_List'); + $object =& $event->getObject(); + + $cur_sort1 = $this->Application->RecallVar($event->Prefix_Special.'_Sort1'); + $cur_sort1_dir = $this->Application->RecallVar($event->Prefix_Special.'_Sort1_Dir'); + $cur_sort2 = $this->Application->RecallVar($event->Prefix_Special.'_Sort2'); + $cur_sort2_dir = $this->Application->RecallVar($event->Prefix_Special.'_Sort2_Dir'); + + + $sorting_configs = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping'); + $list_sortings = $this->Application->getUnitOption($event->Prefix, 'ListSortings'); + $sorting_prefix = getArrayValue($list_sortings, $event->Special) ? $event->Special : ''; + + $tag_sort_by = $event->getEventParam('sort_by'); + if ($tag_sort_by) { + list($by, $dir) = explode(',', $tag_sort_by); + if ($by == 'random') $by = 'RAND()'; + $object->AddOrderField($by, $dir); + } + + if ($sorting_configs && isset ($sorting_configs['DefaultSorting1Field'])){ + $list_sortings[$sorting_prefix]['Sorting'] = Array( + $this->Application->ConfigValue($sorting_configs['DefaultSorting1Field']) => $this->Application->ConfigValue($sorting_configs['DefaultSorting1Dir']), + $this->Application->ConfigValue($sorting_configs['DefaultSorting2Field']) => $this->Application->ConfigValue($sorting_configs['DefaultSorting2Dir']), + ); + } + + // Use default if not specified + if ( !$cur_sort1 || !$cur_sort1_dir) + { + if ( $sorting = getArrayValue($list_sortings, $sorting_prefix, 'Sorting') ) { + reset($sorting); + $cur_sort1 = key($sorting); + $cur_sort1_dir = current($sorting); + if (next($sorting)) { + $cur_sort2 = key($sorting); + $cur_sort2_dir = current($sorting); + } + } + } + + if ( $forced_sorting = getArrayValue($list_sortings, $sorting_prefix, 'ForcedSorting') ) { + foreach ($forced_sorting as $field => $dir) { + $object->AddOrderField($field, $dir); + } + } + + if($cur_sort1 != '' && $cur_sort1_dir != '') + { + $object->AddOrderField($cur_sort1, $cur_sort1_dir); + } + if($cur_sort2 != '' && $cur_sort2_dir != '') + { + $object->AddOrderField($cur_sort2, $cur_sort2_dir); + } + } + + + /** + * Add filters found in session + * + * @param kEvent $event + */ + function AddFilters(&$event) + { + $object =& $event->getObject(); + + $search_filter = $this->Application->RecallVar($event->getPrefixSpecial().'_search_filter'); + if($search_filter) + { + $search_filter = unserialize($search_filter); + foreach($search_filter as $search_field => $filter_params) + { + $filter_type = ($filter_params['type'] == 'having') ? HAVING_FILTER : WHERE_FILTER; + $object->addFilter($search_field, $filter_params['value'], $filter_type, FLT_SEARCH); + } + } + + $view_filter = $this->Application->RecallVar($event->getPrefixSpecial().'_view_filter'); + if($view_filter) + { + $view_filter = unserialize($view_filter); + $temp_filter =& $this->Application->makeClass('kMultipleFilter'); + $filter_menu = $this->Application->getUnitOption($event->Prefix,'FilterMenu'); + + $group_key = 0; $group_count = count($filter_menu['Groups']); + while($group_key < $group_count) + { + $group_info = $filter_menu['Groups'][$group_key]; + + $temp_filter->setType( constant('FLT_TYPE_'.$group_info['mode']) ); + $temp_filter->clearFilters(); + foreach ($group_info['filters'] as $flt_id) + { + $sql_key = getArrayValue($view_filter,$flt_id) ? 'on_sql' : 'off_sql'; + if ($filter_menu['Filters'][$flt_id][$sql_key] != '') + { + $temp_filter->addFilter('view_filter_'.$flt_id, $filter_menu['Filters'][$flt_id][$sql_key]); + } + } + $object->addFilter('view_group_'.$group_key, $temp_filter, $group_info['type'] , FLT_VIEW); + $group_key++; + } + } + } + + /** + * Set's new sorting for list + * + * @param kEvent $event + * @access protected + */ + function OnSetSorting(&$event) + { + $cur_sort1 = $this->Application->RecallVar($event->Prefix_Special.'_Sort1'); + $cur_sort1_dir = $this->Application->RecallVar($event->Prefix_Special.'_Sort1_Dir'); + $cur_sort2 = $this->Application->RecallVar($event->Prefix_Special.'_Sort2'); + $cur_sort2_dir = $this->Application->RecallVar($event->Prefix_Special.'_Sort2_Dir'); + + $passed_sort1 = $this->Application->GetVar($event->getPrefixSpecial(true).'_Sort1'); + if ($cur_sort1 == $passed_sort1) { + $cur_sort1_dir = $cur_sort1_dir == 'asc' ? 'desc' : 'asc'; + } + else { + $cur_sort2 = $cur_sort1; + $cur_sort2_dir = $cur_sort1_dir; + $cur_sort1 = $passed_sort1; + $cur_sort1_dir = 'asc'; + } + + $this->Application->StoreVar($event->Prefix_Special.'_Sort1', $cur_sort1); + $this->Application->StoreVar($event->Prefix_Special.'_Sort1_Dir', $cur_sort1_dir); + $this->Application->StoreVar($event->Prefix_Special.'_Sort2', $cur_sort2); + $this->Application->StoreVar($event->Prefix_Special.'_Sort2_Dir', $cur_sort2_dir); + } + + /** + * Set sorting directly to session + * + * @param kEvent $event + */ + function OnSetSortingDirect(&$event) + { + $combined = $this->Application->GetVar($event->getPrefixSpecial(true).'_CombinedSorting'); + if ($combined) { + list($field,$dir) = explode('|',$combined); + $this->Application->StoreVar($event->Prefix_Special.'_Sort1', $field); + $this->Application->StoreVar($event->Prefix_Special.'_Sort1_Dir', $dir); + return; + } + + $field_pos = $this->Application->GetVar($event->getPrefixSpecial(true).'_SortPos'); + $this->Application->LinkVar( $event->getPrefixSpecial(true).'_Sort'.$field_pos, $event->Prefix_Special.'_Sort'.$field_pos); + $this->Application->LinkVar( $event->getPrefixSpecial(true).'_Sort'.$field_pos.'_Dir', $event->Prefix_Special.'_Sort'.$field_pos.'_Dir'); + } + + /** + * Reset grid sorting to default (from config) + * + * @param kEvent $event + */ + function OnResetSorting(&$event) + { + $this->Application->RemoveVar($event->Prefix_Special.'_Sort1'); + $this->Application->RemoveVar($event->Prefix_Special.'_Sort1_Dir'); + $this->Application->RemoveVar($event->Prefix_Special.'_Sort2'); + $this->Application->RemoveVar($event->Prefix_Special.'_Sort2_Dir'); + } + + /** + * Creates needed sql query to load item, + * if no query is defined in config for + * special requested, then use default + * query + * + * @param kEvent $event + * @access protected + */ + function ItemPrepareQuery(&$event) + { + $sqls = $this->Application->getUnitOption($event->Prefix,'ItemSQLs'); + return isset($sqls[$event->Special]) ? $sqls[$event->Special] : $sqls['']; + } + + /** + * Creates needed sql query to load list, + * if no query is defined in config for + * special requested, then use default + * query + * + * @param kEvent $event + * @access protected + */ + function ListPrepareQuery(&$event) + { + $sqls = $this->Application->getUnitOption($event->Prefix,'ListSQLs'); + return isset( $sqls[$event->Special] ) ? $sqls[$event->Special] : $sqls['']; + } + + /** + * Apply custom processing to item + * + * @param kEvent $event + */ + function customProcessing(&$event, $type) + { + + } + +/* Edit Events mostly used in Admin */ + + /** + * Creates new kDBItem + * + * @param kEvent $event + * @access protected + */ + function OnCreate(&$event) + { + $this->Application->setUnitOption($event->Prefix,'AutoLoad',false); + $object =& $event->getObject(); + + $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); + if($items_info) + { + list($id,$field_values) = each($items_info); + $object->SetFieldsFromHash($field_values); + } + + $this->customProcessing($event,'before'); + + //look at kDBItem' Create for ForceCreateId description, it's rarely used and is NOT set by default + if( $object->Create($event->getEventParam('ForceCreateId')) ) + { + if( $object->IsTempTable() ) $object->setTempID(); + $this->customProcessing($event,'after'); + $event->status=erSUCCESS; + $event->redirect_params = Array('opener'=>'u'); + } + else + { + $event->status=erFAIL; + $event->redirect=false; + $this->Application->SetVar($event->Prefix_Special.'_SaveEvent','OnCreate'); + $object->setID($id); + } + } + + /** + * Updates kDBItem + * + * @param kEvent $event + * @access protected + */ + function OnUpdate(&$event) + { + $this->Application->setUnitOption($event->Prefix,'AutoLoad',false); + $object =& $event->getObject(); + + $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); + if($items_info) + { + foreach($items_info as $id => $field_values) + { + $object->Load($id); + $object->SetFieldsFromHash($field_values); + $this->customProcessing($event, 'before'); + if( $object->Update($id) ) + { + $this->customProcessing($event, 'after'); + $event->status=erSUCCESS; + } + else + { + $event->status=erFAIL; + $event->redirect=false; + break; + } + } + } + $event->redirect_params = Array('opener'=>'u'); + } + + /** + * Delete's kDBItem object + * + * @param kEvent $event + * @access protected + */ + function OnDelete(&$event) + { + $this->Application->setUnitOption($event->Prefix,'AutoLoad',false); + $object =& $event->getObject(); + $object->ID = $this->getPassedID($event); + if( $object->Delete() ) + { + $event->status = erSUCCESS; + } + else + { + $event->status = erFAIL; + $event->redirect = false; + } + } + + /** + * Prepares new kDBItem object + * + * @param kEvent $event + * @access protected + */ + function OnNew(&$event) + { + $this->Application->setUnitOption($event->Prefix,'AutoLoad',false); + $object =& $event->getObject(); + $object->setID(0); + $this->Application->SetVar($event->Prefix_Special.'_SaveEvent','OnCreate'); + + $table_info = $object->getLinkedInfo(); + $object->SetDBField($table_info['ForeignKey'], $table_info['ParentId']); + + $this->Application->setUnitOption($event->Prefix,'AutoLoad',true); + $event->redirect = false; + } + + /** + * Cancel's kDBItem Editing/Creation + * + * @param kEvent $event + * @access protected + */ + function OnCancel(&$event) + { + $event->redirect_params = Array('opener'=>'u'); + } + + + /** + * Deletes all selected items. + * Automatically recurse into sub-items using temp handler, and deletes sub-items + * by calling its Delete method if sub-item has AutoDelete set to true in its config file + * + * @param kEvent $event + */ + function OnMassDelete(&$event) + { + $event->status=erSUCCESS; + + $temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler'); + + $this->StoreSelectedIDs($event); + + $event->setEventParam('ids', $this->getSelectedIDs($event) ); + $this->customProcessing($event, 'before'); + $ids = $event->getEventParam('ids'); + + if($ids) + { + $temp->DeleteItems($event->Prefix, $event->Special, $ids); + } + $this->clearSelectedIDs($event); + } + + /** + * Prepare temp tables and populate it + * with items selected in the grid + * + * @param kEvent $event + */ + function OnEdit(&$event) + { + $this->StoreSelectedIDs($event); + + $temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler'); + $temp->PrepareEdit(); + + $event->redirect=false; + } + + /** + * Saves content of temp table into live and + * redirects to event' default redirect (normally grid template) + * + * @param kEvent $event + */ + function OnSave(&$event) + { + $event->CallSubEvent('OnPreSave'); + if ($event->status==erSUCCESS) { + $skip_master=false; + $temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler'); + + // newly created item + /*if($this->getPassedID($event) == 0) + { + $master_id = $temp->CopyMasterToOriginal(); + $temp->UpdateForeignKeys($master_id); // save linked field values + $skip_master = true; //we've already copied master table to get the id + }*/ + + $temp->SaveEdit($skip_master); + $this->clearSelectedIDs($event); + + $event->redirect_params = Array('opener'=>'u'); + $this->Application->RemoveVar($event->getPrefixSpecial().'_modified'); + } + } + + /** + * Cancels edit + * Removes all temp tables and clears selected ids + * + * @param kEvent $event + */ + function OnCancelEdit(&$event) + { + $temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler'); + $temp->CancelEdit(); + + $this->clearSelectedIDs($event); + $event->redirect_params = Array('opener'=>'u'); + $this->Application->RemoveVar($event->getPrefixSpecial().'_modified'); + } + + /** + * Saves edited item into temp table + * If there is no id, new item is created in temp table + * + * @param kEvent $event + */ + function OnPreSave(&$event) + { + //$event->redirect = false; + // if there is no id - it means we need to create an item + if (is_object($event->MasterEvent)) { + $event->MasterEvent->setEventParam('IsNew',false); + } + $item_id = $this->getPassedID($event); + if($item_id == '') + { + $event->CallSubEvent('OnPreSaveCreated'); + if (is_object($event->MasterEvent)) { + $event->MasterEvent->setEventParam('IsNew',true); + } + return; + } + + $this->Application->setUnitOption($event->Prefix,'AutoLoad',false); + $object =& $event->getObject(); + + $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); + if($items_info) + { + foreach($items_info as $id => $field_values) + { + $object->SetDefaultValues(); + $object->Load($id); + $object->SetFieldsFromHash($field_values); + if( $object->Update($id) ) + { + $event->status=erSUCCESS; + } + else + { + $event->status=erFAIL; + $event->redirect=false; + break; + } + } + } + } + + /** + * Saves edited item in temp table and loads + * item with passed id in current template + * Used in Prev/Next buttons + * + * @param kEvent $event + */ + function OnPreSaveAndGo(&$event) + { + $event->CallSubEvent('OnPreSave'); + if ($event->status==erSUCCESS) { + $event->redirect_params[$event->getPrefixSpecial(true).'_id'] = $this->Application->GetVar($event->Prefix_Special.'_GoId'); + } + } + + /** + * Saves edited item in temp table and goes + * to passed tabs, by redirecting to it with OnPreSave event + * + * @param kEvent $event + */ + function OnPreSaveAndGoToTab(&$event) + { + $event->CallSubEvent('OnPreSave'); + if ($event->status==erSUCCESS) { + $event->redirect=$this->Application->GetVar($event->getPrefixSpecial(true).'_GoTab'); + } + } + + /** + * Saves editable list and goes to passed tab, + * by redirecting to it with empty event + * + * @param kEvent $event + */ + function OnUpdateAndGoToTab(&$event) + { + $event->setPseudoClass('_List'); + $event->CallSubEvent('OnUpdate'); + if ($event->status==erSUCCESS) { + $event->redirect=$this->Application->GetVar($event->getPrefixSpecial(true).'_GoTab'); + } + } + + /** + * Prepare temp tables for creating new item + * but does not create it. Actual create is + * done in OnPreSaveCreated + * + * @param kEvent $event + */ + function OnPreCreate(&$event) + { + $this->clearSelectedIDs($event); + + $this->Application->setUnitOption($event->Prefix,'AutoLoad',false); + $object =& $event->getObject(); + + $temp =& $this->Application->recallObject($event->Prefix.'_TempHandler', 'kTempTablesHandler'); + $temp->PrepareEdit(); + + $object->setID(0); + + $event->redirect=false; + } + + /** + * Creates a new item in temp table and + * stores item id in App vars and Session on succsess + * + * @param kEvent $event + */ + function OnPreSaveCreated(&$event) + { + $this->Application->setUnitOption($event->Prefix,'AutoLoad',false); + + $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); + if($items_info) $field_values = array_shift($items_info); + + $object =& $event->getObject(); + $object->SetFieldsFromHash($field_values); + + $this->customProcessing($event, 'before'); + + if( $object->Create() ) + { + $this->customProcessing($event, 'after'); + $event->redirect_params[$event->getPrefixSpecial(true).'_id'] = $object->GetId(); + $event->status=erSUCCESS; + } + else + { + $event->status=erFAIL; + $event->redirect=false; + $object->setID(0); + } + + } + +/* End of Edit events */ + + // III. Events that allow to put some code before and after Update,Load,Create and Delete methods of item + + /** + * Occurse before loading item, 'id' parameter + * allows to get id of item beeing loaded + * + * @param kEvent $event + * @access public + */ + function OnBeforeItemLoad(&$event) + { + + } + + /** + * Occurse after loading item, 'id' parameter + * allows to get id of item that was loaded + * + * @param kEvent $event + * @access public + */ + function OnAfterItemLoad(&$event) + { + + } + + /** + * Occurse before creating item + * + * @param kEvent $event + * @access public + */ + function OnBeforeItemCreate(&$event) + { + + } + + /** + * Occurse after creating item + * + * @param kEvent $event + * @access public + */ + function OnAfterItemCreate(&$event) + { + + } + + /** + * Occurse before updating item + * + * @param kEvent $event + * @access public + */ + function OnBeforeItemUpdate(&$event) + { + + } + + /** + * Occurse after updating item + * + * @param kEvent $event + * @access public + */ + function OnAfterItemUpdate(&$event) + { + + } + + /** + * Occurse before deleting item, id of item beeing + * deleted is stored as 'id' event param + * + * @param kEvent $event + * @access public + */ + function OnBeforeItemDelete(&$event) + { + + } + + /** + * Occurse after deleting item, id of deleted item + * is stored as 'id' param of event + * + * @param kEvent $event + * @access public + */ + function OnAfterItemDelete(&$event) + { + + } + + /** + * Occurs after successful item validation + * + * @param kEvent $event + */ + function OnAfterItemValidate(&$event) + { + + } + + /** + * Occures after an item has been copied to temp + * Id of copied item is passed as event' 'id' param + * + * @param kEvent $event + */ + function OnAfterCopyToTemp(&$event) + { + + } + + /** + * Occures before an item is deleted from live table when copying from temp + * (temp handler deleted all items from live and then copy over all items from temp) + * Id of item being deleted is passed as event' 'id' param + * + * @param kEvent $event + */ + function OnBeforeDeleteFromLive(&$event) + { + + } + + /** + * Occures before an item is copied to live table (after all foreign keys have been updated) + * Id of item being copied is passed as event' 'id' param + * + * @param kEvent $event + */ + function OnBeforeCopyToLive(&$event) + { + + } + + /** + * !!! NOT FULLY IMPLEMENTED - SEE TEMP HANDLER COMMENTS (search by event name)!!! + * Occures after an item has been copied to live table + * Id of copied item is passed as event' 'id' param + * + * @param kEvent $event + */ + function OnAfterCopyToLive(&$event) + { + + } + + /** + * Occures before an item is cloneded + * Id of ORIGINAL item is passed as event' 'id' param + * Do not call object' Update method in this event, just set needed fields! + * + * @param kEvent $event + */ + function OnBeforeClone(&$event) + { + + } + + /** + * Occures after an item has been cloned + * Id of newly created item is passed as event' 'id' param + * + * @param kEvent $event + */ + function OnAfterClone(&$event) + { + + } + + /** + * Ensures that popup will be closed automatically + * and parent window will be refreshed with template + * passed + * + * @param kEvent $event + * @access public + */ + function finalizePopup(&$event, $main_prefix, $t) + { + $event->redirect = 'incs/close_popup'; + + // 2. substitute opener + $opener_stack = $this->Application->RecallVar('opener_stack'); + $opener_stack = $opener_stack ? unserialize($opener_stack) : Array(); + //array_pop($opener_stack); + + $new_level = 'index4.php|'.ltrim($this->Application->BuildEnv($t, Array('m_opener' => 'u'), 'all'), ENV_VAR_NAME.'='); + array_push($opener_stack,$new_level); + $this->Application->StoreVar('opener_stack',serialize($opener_stack)); + } + + /** + * Create search filters based on search query + * + * @param kEvent $event + * @access protected + */ + function OnSearch(&$event) + { + $event->setPseudoClass('_List'); + $object =& $event->getObject(); + + $keyword = $this->Application->GetVar( $event->getPrefixSpecial(true).'_search_keyword'); + $this->Application->StoreVar( $event->getPrefixSpecial().'_search_keyword', $keyword); + + if(!$keyword) + { + $this->OnSearchReset($event); + return true; + } + + $grid_name = $this->Application->GetVar('grid_name'); + $grids = $this->Application->getUnitOption($event->Prefix,'Grids'); + $search_fields = array_keys($grids[$grid_name]['Fields']); + + $search_filter = Array(); + + foreach($search_fields as $search_field) + { + $filter_type = isset($object->VirtualFields[$search_field]) ? 'having' : 'where'; + $field_type = getArrayValue($object->Fields[$search_field],'type'); + if(!$field_type) $field_type = 'string'; // default LIKE filter for all fields without type + $keyword = trim($keyword); + $keyword = str_replace(Array('"',"'"),'',$keyword); + + $filter_value = ''; + $table_name = ($filter_type == 'where') ? '`'.$object->TableName.'`.' : ''; + + // get field clause by formatter name and/or parameters + $formatter = getArrayValue($object->Fields[$search_field],'formatter'); + switch ($formatter) + { + case 'kOptionsFormatter': + $search_keys = Array(); + $use_phrases = getArrayValue($object->Fields[$search_field], 'use_phrases'); + foreach($object->Fields[$search_field]['options'] as $key => $val) + { + $pattern = '#'.$keyword.'#i'; + if ( preg_match($pattern, $use_phrases ? $this->Application->Phrase($val) : $val) ) { + array_push($search_keys, $key); + } + } + if (count($search_keys) > 0) { + $filter_value = $table_name.'`'.$search_field.'` IN ('.implode(',', $search_keys).')'; + } + + $field_processed = true; + break; + + default: + $field_processed = false; + break; + } + + // if not already processed by formatter, then get clause by field type + + if(!$field_processed) + { + switch($field_type) + { + case 'int': + case 'integer': + case 'numeric': + if( !is_numeric($keyword) ) break; + $filter_value = $table_name.'`'.$search_field.'` = \''.$keyword.'\''; + break; + + case 'double': + case 'float': + case 'real': + if( !is_numeric($keyword) ) break; + $filter_value = 'ABS('.$table_name.'`'.$search_field.'` - \''.str_replace(',','.',$keyword).'\') <= 0.0001'; + break; + + case 'string': + $like_keyword = preg_replace( '/\'(.*)\'/U', '\\1', $this->Conn->qstr( str_replace('\\','\\\\', $keyword) ) ); + $keywords = explode(' ', $like_keyword); + foreach($keywords as $keyword_pos => $keyword_value) + { + $keyword_value = trim($keyword_value); + if($keyword_value) + { + $keywords[$keyword_pos] = $table_name.'`'.$search_field.'` LIKE \'%'.$keyword_value.'%\''; + } + else + { + unset($keywords[$keyword_pos]); + } + } + $filter_value = '('.implode(') OR (',$keywords).')'; + break; + } + } + if($filter_value) $search_filter[$search_field] = Array('type' => $filter_type, 'value' => $filter_value); + } + + $this->Application->StoreVar($event->getPrefixSpecial().'_search_filter', serialize($search_filter) ); + } + + /** + * Clear search keywords + * + * @param kEvent $event + * @access protected + */ + function OnSearchReset(&$event) + { + $this->Application->RemoveVar($event->getPrefixSpecial().'_search_filter'); + $this->Application->RemoveVar($event->getPrefixSpecial().'_search_keyword'); + } + + /** + * Set's new filter value (filter_id meaning from config) + * + * @param kEvent $event + */ + function OnSetFilter(&$event) + { + $filter_id = $this->Application->GetVar('filter_id'); + $filter_value = $this->Application->GetVar('filter_value'); + + $view_filter = $this->Application->RecallVar($event->getPrefixSpecial().'_view_filter'); + $view_filter = $view_filter ? unserialize($view_filter) : Array(); + + $view_filter[$filter_id] = $filter_value; + + $this->Application->StoreVar( $event->getPrefixSpecial().'_view_filter', serialize($view_filter) ); + } + + /** + * Add/Remove all filters applied to list from "View" menu + * + * @param kEvent $event + */ + function FilterAction(&$event) + { + $view_filter = Array(); + $filter_menu = $this->Application->getUnitOption($event->Prefix,'FilterMenu'); + switch ($event->Name) + { + case 'OnRemoveFilters': + $filter_value = 1; + break; + + case 'OnApplyFilters': + $filter_value = 0; + break; + } + + foreach($filter_menu['Filters'] as $filter_key => $filter_params) + { + if(!$filter_params) continue; + $view_filter[$filter_key] = $filter_value; + } + $this->Application->StoreVar( $event->getPrefixSpecial().'_view_filter', serialize($view_filter) ); + } + + function OnPreSaveAndOpenTranslator(&$event) + { + $this->Application->SetVar('allow_translation', true); + $object =& $event->getObject(); + $this->RemoveRequiredFields($object); + $event->CallSubEvent('OnPreSave'); + if ($event->status == erSUCCESS) { +// $url = $this->Application->HREF($t, '', Array('pass'=>'all', $event->getPrefixSpecial(true).'_id' => $object->GetId())); +// $field = $this->Application->GetVar('translator_field'); + $event->redirect = $this->Application->GetVar('translator_t'); + $event->redirect_params = Array('pass'=>'all,trans', + $event->getPrefixSpecial(true).'_id' => $object->GetId(), + 'trans_event'=>'OnLoad', + 'trans_prefix'=>$event->Prefix, + 'trans_field'=>$this->Application->GetVar('translator_field'), + ); + //$after_script = "openTranslator('".$event->getPrefixSpecial()."', '".$field."', '".$url."', '".$wnd_name."')"; + } + +// $this->Application->SetVar('after_script', $after_script); +// $event->redirect = false; + } + + function RemoveRequiredFields(&$object) + { + // making all field non-required to achieve successful presave + foreach($object->Fields as $field => $options) + { + if(isset($options['required'])) + { + unset($object->Fields[$field]['required']); + } + } + } + + + + } + + +?> \ No newline at end of file Index: trunk/core/kernel/utility/unit_config_reader.php =================================================================== diff -u -N --- trunk/core/kernel/utility/unit_config_reader.php (revision 0) +++ trunk/core/kernel/utility/unit_config_reader.php (revision 1560) @@ -0,0 +1,384 @@ +scanModules(MODULES_PATH); + } + + /** + * Read configs from all directories + * on path specified + * + * @param string $folderPath + * @access public + */ + function processFolder($folderPath, $cached) + { + $fh=opendir($folderPath); + while(($sub_folder=readdir($fh))) + { + $full_path=$folderPath.'/'.$sub_folder; + if( $this->isDir($full_path) && file_exists($this->getConfigName($full_path)) ) + { + if (filemtime($full_path) > $cached) { + $this->CacheExpired = true; + + $file = $this->getConfigName($full_path); + if ( defined('DEBUG_MODE') && dbg_ConstOn('DBG_PROFILE_INCLUDES')) { + + if ( in_array($file, get_required_files()) ) return; + global $debugger; + $debugger->IncludeLevel++; + $before_time = getmicrotime(); + $before_mem = memory_get_usage(); + include_once(DOC_ROOT.BASE_PATH.$file); + $used_time = getmicrotime() - $before_time; + $used_mem = memory_get_usage() - $before_mem; + $debugger->IncludeLevel--; + $debugger->IncludesData['file'][] = str_replace(DOC_ROOT.BASE_PATH, '', $file); + $debugger->IncludesData['mem'][] = $used_mem; + $debugger->IncludesData['time'][] = $used_time; + $debugger->IncludesData['level'][] = -1; + } + else { + include_once($file); + } + + $prefix=$config['Prefix']; + $config['BasePath']=$full_path; + $this->configData[$prefix] = $config; + } + } + } + } + + function ParseConfigs() + { + foreach ($this->configData as $prefix => $config) + { + $this->parseConfig($prefix); + } + } + + function findConfigFiles($folderPath) + { + $folderPath = str_replace(DOC_ROOT.BASE_PATH, '', $folderPath); + $fh=opendir(DOC_ROOT.BASE_PATH.$folderPath); + while(($sub_folder=readdir($fh))) + { + $full_path=DOC_ROOT.BASE_PATH.$folderPath.'/'.$sub_folder; + if( $this->isDir($full_path)) + { + if ( file_exists(DOC_ROOT.BASE_PATH.$this->getConfigName($folderPath.'/'.$sub_folder)) ) { + $this->configFiles[] = $this->getConfigName($folderPath.'/'.$sub_folder); + } + $this->findConfigFiles($full_path); + +// if (filemtime($full_path) > $cached) { } + + } + } + } + + function includeConfigFiles() + { + foreach ($this->configFiles as $filename) { + if ( defined('DEBUG_MODE') && DEBUG_MODE && dbg_ConstOn('DBG_PROFILE_INCLUDES')) { + + if ( in_array($filename, get_required_files()) ) return; + global $debugger; + $debugger->IncludeLevel++; + $before_time = getmicrotime(); + $before_mem = memory_get_usage(); + include_once(DOC_ROOT.BASE_PATH.$filename); + $used_time = getmicrotime() - $before_time; + $used_mem = memory_get_usage() - $before_mem; + $debugger->IncludeLevel--; + $debugger->IncludesData['file'][] = str_replace(DOC_ROOT.BASE_PATH, '', $filename); + $debugger->IncludesData['mem'][] = $used_mem; + $debugger->IncludesData['time'][] = $used_time; + $debugger->IncludesData['level'][] = -1; + } + else { + include_once(DOC_ROOT.BASE_PATH.$filename); + } + + $prefix=$config['Prefix']; + + $config['BasePath']=dirname(DOC_ROOT.BASE_PATH.$filename); + $this->configData[$prefix] = $config; + + } + } + + function scanModules($folderPath) + { + global $debugger; + + if (defined('CACHE_CONFIGS_FILES')) { + $conn =& $this->Application->GetADODBConnection(); + $data = $conn->GetRow('SELECT Data, Cached FROM '.TABLE_PREFIX.'Cache WHERE VarName = "config_files"'); + if ($data && $data['Cached'] > (time() - 3600) ) { + $this->configFiles = unserialize($data['Data']); + $files_cached = $data['Cached']; + } + else { + $files_cached = 0; + } + } + else { + $files_cached = 0; + } + + + if (defined('CACHE_CONFIGS_DATA')) { + $conn =& $this->Application->GetADODBConnection(); + $data = $conn->GetRow('SELECT Data, Cached FROM '.TABLE_PREFIX.'Cache WHERE VarName = "config_data"'); + if ($data && $data['Cached'] > (time() - 3600) ) { + $this->configData = unserialize($data['Data']); + $data_cached = $data['Cached']; + } + else { + $data_cached = 0; + } + } + else { + $data_cached = 0; + } + + if ( !defined('CACHE_CONFIGS_FILES') || $files_cached == 0 ) { + $this->findConfigFiles($folderPath); + } + + if ( !defined('CACHE_CONFIGS_DATA') || $data_cached == 0) { + $this->includeConfigFiles(); + } + + /*// && (time() - $cached) > 600) - to skip checking files modified dates + if ( !defined('CACHE_CONFIGS') ) { + $fh=opendir($folderPath); + while(($sub_folder=readdir($fh))) + { + $full_path=$folderPath.'/'.$sub_folder.'/units'; + if( $this->isDir($full_path) ) + { + $this->processFolder($full_path, $cached); + } + } + }*/ + $this->ParseConfigs(); + + if (defined('CACHE_CONFIGS_FILES') && $files_cached == 0) { + $conn->Query('REPLACE '.TABLE_PREFIX.'Cache (VarName, Data, Cached) VALUES ("config_files", '.$conn->qstr(serialize($this->configFiles)).', '.time().')'); + } + + if (defined('CACHE_CONFIGS_DATA') && $data_cached == 0) { + $conn->Query('REPLACE '.TABLE_PREFIX.'Cache (VarName, Data, Cached) VALUES ("config_data", '.$conn->qstr(serialize($this->configData)).', '.time().')'); + } + unset($this->configFiles); +// unset($this->configData); + + } + + /** + * Register nessasary classes + * + * @param string $prefix + * @access private + */ + function parseConfig($prefix) + { + $config =& $this->configData[$prefix]; + $event_manager =& $this->Application->recallObject('EventManager'); + $class_params=Array('ItemClass','ListClass','EventHandlerClass','TagProcessorClass'); + foreach($class_params as $param_name) + { + if ( !(isset($config[$param_name]) ) ) continue; + $class_info =& $config[$param_name]; + $pseudo_class = $this->getPrefixByParamName($param_name,$prefix); + $this->Application->registerClass( $class_info['class'], + $config['BasePath'].'/'.$class_info['file'], + $pseudo_class); + $event_manager->registerBuildEvent($pseudo_class,$class_info['build_event']); + } + + $register_classes = getArrayValue($config,'RegisterClasses'); + if($register_classes) + { + foreach($register_classes as $class_info) + { + $this->Application->registerClass( $class_info['class'], + $config['BasePath'].'/'.$class_info['file'], + $class_info['pseudo']); + //$event_manager->registerBuildEvent($class_info['pseudo'],$class_info['build_event']); + } + } + + if ( is_array(getArrayValue($config, 'Hooks')) ) { + foreach ($config['Hooks'] as $hook) { + $do_prefix = $hook['DoPrefix'] == '' ? $config['Prefix'] : $hook['DoPrefix']; + + if ( !is_array($hook['HookToEvent']) ) { + $hook_events = Array( $hook['HookToEvent'] ); + } + else { + $hook_events = $hook['HookToEvent']; + } + foreach ($hook_events as $hook_event) { + $this->Application->registerHook($hook['HookToPrefix'], $hook['HookToSpecial'], $hook_event, $hook['Mode'], $do_prefix, $hook['DoSpecial'], $hook['DoEvent'], $hook['Conditional']); + } + } + } + + if ( is_array(getArrayValue($config, 'AggregateTags')) ) { + foreach ($config['AggregateTags'] as $aggregate_tag) { + $aggregate_tag['LocalPrefix'] = $config['Prefix']; + $this->Application->registerAggregateTag($aggregate_tag); + } + } + + if ( $this->Application->isDebugMode() && dbg_ConstOn('DBG_VALIDATE_CONFIGS') && isset($config['TableName']) ) + { + global $debugger; + $tablename = $config['TableName']; + + $conn =& $this->Application->GetADODBConnection(); + $res = $conn->Query("DESCRIBE $tablename"); + + foreach ($res as $field) { + $f_name = $field['Field']; + if (getArrayValue($config, 'Fields')) { + if ( !array_key_exists ($f_name, $config['Fields']) ) { + $debugger->appendHTML("Config Warning: Field $f_name exists in the database, but is not defined in config file for prefix ".$config['Prefix']."!"); + safeDefine('DBG_RAISE_ON_WARNINGS', 1); + } + else { + $options = $config['Fields'][$f_name]; + if ($field['Null'] == '') { + if ( $f_name != $config['IDField'] && !isset($options['not_null']) && !isset($options['required']) ) { + $debugger->appendHTML("Config Error: Field $f_name in config for prefix ".$config['Prefix']." is NOT NULL in the database, but is not configured as not_null or required!"); + safeDefine('DBG_RAISE_ON_WARNINGS', 1); + } + if ( isset($options['not_null']) && !isset($options['default']) ) { + $debugger->appendHTML("Config Error: Field $f_name in config for prefix ".$config['Prefix']." is described as NOT NULL, but does not have DEFAULT value!"); + safeDefine('DBG_RAISE_ON_WARNINGS', 1); + } + } + + } + } + } + } + } + + /** + * Reads unit (specified by $prefix) + * option specified by $option + * + * @param string $prefix + * @param string $option + * @return string + * @access public + */ + function getUnitOption($prefix,$name) + { + return isset($this->configData[$prefix][$name])?$this->configData[$prefix][$name]:false; + } + + /** + * Read all unit with $prefix options + * + * @param string $prefix + * @return Array + * @access public + */ + function getUnitOptions($prefix) + { + return isset($this->configData[$prefix])?$this->configData[$prefix]:false; + } + + /** + * Set's new unit option value + * + * @param string $prefix + * @param string $name + * @param string $value + * @access public + */ + function setUnitOption($prefix,$name,$value) + { + $this->configData[$prefix][$name]=$value; + } + + function getPrefixByParamName($paramName,$prefix) + { + $pseudo_class_map=Array( + 'ItemClass'=>'%s', + 'ListClass'=>'%s_List', + 'EventHandlerClass'=>'%s_EventHandler', + 'TagProcessorClass'=>'%s_TagProcessor' + ); + return sprintf($pseudo_class_map[$paramName],$prefix); + } + + /** + * Get's config file name based + * on folder name supplied + * + * @param string $folderPath + * @return string + * @access private + */ + function getConfigName($folderPath) + { + return $folderPath.'/'.basename($folderPath).'_config.php'; + } + + /** + * is_dir ajustment to work with + * directory listings too + * + * @param string $folderPath + * @return bool + * @access private + */ + function isDir($folderPath) + { + $base_name=basename($folderPath); + $ret=!($base_name=='.'||$base_name=='..'); + return $ret&&is_dir($folderPath); + } + + + } + + +?> \ No newline at end of file Index: trunk/core/kernel/session/login_event_handler.php =================================================================== diff -u -N --- trunk/core/kernel/session/login_event_handler.php (revision 0) +++ trunk/core/kernel/session/login_event_handler.php (revision 1560) @@ -0,0 +1,12 @@ +Application->Redirect('logout'); + } + } + + +?> \ No newline at end of file Index: trunk/core/kernel/utility/filters.php =================================================================== diff -u -N --- trunk/core/kernel/utility/filters.php (revision 0) +++ trunk/core/kernel/utility/filters.php (revision 1560) @@ -0,0 +1,106 @@ +setType($type); + } + + /** + * Enter description here... + * + * @param unknown_type $new_type + */ + function setType($new_type) + { + $this->type = $new_type; + } + + /** + * Adds new or replaces old filter with same name + * + * @param string $name + * @param mixed $clause kMultipleFilter object or where clause ifself + * @access public + */ + function addFilter($name, $clause) + { + if( is_object($clause) && $clause->hasFilters() ) + { + $this->filters[$name] = $clause->getSQL(); + } + elseif( !is_object($clause) && $clause ) + { + $this->filters[$name] = $clause; + } + } + + /** + * Removes specified filter from filters list + * + * @param string $name + * @access public + */ + function removeFilter($name) + { + unset($this->filters[$name]); + } + + /** + * Remove all added filters + * + * @access public + */ + function clearFilters() + { + $this->filters = Array(); + } + + /** + * Build where clause based on added filters and multiple filter type + * + * @return string + * @access public + */ + function getSQL() + { + $filter_count = count($this->filters); + if(!$filter_count) return ''; + + return '('.implode(') '.$this->type.' (',$this->filters).')'; + } + + /** + * Allows to check if some filters are added to multiple filter + * + * @return bool + * @access public + */ + function hasFilters() + { + return $this->filters ? true : false; + } + } +?> \ No newline at end of file Index: trunk/core/kernel/utility/formatters.php =================================================================== diff -u -N --- trunk/core/kernel/utility/formatters.php (revision 0) +++ trunk/core/kernel/utility/formatters.php (revision 1560) @@ -0,0 +1,904 @@ +GetFieldOptions($field_name); + if ( isset($format) ) $options['format'] = $format; + $tc_value = $this->TypeCast($value,$options); + if( ($tc_value === false) || ($tc_value != $value) ) return $value; // for leaving badly formatted date on the form + + if (isset($options['format'])) return sprintf($options['format'], $tc_value); + + return $tc_value; + } + +//function Parse($value, $options, &$errors) + function Parse($value, $field_name, &$object) + { + if ($value == '') return NULL; + + $options = $object->GetFieldOptions($field_name); + $tc_value = $this->TypeCast($value,$options); + if($tc_value === false) return $value; // for leaving badly formatted date on the form + + if( isset($options['type']) ) + { + if( preg_match('#double|float|real|numeric#', $options['type']) ) $tc_value = str_replace(',', '.', $tc_value); + } + + if( isset($options['regexp']) ) + { + if( !preg_match($options['regexp'], $value) ) + { + $object->FieldErrors[$field_name]['pseudo'] = 'invalid_format'; + } + } + + return $tc_value; + } + + function HumanFormat($format) + { + return $format; + } + + + /** + * The method is supposed to alter config options or cofigure object in some way based on its usage of formatters + * The methods is called for every field with formatter defined when configuring item. + * Could be used for adding additional VirtualFields to an object required by some special Formatter + * + * @param string $field_name + * @param array $field_options + * @param kDBBase $object + */ + function PrepareOptions($field_name, &$field_options, &$object) + { + + } + + /** + * Used for split fields like timestamp -> date, time + * Called from DBItem to update sub fields values after loading item + * + * @param unknown_type $field + * @param unknown_type $value + * @param unknown_type $options + * @param unknown_type $object + */ + function UpdateSubFields($field, $value, &$options, &$object) + { + + } + + /** + * Used for split fields like timestamp -> date, time + * Called from DBItem Validate (before validation) to get back master field value from its sub_fields + * + * @param unknown_type $field + * @param unknown_type $value + * @param unknown_type $options + * @param unknown_type $object + */ + function UpdateMasterFields($field, $value, &$options, &$object) + { + + } + +/* function GetErrorMsg($pseudo_error, $options) + { + if ( isset($options['error_msgs'][$pseudo_error]) ) { + return $options['error_msgs'][$pseudo_error]; + } + else { + return $this->ErrorMsgs[$pseudo_error]; + } + }*/ + + function GetSample($field, &$options, &$object) + { + + } + +} + +class kOptionsFormatter extends kFormatter { + +//function Format($value, $options, &$errors) + function Format($value, $field_name, &$object, $format=null) + { + if ( is_null($value) ) return ''; + + $options = $object->GetFieldOptions($field_name); + if ( isset($format) ) $options['format'] = $format; + + $label = getArrayValue($options['options'], $value); + if( getArrayValue($options,'use_phrases') ) + { + return $this->Application->Phrase($label); + } + else + { + return $label; + } + } +} + +/** + * Replacement for kOptionsFormatter in case if options + * should be selected from database. Use this formatter + * only in case if formatter attached field is in edit form. + * + * For usage in grid just use LEFT JOIN clause to table + * where requested options are located. + */ +class kLEFTFormatter extends kFormatter { + +//function Format($value, $options, &$errors) + function Format($value, $field_name, &$object, $format=null) + { + if ( is_null($value) ) return ''; + + $options = $object->GetFieldOptions($field_name); + if ( isset($format) ) $options['format'] = $format; + + if( !isset($options['options'][$value]) ) + { + // required option is not defined in config => query for it + $db =& $this->Application->GetADODBConnection(); + $sql = sprintf($options['left_sql'],$options['left_title_field'],$options['left_key_field'],$value); + $options['options'][$value] = $db->GetOne($sql); + } + return $options['options'][$value]; + } + +//function Parse($value, $options, &$errors) + function Parse($value, $field_name, &$object) + { + if ($value == '') return NULL; + + $options = $object->GetFieldOptions($field_name); + if( !array_search($value,$options['options']) ) + { + // required option is not defined in config => query for it + $db =& $this->Application->GetADODBConnection(); + $sql = sprintf($options['left_sql'],$options['left_key_field'],$options['left_title_field'],$value); + $found = $db->GetOne($sql); + if($found !== false) $options['options'][$found] = $value; + } + else + { + $found = array_search($value,$options['options']); + } + if($found === false) $found = $options['default']; + return $found; + } +} + + +class kDateFormatter extends kFormatter { + +/* function kDateFormatter() + { + parent::kFormatter(); + $this->ErrorMsgs['bad_dformat'] = 'Please use correct date format (%s) ex. (%s)'; + } + */ + + function PrepareOptions($field_name, &$field_options, &$object) + { + $date_format = getArrayValue($field_options, 'date_format'); + $time_format = getArrayValue($field_options, 'time_format'); + + $language =& $this->Application->recallObject('lang.current'); + + if ($date_format === false) $date_format = $language->GetDBField('DateFormat'); + if ($time_format === false) $time_format = $language->GetDBField('TimeFormat'); + + if (!isset($field_options['date_time_separator'])) $field_options['date_time_separator'] = ' '; + $field_options['format'] = $date_format.$field_options['date_time_separator'].$time_format; + $field_options['sub_fields'] = Array('date' => $field_name.'_date', 'time' => $field_name.'_time'); + + $add_fields = Array(); + + $opts = Array('master_field' => $field_name, 'formatter'=>'kDateFormatter', 'format'=>$date_format); + if ( isset($field_options['default']) ) $opts['default'] = $field_options['default']; + if ( isset($field_options['required']) ) $opts['required'] = $field_options['required']; + + $add_fields[$field_name.'_date'] = $opts; + $opts['format'] = $time_format; + $add_fields[$field_name.'_time'] = $opts; + + if ( !isset($object->VirtualFields[$field_name]) ) { + // adding caluclated field to format date directly in the query + if ( !isset($object->CalculatedFields) || !is_array($object->CalculatedFields) ) { + $object->CalculatedFields = Array(); + } + $object->CalculatedFields[$field_name.'_formatted'] = 'FROM_UNIXTIME('.'`%1$s`.'.$field_name.', \''.$this->SQLFormat($field_options['format']).'\')'; + $opts['format'] = $field_options['format']; + $opts['required'] = 0; + unset($opts['master_field']); + $add_fields[$field_name.'_formatted'] = $opts; + } + + $add_fields = array_merge_recursive2($add_fields, $object->VirtualFields); + $object->setVirtualFields($add_fields); + } + + function UpdateSubFields($field, $value, &$options, &$object) + { + if ( $sub_fields = getArrayValue($options, 'sub_fields') ) { + if( isset($value) && $value ) + { + $object->SetDBField( $sub_fields['date'], $value ); + $object->SetDBField( $sub_fields['time'], $value ); + } + } + } + + function UpdateMasterFields($field, $value, &$options, &$object) + { + // when in master field - set own value from sub_fields + if ( $sub_fields = getArrayValue($options, 'sub_fields') ) { + // if date is not empty, but time is empty - set time to 0, otherwise master field fomratter will complain + // when we have only date field on form, we need time hidden field always empty, don't ask me why! + if ( $object->GetDBField($sub_fields['date']) != '' && $object->GetDBField($sub_fields['time']) == '' ) { + $empty_time = getArrayValue($options,'empty_time'); + if($empty_time === false) $empty_time = mktime(0,0,0); + $object->SetDBField($sub_fields['time'], $empty_time); + } + $object->SetField($field, $object->GetField($sub_fields['date']).$options['date_time_separator'].$object->GetField($sub_fields['time'])); + } + // when in one of sub_fields - call update for master_field to update its value from sub_fields [are you following ? :) ] + elseif ($master_field = getArrayValue($options, 'master_field') ) { + $this->UpdateMasterFields($master_field, null, $object->GetFieldOptions($master_field), $object); + } + } + +//function Format($value, $options, &$errors) + function Format($value, $field_name, &$object, $format=null) + { + if ( is_null($value) ) return ''; + if ( !is_numeric($value) ) return $value; // for leaving badly formatted date on the form + settype($value, 'int'); + if ( !is_int($value) ) return $value; + + $options = $object->GetFieldOptions($field_name); + if ( isset($format) ) $options['format'] = $format; + + return date($options['format'], $value); + } + + function HumanFormat($format) + { + $patterns = Array('/m/', + '/n/', + '/d/', + '/j/', + '/y/', + '/Y/', + '/h|H/', + '/g|G/', + '/i/', + '/s/', + '/a|A/'); + $replace = Array( 'mm', + 'm', + 'dd', + 'd', + 'yy', + 'yyyy', + 'hh', + 'h', + 'mm', + 'ss', + 'AM'); + $res = preg_replace($patterns, $replace, $format); + return $res; + } + + function SQLFormat($format) + { + $mapping = Array( + '/%/' => '%%', + '/(? '%p', // Lowercase Ante meridiem and Post meridiem => MySQL provides only uppercase + '/(? '%p', // Uppercase Ante meridiem and Post meridiem + '/(? '%d', // Day of the month, 2 digits with leading zeros + '/(? '%a', // A textual representation of a day, three letters + '/(? '%M', // A full textual representation of a month, such as January or March + '/(? '%l', // 12-hour format of an hour without leading zeros + '/(? '%k', // 24-hour format of an hour without leading zeros + '/(? '%h', // 12-hour format of an hour with leading zeros + '/(? '%H', // 24-hour format of an hour with leading zeros + '/(? '%i', // Minutes with leading zeros + '/(? 'N/A', // Whether or not the date is in daylights savings time + + '/(? 'N/A', // English ordinal suffix for the day of the month, 2 characters, see below + '/jS/' => '%D', // MySQL can't return separate suffix, but could return date with suffix + '/(? '%e', // Day of the month without leading zeros + '/(? '%W', // A full textual representation of the day of the week + '/(? 'N/A', // Whether it's a leap year + '/(? '%m', // Numeric representation of a month, with leading zeros + '/(? '%b', // A short textual representation of a month, three letters + '/(? '%c', // Numeric representation of a month, without leading zeros + '/(? 'N/A', // Difference to Greenwich time (GMT) in hours + '/(? 'N/A', // RFC 2822 formatted date + '/(? '%s', // Seconds, with leading zeros + // S and jS moved before j - see above + '/(? 'N/A', // Number of days in the given month + '/(? 'N/A', // Timezone setting of this machine + '/(? 'N/A', // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) + '/(? '%w', // Numeric representation of the day of the week + '/(? '%v', // ISO-8601 week number of year, weeks starting on Monday (added in PHP 4.1.0) + '/(? '%Y', // A full numeric representation of a year, 4 digits + '/(? '%y', // A two digit representation of a year + '/(? 'N/A', // The day of the year (starting from 0) => MySQL starts from 1 + '/(? 'N/A', // Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those east of UTC is always positive. + ); + + $patterns = array_keys($mapping); + $replacements = array_values($mapping); + + $res = preg_replace($patterns, $replacements, $format); + return $res; + } + +//function Parse($value, $options, &$errors) + function Parse($value, $field_name, &$object) + { + $options = $object->GetFieldOptions($field_name); + + $dt_separator = getArrayValue($options,'date_time_separator'); + if($dt_separator) $value = trim($value, $dt_separator); + if($value == '') return NULL; + //return strtotime($value); + + $format = $options['format']; + if($dt_separator) $format = trim($format, $dt_separator); + + $object->FieldErrors[$field_name]['params'] = Array( $this->HumanFormat($format), date($format) ); + $object->FieldErrors[$field_name]['value'] = $value; + + $hour = 0; + $minute = 0; + $second = 0; + $month = 1; + $day = 1; + $year = 1970; + + $patterns['n'] = '([0-9]{1,2})'; + $patterns['m'] = '([0-9]{1,2})'; + $patterns['d'] = '([0-9]{1,2})'; + $patterns['j'] = '([0-9]{1,2})'; + $patterns['Y'] = '([0-9]{4})'; + $patterns['y'] = '([0-9]{2})'; + $patterns['G'] = '([0-9]{1,2})'; + $patterns['g'] = '([0-9]{1,2})'; + $patterns['H'] = '([0-9]{2})'; + $patterns['h'] = '([0-9]{2})'; + $patterns['i'] = '([0-9]{2})'; + $patterns['s'] = '([0-9]{2})'; + $patterns['a'] = '(am|pm)'; + $patterns['A'] = '(AM|PM)'; + + $holders_mask = eregi_replace('[a-zA-Z]{1}', '([a-zA-Z]{1})', $format); + if (!ereg($holders_mask, $format, $holders)) { + $object->FieldErrors[$field_name]['pseudo'] = 'bad_date_format'; + return $value; + } + + $values_mask = '/^'.str_replace('/','\/',$format).'$/'; + foreach ($patterns as $key => $val) { + $values_mask = ereg_replace($key, $val, $values_mask); + } + // echo " values_mask : $values_mask
"; + + if (!preg_match($values_mask, $value, $values)) { + $object->FieldErrors[$field_name]['pseudo'] = 'bad_date_format'; + return $value; + } + + for ($i = 1; $i < count($holders); $i++) { + switch ($holders[$i]) { + case 'n': + case 'm': + $month = $values[$i]; + $month = ereg_replace("^0{1}", '', $month); + break; + case 'd': + $day = $values[$i]; + $day = ereg_replace("^0{1}", '', $day); + break; + case 'Y': + $year = $values[$i]; + break; + case 'y': + $year = $values[$i] >= 70 ? 1900 + $values[$i] : 2000 + $values[$i]; + break; + case 'H': + case 'h': + case 'G': + case 'g': + $hour = $values[$i]; + $hour = ereg_replace("^0{1}", '', $hour); + break; + case 'i': + $minute = $values[$i]; + $minute = ereg_replace("^0{1}", '', $minute); + break; + case 's': + $second = $values[$i]; + $second = ereg_replace("^0{1}", '', $second); + break; + case 'a': + case 'A': + if ($hour <= 12) { // if AM/PM used with 24-hour - could happen :) + if ($values[$i] == 'pm' || $values[$i] == 'PM') { + $hour += 12; + if ($hour == 24) $hour = 12; + } + elseif ($values[$i] == 'am' || $values[$i] == 'AM') { + if ($hour == 12) $hour = 0; + } + } + break; + } + } + + //echo "day: $day, month: $month, year: $year, hour: $hour, minute: $minute
"; + + /*if (!($year >= 1970 && $year <= 2037)) { + $object->FieldErrors[$field_name]['pseudo'] = 'bad_date_format'; + return $value; + }*/ + + if (!($month >= 1 && $month <= 12)) { + $object->FieldErrors[$field_name]['pseudo'] = 'bad_date_format'; + return $value; + } + + $months_days = Array ( 1 => 31,2 => 28, 3 => 31, 4 => 30,5 => 31,6 => 30, 7 => 31, 8 => 31,9 => 30,10 => 31,11 => 30,12 => 31); + if ($year % 4 == 0) $months_days[2] = 29; + + if (!($day >=1 && $day <= $months_days[$month])) { + $object->FieldErrors[$field_name]['pseudo'] = 'bad_date_format'; + return $value; + } + + if (!($hour >=0 && $hour <= 23)) { + $object->FieldErrors[$field_name]['pseudo'] = 'bad_date_format'; + return $value; + } + + if (!($minute >=0 && $minute <= 59)) { + $object->FieldErrors[$field_name]['pseudo'] = 'bad_date_format'; + return $value; + } + + if (!($second >=0 && $second <= 59)) { + $object->FieldErrors[$field_name]['pseudo'] = 'bad_date_format'; + return $value; + } + // echo "day: $day, month: $month, year: $year, hour: $hour, minute: $minute
"; + return (mktime($hour, $minute, $second, $month, $day, $year)); + } + + function GetSample($field, &$options, &$object) + { + return $this->Format( time(), $field, $object); + } +} + +class kUploadFormatter extends kFormatter +{ + var $DestinationPath; + var $FullPath; + + function kUploadFormatter() + { + if ($this->DestinationPath) + { + $this->FullPath = DOC_ROOT.BASE_PATH.$this->DestinationPath; + } + parent::kBase(); + } + + +//function Parse($value, $options, &$errors) + function Parse($value, $field_name, &$object) + { + $ret = ''; + $options = $object->GetFieldOptions($field_name); + + if (getArrayValue($value, 'upload') && getArrayValue($value, 'error') == UPLOAD_ERR_NO_FILE) + { + return getArrayValue($value, 'upload'); + } + + if ( is_array($value) && $value['size'] ) + { + if ( is_array($value) && $value['error'] === UPLOAD_ERR_OK ) + { + if ( !in_array($value['type'], $options['allowed_types']) ) + { + $object->FieldErrors[$field_name]['pseudo'] = 'bad_file_format'; + } + elseif ( $value['size'] > ($options['max_size'] ? $options['max_size'] : MAX_UPLOAD_SIZE) ) + { + $object->FieldErrors[$field_name]['pseudo'] = 'bad_file_size'; + } + elseif ( !is_writable($this->FullPath) ) + { + $object->FieldErrors[$field_name]['pseudo'] = 'cant_save_file'; + } + else + { + $real_name = $this->ValidateFileName($this->FullPath, $value['name']); + $file_name = $this->FullPath.$real_name; + if ( !move_uploaded_file($value['tmp_name'], $file_name) ) + { + $object->FieldErrors[$field_name]['pseudo'] = 'cant_save_file'; + } + else + { + $ret = $this->DestinationPath.$real_name; + } + } + } + else + { + $object->FieldErrors[$field_name]['pseudo'] = 'cant_save_file'; + } + } + + if ($value['error'] && !( $value['error'] == UPLOAD_ERR_NO_FILE ) && !$object->FieldErrors[$field_name]['pseudo']) + { + $object->FieldErrors[$field_name]['pseudo'] = 'cant_save_file'; + } + + return $ret; + } + + function ValidateFileName($path, $name) + { + $parts = pathinfo($name); + $ext = '.'.$parts['extension']; + $filename = substr($parts['basename'], 0, -strlen($ext)); + $new_name = $filename.$ext; + while ( file_exists($path.'/'.$new_name) ) + { + if ( preg_match("/({$filename}_)([0-9]*)($ext)/", $new_name, $regs) ) { + $new_name = $regs[1].($regs[2]+1).$regs[3]; + } + else { + $new_name = $filename.'_1'.$ext; + } + } + return $new_name; + } + +} + +class kPictureFormatter extends kUploadFormatter +{ + + function kPictureFormatter() + { + $this->NakeLookupPath = IMAGES_PATH; + $this->DestinationPath = IMAGES_PENDING_PATH; + parent::kUploadFormatter(); + } + +} + +class kMultiLanguage extends kFormatter +{ + + function LangFieldName($field_name) + { + $lang = $this->Application->GetVar('m_lang'); + return 'l'.$lang.'_'.$field_name; + } + + function PrepareOptions($field_name, &$field_options, &$object) + { + if (getArrayValue($object->Fields, $field_name, 'master_field')) return; + + $lang_field_name = $this->LangFieldName($field_name); + + //substitude title field + $title_field = $this->Application->getUnitOption($object->Prefix, 'TitleField'); + if ($title_field == $field_name) { + $this->Application->setUnitOption($object->Prefix, 'TitleField', $lang_field_name); + } + + //substitude fields + $fields = $this->Application->getUnitOption($object->Prefix, 'Fields'); + if ( isset($fields[$field_name]) ) { + + $fields[$lang_field_name] = $fields[$field_name]; + $fields[$lang_field_name]['master_field'] = $field_name; + $object->Fields[$lang_field_name] = $fields[$lang_field_name]; + $fields[$field_name]['required'] = false; + $object->Fields[$field_name]['required'] = false; + $object->VirtualFields[$field_name] = $object->Fields[$field_name]; + } + $this->Application->setUnitOption($object->Prefix, 'Fields', $fields); + + //substitude virtual fields + $virtual_fields = $this->Application->getUnitOption($object->Prefix, 'VirtualFields'); + if ( isset($virtual_fields[$field_name]) ) { + $virtual_fields[$lang_field_name] = $virtual_fields[$field_name]; + $virtual_fields[$lang_field_name]['master_field'] = $field_name; + $object->VirtualFields[$lang_field_name] = $virtual_fields[$lang_field_name]; + $virtual_fields[$field_name]['required'] = false; + $object->VirtualFields[$field_name]['required'] = false; + } + $this->Application->setUnitOption($object->Prefix, 'VirtualFields', $virtual_fields); + + //substitude grid fields + $grids = $this->Application->getUnitOption($object->Prefix, 'Grids'); + foreach ($grids as $name => $grid) { + if ( getArrayValue($grid, 'Fields', $field_name) ) { + array_rename_key($grids[$name]['Fields'], $field_name, $lang_field_name); + } + } + $this->Application->setUnitOption($object->Prefix, 'Grids', $grids); + + //substitude default sortings + $sortings = $this->Application->getUnitOption($object->Prefix, 'ListSortings'); + foreach ($sortings as $special => $the_sortings) { + if (isset($the_sortings['ForcedSorting'])) { + array_rename_key($sortings[$special]['ForcedSorting'], $field_name, $lang_field_name); + } + if (isset($the_sortings['Sorting'])) { + array_rename_key($sortings[$special]['Sorting'], $field_name, $lang_field_name); + } + } + $this->Application->setUnitOption($object->Prefix, 'ListSortings', $sortings); + + //TODO: substitude possible language-fields sortings after changing language + } + + /*function UpdateSubFields($field, $value, &$options, &$object) + { + + } + + function UpdateMasterFields($field, $value, &$options, &$object) + { + + }*/ + + function Format($value, $field_name, &$object, $format=null) + { + $master_field = getArrayValue($object->Fields, $field_name, 'master_field'); + if (!$master_field) { // if THIS field is master it does NOT have reference to it's master_field + $lang = $this->Application->GetVar('m_lang'); + $value = $object->GetDBField('l'.$lang.'_'.$field_name); //getting value of current language + $master_field = $field_name; // THIS is master_field + } + if ( $value == '' && $format != 'no_default') { // try to get default language value + $def_lang_value = $object->GetDBField('l'.$this->Application->GetDefaultLanguageId().'_'.$master_field); + if ($def_lang_value == '') return NULL; + return $def_lang_value; //return value from default language + } + return $value; + } + + function Parse($value, $field_name, &$object) + { + $lang = $this->Application->GetVar('m_lang'); + $def_lang = $this->Application->GetDefaultLanguageId(); + $master_field = getArrayValue($object->Fields, $field_name, 'master_field'); + + if ( getArrayValue($object->Fields, $field_name, 'required') && ( (string) $value == '' ) ) { + $object->FieldErrors[$master_field]['pseudo'] = 'required'; + }; + + if (!$this->Application->GetVar('allow_translation') && $lang != $def_lang && getArrayValue($object->Fields, $field_name, 'required')) { + $def_lang_field = 'l'.$def_lang.'_'.$master_field; + if ( !$object->ValidateRequired($def_lang_field, $object->Fields[$field_name]) ) { + $object->FieldErrors[$master_field]['pseudo'] = 'primary_lang_required'; + } + } + + if ($value == '') return NULL; + return $value; + } + +} + + +class kPasswordFormatter extends kFormatter +{ + + function PrepareOptions($field_name, &$field_options, &$object) + { + if( isset( $field_options['verify_field'] ) ) + { + $add_fields = Array(); + $options = Array('master_field' => $field_name, 'formatter'=>'kPasswordFormatter'); + $add_fields[ $field_options['verify_field'] ] = $options; + + $add_fields[$field_name.'_plain'] = Array('type'=>'string', 'error_field'=>$field_name); + $add_fields[ $field_options['verify_field'].'_plain' ] = Array('type'=>'string', 'error_field'=>$field_options['verify_field'] ); + + $add_fields = array_merge_recursive2($add_fields, $object->VirtualFields); + $object->setVirtualFields($add_fields); + } + } + + function Format($value, $field_name, &$object, $format=null) + { + return $value; + } + + function Parse($value, $field_name, &$object) + { + $options = $object->GetFieldOptions($field_name); + + $fields = Array('master_field','verify_field'); + $fields_set = true; + $flip_count = 0; + while($flip_count < 2) + { + if( getArrayValue($options,$fields[0]) ) + { + $object->SetDBField($field_name.'_plain', $value); + if( !getArrayValue($object->Fields[ $options[ $fields[0] ] ], $fields[1].'_set') ) + { + $object->Fields[ $options[ $fields[0] ] ][$fields[1].'_set'] = true; + } + + $password_field = $options[ $fields[0] ]; + $verify_field = $field_name; + } + $fields = array_reverse($fields); + $flip_count++; + } + + if( getArrayValue($object->Fields[$password_field], 'verify_field_set') && getArrayValue($object->Fields[$verify_field], 'master_field_set') ) + { + $new_password = $object->GetDBField($password_field.'_plain'); + $verify_password = $object->GetDBField($verify_field.'_plain'); + + if($new_password == '' && $verify_password == '') + { + if( $object->GetDBField($password_field) != $this->EncryptPassword('') ) + { + return $this->EncryptPassword($value); + } + else + { + $object->Fields[$password_field.'_plain']['required'] = true; + $object->Fields[$verify_field.'_plain']['required'] = true; + return null; + } + } + + $min_length = $this->Application->ConfigValue('Min_Password'); + if( strlen($new_password) >= $min_length ) + { + if($new_password != $verify_password) + { + $object->ErrorMsgs['passwords_do_not_match'] = $this->Application->Phrase('lu_passwords_do_not_match'); + $object->FieldErrors[$password_field]['pseudo'] = 'passwords_do_not_match'; + $object->FieldErrors[$verify_field]['pseudo'] = 'passwords_do_not_match'; + } + } + else + { + $object->FieldErrors[$password_field]['pseudo'] = 'length_out_of_range'; + $object->FieldErrors[$verify_field]['pseudo'] = 'length_out_of_range'; + } + } + if($value == '') return $object->GetDBField($field_name); + return $this->EncryptPassword($value); + } + + function EncryptPassword($value) + { + return md5($value); + } +} + + + /** + * Credit card expiration date formatter + * + */ + class kCCDateFormatter extends kFormatter + { + function PrepareOptions($field_name, &$field_options, &$object) + { + $add_fields = Array(); + + $i = 1; + $options = Array('00' => ''); + while($i <= 12) + { + $options[ sprintf('%02d',$i) ] = sprintf('%02d',$i); + $i++; + } + $add_fields[ $field_options['month_field'] ] = Array('formatter'=>'kOptionsFormatter', 'options' => $options, 'not_null' => true, 'default' => '00'); + $add_fields[ $field_options['year_field'] ] = Array('type' => 'string', 'default' => ''); + + $add_fields = array_merge_recursive2($add_fields, $object->VirtualFields); + $object->setVirtualFields($add_fields); + } + + function UpdateSubFields($field, $value, &$options, &$object) + { + if(!$value) return false; + $date = explode('/', $value); + $object->SetDBField( $options['month_field'], $date[0] ); + $object->SetDBField( $options['year_field'], $date[1] ); + } + + /** + * Will work in future if we could attach 2 formatters to one field + * + * @param string $value + * @param string $field_name + * @param kBase $object + * @return string + */ + function Parse($value, $field_name, &$object) + { +// if ( is_null($value) ) return ''; + + $options = $object->GetFieldOptions($field_name); + + $month = $object->GetDirtyField($options['month_field']); + $year = $object->GetDirtyField($options['year_field']); + + if( !(int)$month && !(int)$year ) return NULL; + $is_valid = ($month >= 1 && $month <= 12) && ($year >= 0 && $year <= 99); + + if(!$is_valid) $object->FieldErrors[$field_name]['pseudo'] = 'bad_type'; + return $month.'/'.$year; + } + + } + + +?> Index: trunk/core/kernel/db/db_connection.php =================================================================== diff -u -N --- trunk/core/kernel/db/db_connection.php (revision 0) +++ trunk/core/kernel/db/db_connection.php (revision 1560) @@ -0,0 +1,542 @@ +dbType = $dbType; + $this->initMetaFunctions(); + if(!$errorHandler) + { + $this->errorHandler = Array(&$this,'handleError'); + } + else + { + $this->errorHandler=$errorHandler; + } + } + + /** + * Set's custom error + * + * @param int $code + * @param string $msg + * @access public + */ + function setError($code,$msg) + { + $this->errorCode=$code; + $this->errorMessage=$msg; + } + + /** + * Checks if previous query execution + * raised an error. + * + * @return bool + * @access public + */ + function hasError() + { + return !($this->errorCode == 0); + } + + /** + * Caches function specific to requested + * db type + * + * @access private + */ + function initMetaFunctions() + { + $ret = Array(); + switch($this->dbType) + { + case 'mysql': + $ret = Array(); // only define functions, that name differs from "dbType_" + + break; + + + } + $this->metaFunctions = $ret; + } + + /** + * Get's function for specific db type + * based on it's meta name + * + * @param string $name + * @return string + * @access private + */ + function getMetaFunction($name) + { + if( !isset($this->metaFunctions[$name]) ) + { + if(function_exists($this->dbType.'_'.$name)) return $this->dbType.'_'.$name; + } + else + { + return $this->dbType.$name; + } + return false; + } + + + /** + * Try to connect to database server + * using specified parameters and set + * database to $db if connection made + * + * @param string $host + * @param string $user + * @param string $pass + * @param string $db + * @access public + */ + function Connect($host,$user,$pass,$db,$force_new=false) + { + $func = $this->getMetaFunction('connect'); + $this->connectionID = $func($host,$user,$pass,$force_new) or die('Can\'t connect to db'); + if($this->connectionID) + { + $this->setDB($db); + $this->showError(); + } + } + + function ReConnect($host,$user,$pass,$db) + { + $func = $this->getMetaFunction('close'); + $func($this->connectionID); + $this->Connect($host,$user,$pass,$db); + } + + /** + * Shows error message from previous operation + * if it failed + * + * @access private + */ + function showError($sql='') + { + $this->setError(0,''); // reset error + if($this->connectionID) + { + $func = $this->getMetaFunction('errno'); $this->errorCode = $func($this->connectionID); + if($this->hasError()) + { + $func = $this->getMetaFunction('error'); $this->errorMessage = $func($this->connectionID); + if(is_array($this->errorHandler)) + { + $func = $this->errorHandler[1]; + $ret = $this->errorHandler[0]->$func($this->errorCode,$this->errorMessage,$sql); + } + else + { + $func = $this->errorHandler; + $ret = $func($this->errorCode,$this->errorMessage,$sql); + } + if(!$ret) exit; + } + } + } + + /** + * Default error handler for sql errors + * + * @param int $code + * @param string $msg + * @param string $sql + * @return bool + * @access private + */ + function handleError($code,$msg,$sql) + { + echo 'Processing SQL: '.$sql.'
'; + echo 'Error ('.$code.'): '.$msg.'
'; + return false; + } + + /** + * Set's database name for connection + * to $new_name + * + * @param string $new_name + * @return bool + * @access public + */ + function setDB($new_name) + { + if(!$this->connectionID) return false; + $func = $this->getMetaFunction('select_db'); + return $func($new_name); + } + + /** + * Returns first field of first line + * of recordset if query ok or false + * otherwise + * + * @param string $sql + * @return string + * @access public + */ + function GetOne($sql) + { + $row = $this->GetRow($sql); + if(!$row) return false; + + return array_shift($row); + } + + /** + * Returns first row of recordset + * if query ok, false otherwise + * + * @param stirng $sql + * @return Array + * @access public + */ + function GetRow($sql) + { + $sql .= ' '.$this->getLimitClause(0,1); + $ret = $this->Query($sql); + if(!$ret) return false; + + return array_shift($ret); + } + + /** + * Returns 1st column of recordset as + * one-dimensional array or false otherwise + * Optional parameter $key_field can be used + * to set field name to be used as resulting + * array key + * + * @param string $sql + * @param string $key_field + * @return Array + * @access public + */ + function GetCol($sql, $key_field = null) + { + $rows = $this->Query($sql); + if(!$rows) return $rows; + + $i = 0; $row_count = count($rows); + $ret = Array(); + if(isset($key_field)) + { + while ($i < $row_count) + { + $ret[$rows[$i][$key_field]] = array_shift($rows[$i]); + $i++; + } + } + else + { + while ($i < $row_count) + { + $ret[] = array_shift($rows[$i]); + $i++; + } + } + return $ret; + } + + /** + * Queries db with $sql query supplied + * and returns rows selected if any, false + * otherwise. Optional parameter $key_field + * allows to set one of the query fields + * value as key in string array. + * + * @param string $sql + * @param string $key_field + * @return Array + */ + function Query($sql,$key_field = null) + { + if($this->debugMode) return $this->debugQuery($sql,$key_field); + $query_func = $this->getMetaFunction('query'); + $this->queryID = $query_func($sql,$this->connectionID); + if( is_resource($this->queryID) ) + { + $ret = Array(); + $fetch_func = $this->getMetaFunction('fetch_assoc'); + if( isset($key_field) ) + { + while( ($row = $fetch_func($this->queryID)) ) + { + $ret[$row[$key_field]] = $row; + } + } + else + { + while( ($row = $fetch_func($this->queryID)) ) + { + $ret[] = $row; + } + } + $this->Destroy(); + return $ret; + } + $this->showError($sql); + return false; + } + + function ChangeQuery($sql) + { + $this->Query($sql); + return $this->errorCode==0 ? true : false; + } + + function debugQuery($sql, $key_field = null) + { + global $debugger; + $query_func = $this->getMetaFunction('query'); + + // set 1st checkpoint: begin + $isSkipTable=true; + $profileSQLs=defined('DBG_SQL_PROFILE')&&DBG_SQL_PROFILE; + if($profileSQLs) + { + $isSkipTable=isSkipTable($sql); + if(!$isSkipTable) + { + $queryID=$debugger->generateID(); + $debugger->profileStart('sql_'.$queryID, $debugger->formatSQL($sql) ); + } + } + // set 1st checkpoint: end + + $this->queryID = $query_func($sql,$this->connectionID); + + // set 2nd checkpoint: begin + if(!$isSkipTable) { + $debugger->profileFinish('sql_'.$queryID); + $debugger->profilerAddTotal('sql', 'sql_'.$queryID); + } + // set 2nd checkpoint: end + + if( is_resource($this->queryID) ) + { + $ret = Array(); + $fetch_func = $this->getMetaFunction('fetch_assoc'); + if( isset($key_field) ) + { + while( ($row = $fetch_func($this->queryID)) ) + { + $ret[$row[$key_field]] = $row; + } + } + else + { + while( ($row = $fetch_func($this->queryID)) ) + { + $ret[] = $row; + } + } + $this->Destroy(); + return $ret; + } + $this->showError($sql); + return false; + } + + /** + * Free memory used to hold recordset handle + * + * @access private + */ + function Destroy() + { + if($this->queryID) + { + $free_func = $this->getMetaFunction('free_result'); + $free_func($this->queryID); + $this->queryID = null; + } + } + + /** + * Returns auto increment field value from + * insert like operation if any, zero otherwise + * + * @return int + * @access public + */ + function getInsertID() + { + $func = $this->getMetaFunction('insert_id'); + return $func($this->connectionID); + } + + /** + * Returns row count affected by last query + * + * @return int + * @access public + */ + function getAffectedRows() + { + $func = $this->getMetaFunction('affected_rows'); + return $func($this->connectionID); + } + + /** + * Returns LIMIT sql clause part for specific db + * + * @param int $offset + * @param int $rows + * @return string + * @access private + */ + function getLimitClause($offset, $rows) + { + if(!($rows > 0)) return ''; + + switch ($this->dbType) { + + default: + return 'LIMIT '.$offset.','.$rows; + break; + } + } + + /** + * Correctly quotes a string so that all strings are escaped. We prefix and append + * to the string single-quotes. + * An example is $db->qstr("Don't bother",magic_quotes_runtime()); + * + * @param s the string to quote + * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc(). + * This undoes the stupidity of magic quotes for GPC. + * + * @return quoted string to be sent back to database + */ + function qstr($s,$magic_quotes=false) + { + $replaceQuote = "\\'"; + if (!$magic_quotes) + { + if ($replaceQuote[0] == '\\') + { + // only since php 4.0.5 + $s = str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s); + //$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s)); + } + return "'".str_replace("'",$replaceQuote,$s)."'"; + } + + // undo magic quotes for " + $s = str_replace('\\"','"',$s); + + if($replaceQuote == "\\'") // ' already quoted, no need to change anything + { + return "'$s'"; + } + else // change \' to '' for sybase/mssql + { + $s = str_replace('\\\\','\\',$s); + return "'".str_replace("\\'",$replaceQuote,$s)."'"; + } + } + + /** + * Returns last error code occured + * + * @return int + */ + function getErrorCode() + { + return $this->errorCode; + } + + /** + * Returns last error message + * + * @return string + * @access public + */ + function getErrorMsg() + { + return $this->errorMessage; + } + } +?> \ No newline at end of file Index: trunk/core/kernel/db/db_tag_processor.php =================================================================== diff -u -N --- trunk/core/kernel/db/db_tag_processor.php (revision 0) +++ trunk/core/kernel/db/db_tag_processor.php (revision 1560) @@ -0,0 +1,1195 @@ +Conn =& $this->Application->GetADODBConnection(); + } + + + function IsNew($params) + { + $object =& $this->Application->recallObject( $this->getPrefixSpecial(), $this->Prefix, $params ); + return $object->GetID(); + } + + /** + * Returns view menu name for current prefix + * + * @param Array $params + * @return string + */ + function GetItemName($params) + { + $item_name = $this->Application->getUnitOption($this->Prefix,'ViewMenuPhrase'); + return $this->Application->Phrase($item_name); + } + + function ViewMenu($params) + { + $block_params = $params; + unset($block_params['block']); + $block_params['name'] = $params['block']; + + $list =& $this->GetList($params); + $block_params['PrefixSpecial'] = $list->getPrefixSpecial(); + return $this->Application->ParseBlock($block_params); + } + + + function SearchKeyword($params) + { + $list =& $this->GetList($params); + return $this->Application->RecallVar($list->getPrefixSpecial().'_search_keyword'); + } + + /** + * Draw filter menu content (for ViewMenu) based on filters defined in config + * + * @param Array $params + * @return string + */ + function DrawFilterMenu($params) + { + $block_params = $this->prepareTagParams($params); + $block_params['name'] = $params['spearator_block']; + $separator = $this->Application->ParseBlock($block_params); + $filter_menu = $this->Application->getUnitOption($this->Prefix,'FilterMenu'); + + // Params: label, filter_action, filter_status + $block_params['name'] = $params['item_block']; + + $view_filter = $this->Application->RecallVar($this->getPrefixSpecial().'_view_filter'); + if($view_filter === false) + { + $event_params = Array('prefix'=>$this->Prefix,'special'=>$this->Special,'name'=>'OnRemoveFilters'); + $this->Application->HandleEvent( new kEvent($event_params) ); + $view_filter = $this->Application->RecallVar($this->getPrefixSpecial().'_view_filter'); + } + $view_filter = unserialize($view_filter); + + $filters = Array(); + $prefix_special = $this->getPrefixSpecial(); + foreach($filter_menu['Filters'] as $filter_key => $filter_params) + { + if(!$filter_params) + { + $filters[] = $separator; + continue; + } + + $block_params['label'] = addslashes( $this->Application->Phrase($filter_params['label']) ); + if( getArrayValue($view_filter,$filter_key) ) + { + $submit = 0; + $status = 1; + } + else + { + $submit = 1; + $status = 0; + } + $block_params['filter_action'] = 'set_filter("'.$prefix_special.'","'.$filter_key.'","'.$submit.'");'; + $block_params['filter_status'] = $status; + $filters[] = $this->Application->ParseBlock($block_params); + } + return implode('', $filters); + } + + function IterateGridFields($params) + { + $mode = $params['mode']; + $def_block = $params['block']; + + $grids = $this->Application->getUnitOption($this->Prefix,'Grids'); + $grid_config = $grids[$params['grid']]['Fields']; + + $std_params['pass_params']='true'; + $std_params['PrefixSpecial']=$this->getPrefixSpecial(); + + $o = ''; + foreach ($grid_config as $field => $options) { + $block_params = Array(); + $block_params['name'] = isset($options[$mode.'_block']) ? $options[$mode.'_block'] : $def_block; + $block_params['field'] = $field; + $block_params['sort_field'] = isset($options['sort_field']) ? $options['sort_field'] : $field; + $block_params = array_merge($std_params, $block_params, $options); + $o.= $this->Application->ParseBlock($block_params, 1); + } + return $o; + } + + /** + * Prints list content using block specified + * + * @param Array $params + * @return string + * @access public + */ + function PrintList($params) + { +// $list =& $this->Application->recallObject( $this->getPrefixSpecial(), $this->Prefix.'_List',$params); + $list =& $this->GetList($params); + $id_field = $this->Application->getUnitOption($this->Prefix,'IDField'); + + $list->Query(); + $o = ''; + $list->GoFirst(); + + $block_params=$this->prepareTagParams($params); + $block_params['name'] = $this->SelectParam($params, 'render_as,block'); + $block_params['pass_params'] = 'true'; + + while (!$list->EOL()) + { + $this->Application->SetVar( $this->getPrefixSpecial().'_id', $list->GetDBField($id_field) ); // for edit/delete links using GET + $o.= $this->Application->ParseBlock($block_params, 1); + $list->GoNext(); + } + + $this->Application->SetVar( $this->getPrefixSpecial().'_id', ''); + return $o; + } + + function InitList($params) + { + $list_name = $params['list_name']; + + $names_mapping = $this->Application->GetVar('NamesToSpecialMapping'); + + if( !getArrayValue($names_mapping,$list_name) ) + { + $list =& $this->GetList($params); + } + } + + function BuildListSpecial($params) + { + return $this->Special; + } + + /** + * Enter description here... + * + * @param unknown_type $params + * @return kDBList + */ + function &GetList($params) + { + $list_name = $this->SelectParam($params, 'list_name,name'); + if (!$list_name) { + $list_name = $this->Application->Parser->GetParam('list_name'); + } + + $requery = getArrayValue($params, 'requery'); + if ($list_name && !$requery){ + $names_mapping = $this->Application->GetVar('NamesToSpecialMapping'); + + $special = getArrayValue($names_mapping, $list_name); + if(!$special) + { + $special = $this->BuildListSpecial($params); + } + } + else + { + $special = $this->BuildListSpecial($params); + } + + $prefix_special = rtrim($this->Prefix.'.'.$special, '.'); + $params['skip_counting'] = true; + $list =& $this->Application->recallObject( $prefix_special, $this->Prefix.'_List',$params); + $list->Query(); + $this->Special = $special; + + if ($list_name) { + $names_mapping[$list_name] = $special; + $this->Application->SetVar('NamesToSpecialMapping', $names_mapping); + } + + return $list; + } + + function ListMarker($params) + { + $list =& $this->GetList($params); + return $list->getPrefixSpecial(); + } + + function SubmitName($params) + { + $list =& $this->GetList($params); + + $prefix_special = $list->getPrefixSpecial(); + + return 'events['.$prefix_special.']['.$params['event'].']'; + } + + + function CombinedSortingDropDownName($params) + { + $list =& $this->GetList($params); + $prefix_special = $list->getPrefixSpecial(); + + return $prefix_special.'_CombinedSorting'; + } + + function SortingSelected($params) + { + $list =& $this->GetList($params); + $user_sorting_start = $this->getUserSortIndex(); + + $sorting = strtolower($list->GetOrderField($user_sorting_start).'|'.$list->GetOrderDirection($user_sorting_start)); + + if ($sorting == strtolower($params['sorting'])) return $params['selected']; + } + + + + /** + * Prints list content using block specified + * + * @param Array $params + * @return string + * @access public + */ + function PrintList2($params) + { + $per_page = $this->SelectParam($params, 'per_page,max_items'); + if ($per_page !== false) $params['per_page'] = $per_page; + + $list =& $this->GetList($params); + $o = ''; + + $direction = (isset($params['direction']) && $params['direction']=="H")?"H":"V"; + $columns = (isset($params['columns'])) ? $params['columns'] : 1; + $id_field = (isset($params['id_field'])) ? $params['id_field'] : $this->Application->getUnitOption($this->Prefix, 'IDField'); + + if ($columns>1 && $direction=="V") { + $list->Records = $this->LinearToVertical($list->Records, $columns, $list->GetPerPage()); + $list->SelectedCount=count($list->Records); + ksort($list->Records); + } + + $list->GoFirst(); + + $block_params=$this->prepareTagParams($params); + $block_params['name']=$this->SelectParam($params, 'render_as,block'); + $block_params['pass_params']='true'; + + $block_start_row_params=$this->prepareTagParams($params); + $block_start_row_params['name'] = $this->SelectParam($params, 'row_start_render_as,block_row_start,row_start_block'); + + $block_end_row_params=$this->prepareTagParams($params); + $block_end_row_params['name'] = $this->SelectParam($params, 'row_end_render_as,block_row_end,row_end_block'); + + $block_empty_cell_params = $this->prepareTagParams($params); + $block_empty_cell_params['name'] = $this->SelectParam($params, 'empty_cell_render_as,block_empty_cell,empty_cell_block'); + + $i=0; + + $backup_id=$this->Application->GetVar($this->Prefix."_id"); + $displayed = array(); + while (!$list->EOL()) + { + $this->Application->SetVar( $this->getPrefixSpecial().'_id', $list->GetDBField($id_field) ); // for edit/delete links using GET + $this->Application->SetVar( $this->Prefix.'_id', $list->GetDBField($id_field) ); + + if ($i % $columns == 0) { + $o.= $block_start_row_params['name'] ? $this->Application->ParseBlock($block_start_row_params, 1) : ''; + } + + if (!$list->getCurrentRecord()){ + $o.= $block_empty_cell_params['name'] ? $this->Application->ParseBlock($block_params, 1) : ' '; + } + else { + $o.= $this->Application->ParseBlock($block_params, 1); + array_push($displayed, $list->GetDBField($id_field)); + } + + if (($i+1) % $columns == 0) { + $o.= $block_end_row_params['name'] ? $this->Application->ParseBlock($block_end_row_params, 1) : ''; + } + + $list->GoNext(); + + $i++; + } + $cur_displayed = $this->Application->GetVar($this->Prefix.'_displayed_ids'); + if (!$cur_displayed) { + $cur_displayed = Array(); + } + else { + $cur_displayed = explode(',', $cur_displayed); + } + + $displayed = array_unique(array_merge($displayed, $cur_displayed)); + $this->Application->SetVar($this->Prefix.'_displayed_ids', implode(',',$displayed)); + + $this->Application->SetVar( $this->Prefix.'_id', $backup_id); + $this->Application->SetVar( $this->getPrefixSpecial().'_id', ''); + + if (isset($params['more_link_render_as'])) { + $block_params = $params; + $params['render_as'] = $params['more_link_render_as']; + $o .= $this->MoreLink($params); + } + + return $o; + } + + function MoreLink($params) + { + $per_page = $this->SelectParam($params, 'per_page,max_items'); + if ($per_page !== false) $params['per_page'] = $per_page; + $list =& $this->GetList($params); + if ($list->PerPage < $list->RecordsCount) { + $block_params = array(); + $block_params['name'] = $this->SelectParam($params, 'render_as,block'); + return $this->Application->ParseBlock($block_params, 1); + } + } + + function NotLastItem($params) + { + $list =& $this->Application->recallObject($this->getPrefixSpecial()); + + return ($list->CurrentIndex < min($list->PerPage, $list->RecordsCount)-1); + } + + function PageLink($params) + { + $t = isset($params['template']) ? $param['template'] : ''; + if (!$t) $t = $this->Application->GetVar('t'); + + if (isset($params['page'])) { + $this->Application->SetVar($this->getPrefixSpecial().'_Page', $params['page']); + } + + $pass = Array('pass' => 'all,'.$this->getPrefixSpecial()); +// $pass = Array('pass' => 'all'); + $http_query =& $this->Application->recallObject('HTTPQuery'); + $get = $http_query->Get; + unset($get['env']); +// $pass = array_merge($get, $pass); + + return $this->Application->HREF($t, '', $pass); + } + + function ColumnWidth($params) + { + $columns = $this->Application->Parser->GetParam('columns'); + return round(100/$columns).'%'; + } + + /** + * Append prefix and special to tag + * params (get them from tagname) like + * they were really passed as params + * + * @param Array $tag_params + * @return Array + * @access protected + */ + function prepareTagParams($tag_params=Array()) + { + $ret=$tag_params; + $ret['Prefix']=$this->Prefix; + $ret['prefix']=$this->Prefix; + $ret['Special']=$this->Special; + $ret['special']=$this->Special; + $ret['PrefixSpecial']=$this->getPrefixSpecial(); + return $ret; + } + + function GetISO($currency) + { + if ($currency == 'selected') { + $iso = $this->Application->RecallVar('curr_iso'); + } + elseif ($currency == 'primary' || $currency == '') { + $iso = $this->Application->GetPrimaryCurrency(); + } + else { //explicit currency + $iso = $currency; + } + return $iso; + } + + function ConvertCurrency($value, $iso) + { + $converter =& $this->Application->recallObject('kCurrencyRates'); + // convery primary currency to selected (if they are the same, converter will just return) + $value = $converter->Convert($value, 'PRIMARY', $iso); + return $value; + } + + function AddCurrencySymbol($value, $iso) + { + $this->Application->setUnitOption('curr', 'AutoLoad', false); + $currency =& $this->Application->recallObject('curr.-'.$iso); + if (!$currency->Loaded) { + $currency->Load($iso, 'ISO'); + } + + $symbol = $currency->GetDBField('Symbol'); + if (!$symbol) $symbol = $currency->GetDBField('ISO').' '; + if ($currency->GetDBField('SymbolPosition') == 0) { + $value = $symbol.$value; + } + if ($currency->GetDBField('SymbolPosition') == 1) { + $value = $value.$symbol; + } + return $value; + } + + /** + * Get's requested field value + * + * @param Array $params + * @return string + * @access public + */ + function Field($params) + { + $field = $this->SelectParam($params, 'name,field'); + + if( !$this->Application->IsAdmin() ) $params['no_special'] = 'no_special'; + + $object =& $this->Application->recallObject($this->getPrefixSpecial(),$this->Prefix, $params); + + if ( getArrayValue($params, 'db') !== false ) + { + $value = $object->GetDBField($field); + } + else + { + if (isset($params['currency'])) { + $iso = $this->GetISO($params['currency']); + $original = $object->GetDBField($field); + $value = $this->ConvertCurrency($original, $iso); + $object->SetDBField($field, $value); + $object->Fields[$field]['converted'] = true; + } + + $format = getArrayValue($params, 'format'); + if( !$format || $format == '$format' ) + { + $format = null; + } + else + { + if(preg_match("/_regional_(.*)/", $format, $regs)) + { + $lang =& $this->Application->recallObject('lang.current'); + $format = $lang->GetDBField($regs[1]); + } + } + $value = $object->GetField($field, $format); + + if (isset($params['currency'])) { + $value = $this->AddCurrencySymbol($value, $iso); + $params['no_special'] = 1; + } + } + + if( getArrayValue($params,'nl2br' ) ) $value = nl2br($value); + if( !$this->HasParam($params, 'no_special') ) $value = htmlspecialchars($value); + if( getArrayValue($params,'checked' ) ) $value = ($value == 1) ? 'checked' : ''; + if( getArrayValue($params,'as_label') ) $value = $this->Application->Phrase($value); + + $first_chars = $this->SelectParam($params,'first_chars,cut_first'); + if($first_chars) + { + $needs_cut = strlen($value) > $first_chars; + $value = substr($value,0,$first_chars); + if($needs_cut) $value .= ' ...'; + } + + if ($value != '') $this->Application->Parser->DataExists = true; + + if (isset($params['currency'])) { + //restoring value in original currency, for other Field tags to work properly + $object->SetDBField($field, $original); + } + + return $value; + } + + function HasParam($params, $param_name) + { + $value = getArrayValue($params, $param_name); + return $value && ($value != '$'.$param_name); + } + + function PhraseField($params) + { + $field_label = $this->Field($params); + $translation = $this->Application->Phrase( $field_label ); + return $translation; + } + + function Error($params) + { + $field = $params['field']; + $object =& $this->Application->recallObject($this->getPrefixSpecial(),$this->Prefix, $params); + + $msg = $object->GetErrorMsg($field); + + return $msg; + } + + function HasError($params) + { + if ($params['field'] == 'any') { + $object =& $this->Application->recallObject($this->getPrefixSpecial()); + return $object->HasErrors(); + } + else { + return $this->Error($params) != ''; + } + } + + function IsRequired($params) + { + $field = $params['field']; + $object =& $this->Application->recallObject($this->getPrefixSpecial(),$this->Prefix, $params); + + $options = $object->GetFieldOptions($field); + return getArrayValue($options,'required'); + } + + function PredefinedOptions($params) + { + $field = $params['field']; + $object =& $this->Application->recallObject($this->getPrefixSpecial(),$this->Prefix, $params); + + $value = $object->GetDBField($field); + $options = $object->GetFieldOptions($field); + + $block_params = $this->prepareTagParams($params); + + $block_params['name'] = $this->SelectParam($params, 'render_as,block'); + $block_params['field'] = $params['field']; + $block_params['pass_params'] = 'true'; + + $selected_param_name = getArrayValue($params,'selected_param'); + if(!$selected_param_name) $selected_param_name = $params['selected']; + $selected = $params['selected']; + + $o = ''; + if( $this->HasParam($params,'no_empty') && !getArrayValue($options['options'],'') ) array_shift($options['options']); + foreach ($options['options'] as $key => $val) { + $block_params['key'] = $key; + $block_params['option'] = $val; + $block_params['field_name'] = $this->InputName($params); + $block_params[$selected_param_name] = ( $key == $value ? ' '.$selected : ''); + $block_params['PrefixSpecial'] = $this->getPrefixSpecial(); + $o .= $this->Application->ParseBlock($block_params, 1); + } + + return $o; + } + + function Format($params) + { + $field = $params['field']; + $object =& $this->Application->recallObject($this->getPrefixSpecial(),$this->Prefix, $params); + + $options = $object->GetFieldOptions($field); + + $formatter_class = getArrayValue($options,'formatter'); + if($formatter_class) + { + $formatter =& $this->Application->recallObject($formatter_class); + $human_format = getArrayValue($params,'human'); + $edit_size = getArrayValue($params,'edit_size'); + $sample = getArrayValue($params,'sample'); + if($sample) + { + return $formatter->GetSample($field, $options, $object); + } + elseif($human_format || $edit_size) + { + $format = $formatter->HumanFormat($options['format']); + return $edit_size ? strlen($format) : $format; + } + } + + return $options['format']; + } + + /** + * Print grid pagination using + * block names specified + * + * @param Array $params + * @return string + * @access public + */ + function PrintPages($params) + { + $list =& $this->GetList($params); + $prefix_special = $list->getPrefixSpecial(); + $total_pages = $list->GetTotalPages(); + + if ($total_pages) $this->Application->Parser->DataExists = true; + + $o = ''; + + // what are these 2 lines for? + $this->Application->SetVar($prefix_special.'_event',''); + $this->Application->SetVar($prefix_special.'_id',''); + + $current_page = $this->Application->RecallVar($prefix_special.'_Page'); + + $block_params = $this->prepareTagParams($params); + + $split = ( isset($params['split'] ) ? $params['split'] : 10 ); + + $split_start = $current_page - ceil($split/2); + if ($split_start < 1){ + $split_start = 1; + } + $split_end = $split_start + $split-1; + + if ($split_end > $total_pages) { + $split_end = $total_pages; + $split_start = max($split_end - $split + 1, 1); + } + + if ($current_page > 1){ + $prev_block_params = $this->prepareTagParams(); + + if ($total_pages > $split){ + $prev_block_params['page'] = max($current_page-$split, 1); + $prev_block_params['name'] = $this->SelectParam($params, 'prev_page_split_render_as,prev_page_split_block'); + if ($prev_block_params['name']){ + $o .= $this->Application->ParseBlock($prev_block_params, 1); + } + } + + $prev_block_params['name'] = 'page'; + $prev_block_params['page'] = $current_page-1; + $prev_block_params['name'] = $this->SelectParam($params, 'prev_page_render_as,block_prev_page,prev_page_block'); + if ($prev_block_params['name']) { + $this->Application->SetVar($this->getPrefixSpecial().'_Page', $current_page-1); + $o .= $this->Application->ParseBlock($prev_block_params, 1); + } + } + else { + if ( $no_prev_page_block = $this->SelectParam($params, 'no_prev_page_render_as,block_no_prev_page') ) { + $block_params['name'] = $no_prev_page_block; + $o .= $this->Application->ParseBlock($block_params, 1); + } + } + + $separator_params['name'] = $this->SelectParam($params, 'separator_render_as,block_separator'); + for ($i = $split_start; $i <= $split_end; $i++) + { + if ($i == $current_page) { + $block = $this->SelectParam($params, 'current_render_as,active_render_as,block_current,active_block'); + } + else { + $block = $this->SelectParam($params, 'link_render_as,inactive_render_as,block_link,inactive_block'); + } + + $block_params['name'] = $block; + $block_params['page'] = $i; + $this->Application->SetVar($this->getPrefixSpecial().'_Page', $i); + $o .= $this->Application->ParseBlock($block_params, 1); + + if ($this->SelectParam($params, 'separator_render_as,block_separator') + && $i < $split_end) + { + $o .= $this->Application->ParseBlock($separator_params, 1); + } + } + + if ($current_page < $total_pages){ + $next_block_params = $this->prepareTagParams(); + $next_block_params['page']=$current_page+1; + $next_block_params['name'] = $this->SelectParam($params, 'next_page_render_as,block_next_page,next_page_block'); + if ($next_block_params['name']){ + $this->Application->SetVar($this->getPrefixSpecial().'_Page', $current_page+1); + $o .= $this->Application->ParseBlock($next_block_params, 1); + } + if ($total_pages > $split){ + $next_block_params['page']=min($current_page+$split, $total_pages); + $next_block_params['name'] = $this->SelectParam($params, 'next_page_split_render_as,next_page_split_block'); + if ($next_block_params['name']){ + $o .= $this->Application->ParseBlock($next_block_params, 1); + } + } + } + + return $o; + } + + /** + * Print grid pagination using + * block names specified + * + * @param Array $params + * @return string + * @access public + */ + function PaginationBar($params) + { + return $this->PrintPages($params); + } + + /** + * Returns input field name to + * be placed on form (for correct + * event processing) + * + * @param Array $params + * @return string + * @access public + */ + function InputName($params) + { + $prefix_special=$this->getPrefixSpecial(); + $object =& $this->Application->recallObject($prefix_special); + + if ( $formatter = getArrayValue($object->Fields, $params['field'], 'formatter') ) { + if ( $formatter == 'kMultiLanguage' ) { + $formatter =& $this->Application->recallObject($formatter); + $params['field'] = $formatter->LangFieldName($params['field']); + } + } + + if ( $idfield = getArrayValue($params, 'IdField') ) { + $id = $object->GetDBField($idfield); + } + else { + $id = $object->GetID(); + } + + return $prefix_special.'['.$id.']['.$params['field'].']'; + } + + + + /** + * Returns index where 1st changable sorting field begins + * + * @return int + * @access private + */ + function getUserSortIndex() + { + $list_sortings = $this->Application->getUnitOption($this->Prefix, 'ListSortings'); + $sorting_prefix = getArrayValue($list_sortings, $this->Special) ? $this->Special : ''; + + $user_sorting_start = 0; + if ( $forced_sorting = getArrayValue($list_sortings, $sorting_prefix, 'ForcedSorting') ) { + $user_sorting_start = count($forced_sorting); + } + return $user_sorting_start; + } + + /** + * Returns order direction for given field + * + * + * + * @param Array $params + * @return string + * @access public + */ + function Order($params) + { + $field = $params['field']; + $user_sorting_start = $this->getUserSortIndex(); + + $list =& $this->GetList($params); + + if ($list->GetOrderField($user_sorting_start) == $field) + { + return strtolower($list->GetOrderDirection($user_sorting_start)); + } + elseif($list->GetOrderField($user_sorting_start+1) == $field) + { + return '2_'.strtolower($list->GetOrderDirection($user_sorting_start+1)); + } + else + { + return 'no'; + } + } + + /** + * Get's information of sorting field at "pos" position, + * like sorting field name (type="field") or sorting direction (type="direction") + * + * @param Array $params + * @return mixed + */ + function OrderInfo($params) + { + $user_sorting_start = $this->getUserSortIndex() + --$params['pos']; + $list =& $this->GetList($params); +// $object =& $this->Application->recallObject( $this->getPrefixSpecial() ); + + if($params['type'] == 'field') return $list->GetOrderField($user_sorting_start); + if($params['type'] == 'direction') return $list->GetOrderDirection($user_sorting_start); + } + + /** + * Checks if sorting field/direction matches passed field/direction parameter + * + * @param Array $params + * @return bool + */ + function IsOrder($params) + { + $params['type'] = isset($params['field']) ? 'field' : 'direction'; + $value = $this->OrderInfo($params); + + if( isset($params['field']) ) return $params['field'] == $value; + if( isset($params['direction']) ) return $params['direction'] == $value; + } + + /** + * Returns list perpage + * + * @param Array $params + * @return int + */ + function PerPage($params) + { + $object =& $this->Application->recallObject( $this->getPrefixSpecial() ); + return $object->PerPage; + } + + /** + * Checks if list perpage matches value specified + * + * @param Array $params + * @return bool + */ + function PerPageEquals($params) + { + $object =& $this->Application->recallObject( $this->getPrefixSpecial() ); + return $object->PerPage == $params['value']; + } + + function SaveEvent($params) + { + // SaveEvent is set during onbuild, but we may need it before any other tag calls onBuild + $prefix_special = $this->getPrefixSpecial(); + $item = $this->Application->recallObject($prefix_special); + + return $this->Application->GetVar($prefix_special.'_SaveEvent'); + } + + function NextId($params) + { + $prefix_special=$this->getPrefixSpecial(); + $ids = explode(',', $this->Application->RecallVar($prefix_special.'_selected_ids')); + $item = $this->Application->recallObject($prefix_special); + $cur_id = $item->GetId(); + + $i = array_search($cur_id,$ids); + if ($i !== false) { + return $i < count($ids)-1 ? $ids[$i+1] : ''; + } + return ''; + } + + function PrevId($params) + { + $prefix_special=$this->getPrefixSpecial(); + $ids = explode(',', $this->Application->RecallVar($prefix_special.'_selected_ids')); + $item = $this->Application->recallObject($prefix_special); + $cur_id = $item->GetId(); + + $i = array_search($cur_id,$ids); + if ($i !== false) { + return $i > 0 ? $ids[$i-1] : ''; + } + return ''; + } + + function IsSingle($params) + { + return ($this->NextId($params) === '' && $this->PrevId($params) === ''); + } + + function IsLast($params) + { + return ($this->NextId($params) === ''); + } + + function IsFirst($params) + { + return ($this->PrevId($params) === ''); + } + + /** + * Checks if field value is equal to proposed one + * + * @param Array $params + * @return bool + */ + function FieldEquals($params) + { + $object =& $this->Application->recallObject($this->getPrefixSpecial(),$this->Prefix, $params); + $ret = $object->GetDBField($this->SelectParam($params, 'name,field')) == $params['value']; +// if( getArrayValue($params,'inverse') ) $ret = !$ret; + return $ret; + } + + function ItemIcon($params) + { + $object =& $this->Application->recallObject($this->getPrefixSpecial(),$this->Prefix, $params); + + $grids = $this->Application->getUnitOption($this->Prefix,'Grids'); + $icons =& $grids[ $params['grid'] ]['Icons']; + + $key = ''; + $status_fields = $this->Application->getUnitOption($this->Prefix,'StatusField'); + if(!$status_fields) return $icons['default']; + + foreach($status_fields as $status_field) + { + $key .= $object->GetDBField($status_field).'_'; + } + $key = rtrim($key,'_'); + $value = ($key !== false) ? $key : 'default'; + + return isset($icons[$value]) ? $icons[$value] : $icons['default']; + } + + function SectionTitle($params) + { + $title_presets = $this->Application->getUnitOption($this->Prefix,'TitlePresets'); + $title_info = getArrayValue($title_presets, $params['title_preset'] ); + if($title_info === false) return $params['title']; + + if( getArrayValue($title_presets,'default') ) + { + // use default labels + custom labels specified in preset used + $title_info = array_merge_recursive2($title_presets['default'], $title_info); + } + + $title = $title_info['format']; + + // 1. get objects in use for title construction + $objects = Array(); + $object_status = Array(); + $status_labels = Array(); + + $prefixes = getArrayValue($title_info,'prefixes'); + if($prefixes) + { + foreach($prefixes as $prefix_special) + { + $prefix_data = $this->Application->processPrefix($prefix_special); + $prefix_data['prefix_special'] = rtrim($prefix_data['prefix_special'],'.'); + $objects[ $prefix_data['prefix_special'] ] =& $this->Application->recallObject($prefix_data['prefix_special'], $prefix_data['prefix'], $params); + $object_status[ $prefix_data['prefix_special'] ] = $objects[ $prefix_data['prefix_special'] ]->GetID() ? 'edit' : 'new'; + + // a. set object's status field (adding item/editing item) for each object in title + if( getArrayValue($title_info[ $object_status[ $prefix_data['prefix_special'] ].'_status_labels' ],$prefix_data['prefix_special']) ) + { + $status_labels[ $prefix_data['prefix_special'] ] = $title_info[ $object_status[ $prefix_data['prefix_special'] ].'_status_labels' ][ $prefix_data['prefix_special'] ]; + $title = str_replace('#'.$prefix_data['prefix_special'].'_status#', $status_labels[ $prefix_data['prefix_special'] ], $title); + } + + // b. setting object's titlefield value (in titlebar ONLY) to default in case if object beeing created with no titlefield filled in + if( $object_status[ $prefix_data['prefix_special'] ] == 'new' ) + { + $new_value = $this->getInfo( $objects[ $prefix_data['prefix_special'] ], 'titlefield' ); + if(!$new_value && getArrayValue($title_info['new_titlefield'],$prefix_data['prefix_special']) ) $new_value = $this->Application->Phrase($title_info['new_titlefield'][ $prefix_data['prefix_special'] ]); + $title = str_replace('#'.$prefix_data['prefix_special'].'_titlefield#', $new_value, $title); + } + } + } + + // 2. replace phrases if any found in format string + $title = $this->Application->ReplaceLanguageTags($title,false); + + // 3. find and replace any replacement vars + preg_match_all('/#(.*_.*)#/Uis',$title,$rets); + if($rets[1]) + { + $replacement_vars = array_keys( array_flip($rets[1]) ); + foreach($replacement_vars as $replacement_var) + { + $var_info = explode('_',$replacement_var,2); + $object =& $objects[ $var_info[0] ]; + $new_value = $this->getInfo($object,$var_info[1]); + $title = str_replace('#'.$replacement_var.'#', $new_value, $title); + } + } + + return $title; + } + + function getInfo(&$object, $info_type) + { + switch ($info_type) + { + case 'titlefield': + $field = $this->Application->getUnitOption($object->Prefix,'TitleField'); + return $field !== false ? $object->GetField($field) : 'TitleField Missing'; + break; + + case 'recordcount': + $of_phrase = $this->Application->Phrase('la_of'); + return $object->NoFilterCount != $object->RecordsCount ? $object->RecordsCount.' '.$of_phrase.' '.$object->NoFilterCount : $object->RecordsCount; + break; + + default: + break; + } + } + + /** + * Parses block depending on its element type. + * For radio and select elements values are taken from 'value_list_field' in key1=value1,key2=value2 + * format. key=value can be substituted by SELECT f1 AS OptionName, f2 AS OptionValue... FROM TableName + * where prefix is TABLE_PREFIX + * + * @param Array $params + * @return string + */ + function ConfigFormElement($params) + { + $object =& $this->Application->recallObject( $this->getPrefixSpecial() ); + $field = $params['field']; + + $helper = $this->Application->recallObject('InpCustomFieldsHelper'); + + $element_type = $object->GetDBField($params['element_type_field']); + + if($element_type=='label') $element_type='text'; + $params['name']= $params['blocks_prefix'] . $element_type; + + switch ($element_type){ + case 'select': + case 'radio': + $field_options = $object->GetFieldOptions($field, 'options'); + $field_options['options'] = $helper->GetValuesHash( $object->GetDBField($params['value_list_field']) ); + $object->SetFieldOptions($field, $field_options); + break; + + case 'textarea': + $params['field_params'] = $helper->ParseConfigSQL($object->GetDBField($params['value_list_field'])); + break; + + case 'password': + case 'text': + case 'checkbox': + default: + break; + } + return $this->Application->ParseBlock($params, 1); + } + + /** + * Get's requested custom field value + * + * @param Array $params + * @return string + * @access public + */ + function CustomField($params) + { + $prefix = $this->Prefix; + $object =& $this->Application->recallObject( $prefix ); + + $field = $this->SelectParam($params, 'name,field'); + + $sql = ' SELECT cv.Value FROM '.TABLE_PREFIX.'CustomField cf + LEFT JOIN '.TABLE_PREFIX.'CustomMetaData cv + ON cf.CustomFieldId = cv.CustomFieldId + WHERE cf.Type = '.$this->Application->getUnitOption($prefix, 'ItemType').' + AND cv.ResourceId = '.$object->GetDBField('ResourceId').' + AND cf.FieldName = "'.$field.'"'; + return $this->Conn->GetOne($sql); + } + + /** + * transposes 1-dimensional array elements for vertical alignment according to given columns and per_page parameters + * + * @param array $arr + * @param int $columns + * @param int $per_page + * @return array + */ + function LinearToVertical(&$arr, $columns, $per_page) + { + $rows=$columns; + $cols=min(ceil($per_page/$columns), ceil(sizeof($arr)/$columns)); + $imatrix=array(); + for ($row=0; $row<$rows; $row++) { + for ($col=0; $col<$cols; $col++){ + $imatrix[$col*$rows+$row]=$arr[$row*$cols+$col]; + } + } + ksort($imatrix); + reset($imatrix); + return $imatrix; + } + + /** + * If data was modfied & is in TempTables mode, then parse block with name passed; + * remove modification mark if not in TempTables mode + * + * @param Array $params + * @return string + * @access public + * @author Alexey + */ + function SaveWarning($params) + { + $main_prefix = getArrayValue($params, 'main_prefix'); + if($main_prefix && $main_prefix != '$main_prefix') + { + $top_prefix = $main_prefix; + } + else + { + $top_prefix = $this->Application->GetTopmostPrefix($this->Prefix); + } + + $temp_tables = $this->Application->GetVar($top_prefix.'_mode') == 't'; + $modified = $this->Application->RecallVar($top_prefix.'_modified'); + + if($temp_tables && $modified) + { + return $this->Application->ParseBlock($params); + } + $this->Application->RemoveVar($top_prefix.'_modified'); + return ''; + } + + /** + * Returns list record count queries (on all pages) + * + * @param Array $params + * @return int + */ + function TotalRecords($params) + { + $list =& $this->GetList($params); + if (!$list->Counted) $list->CountRecs(); + return $list->RecordsCount; + } +} + +?> \ No newline at end of file Index: trunk/core/kernel/session/session.php =================================================================== diff -u -N --- trunk/core/kernel/session/session.php (revision 0) +++ trunk/core/kernel/session/session.php (revision 1560) @@ -0,0 +1,632 @@ +SetCookieDomain('my.domain.com'); +$session->SetCookiePath('/myscript'); +$session->SetCookieName('my_sid_cookie'); +$session->SetGETName('sid'); +$session->InitSession(); + +... + +//link output: + +echo "NeedQueryString() ? 'sid='.$session->SID : '' ) .">My Link"; + +*/ + +//Implements session storage in the database +class SessionStorage extends kDBBase { + + var $Expiration; + var $SessionTimeout=0; + + var $OriginalData=Array(); + + var $TimestampField; + var $SessionDataTable; + var $DataValueField; + var $DataVarField; + + function Init($prefix,$special) + { + parent::Init($prefix,$special); + $this->setTableName('sessions'); + $this->setIDField('sid'); + $this->TimestampField = 'expire'; + $this->SessionDataTable = 'SessionData'; + $this->DataValueField = 'value'; + $this->DataVarField = 'var'; + } + + function setSessionTimeout($new_timeout) + { + $this->SessionTimeout = $new_timeout; + } + + function StoreSession(&$session) + { + $query = ' INSERT INTO '.$this->TableName.' ('.$this->IDField.', '.$this->TimestampField.')'. + ' VALUES ('.$this->Conn->qstr($session->SID).', '.$session->Expiration.')'; + $this->Conn->Query($query); + } + + function DeleteSession(&$session) + { + $query = ' DELETE FROM '.$this->TableName.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID); + $this->Conn->Query($query); + + $query = ' DELETE FROM '.$this->SessionDataTable.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID); + $this->Conn->Query($query); + + $this->OriginalData = Array(); + } + + function UpdateSession(&$session, $timeout=0) + { + $query = ' UPDATE '.$this->TableName.' SET '.$this->TimestampField.' = '.$session->Expiration.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID); + $this->Conn->Query($query); + } + + function LocateSession($sid) + { + $query = ' SELECT '.$this->TimestampField.' FROM '.$this->TableName.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($sid); + $result = $this->Conn->GetOne($query); + + if($result===false) return false; + + $this->Expiration = $result; + return true; + } + + function GetExpiration() + { + return $this->Expiration; + } + + function LoadData(&$session) + { + $query = 'SELECT '.$this->DataValueField.','.$this->DataVarField.' FROM '.$this->SessionDataTable.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID); + + $this->OriginalData = $this->Conn->GetCol($query, $this->DataVarField); + return $this->OriginalData; + } + + /** + * Enter description here... + * + * @param Session $session + * @param string $var_name + */ + function GetField(&$session, $var_name) + { + return $this->Conn->GetOne('SELECT '.$var_name.' FROM '.$this->TableName.' WHERE `'.$this->IDField.'` = '.$this->Conn->qstr($session->GetID()) ); + } + + function SetField(&$session, $var_name, $value) + { + return $this->Conn->Query('UPDATE '.$this->TableName.' SET '.$var_name.' = '.$this->Conn->qstr($value).' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->GetID()) ); + } + + function SaveData(&$session) + { + if(!$session->SID) return false; // can't save without sid + + $ses_data = $session->Data->GetParams(); + + $replace = ''; + foreach ($ses_data as $key => $value) + { + if ( isset($this->OriginalData[$key]) && $this->OriginalData[$key] == $value) + { + continue; //skip unchanged session data + } + else + { + $replace .= sprintf("(%s, %s, %s),", + $this->Conn->qstr($session->SID), + $this->Conn->qstr($key), + $this->Conn->qstr($value)); + } + } + $replace = rtrim($replace, ','); + if ($replace != '') { + $query = ' REPLACE INTO '.$this->SessionDataTable. ' ('.$this->IDField.', '.$this->DataVarField.', '.$this->DataValueField.') VALUES '.$replace; + $this->Conn->Query($query); + } + } + + function RemoveFromData(&$session, $var) + { + $query = 'DELETE FROM '.$this->SessionDataTable.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID). + ' AND '.$this->DataVarField.' = '.$this->Conn->qstr($var); + $this->Conn->Query($query); + unset($this->OriginalData[$var]); + } + + function GetExpiredSIDs() + { + $query = ' SELECT '.$this->IDField.' FROM '.$this->TableName.' WHERE '.$this->TimestampField.' > '.time(); + return $this->Conn->GetCol($query); + } + + function DeleteExpired() + { + $expired_sids = $this->GetExpiredSIDs(); + if($expired_sids) + { + $where_clause=' WHERE '.$this->IDField.' IN ("'.implode('","',$expired_sids).'")'; + $sql = 'DELETE FROM '.$this->SessionDataTable.$where_clause; + $this->Conn->Query($sql); + + $sql = 'DELETE FROM '.$this->TableName.$where_clause; + $this->Conn->Query($sql); + } + return $expired_sids; + } +} + +define('smAUTO', 1); +define('smCOOKIES_ONLY', 2); +define('smGET_ONLY', 3); +define('smCOOKIES_AND_GET', 4); + +class Session extends kBase { + var $Checkers; + + var $Mode; + var $GETName = 'sid'; + + var $CookiesEnabled = true; + var $CookieName = 'sid'; + var $CookieDomain; + var $CookiePath; + var $CookieSecure = 0; + + var $SessionTimeout = 3600; + var $Expiration; + + var $SID; + + var $Storage; + + var $CachedNeedQueryString = null; + + var $Data; + + + function Session($mode=smAUTO) + { + parent::kBase(); + $this->SetMode($mode); + } + + function SetMode($mode) + { + $this->Mode = $mode; + } + + function SetCookiePath($path) + { + $this->CookiePath = $path; + } + + function SetCookieDomain($domain) + { + $this->CookieDomain = $domain; + } + + function SetGETName($get_name) + { + $this->GETName = $get_name; + } + + function SetCookieName($cookie_name) + { + $this->CookieName = $cookie_name; + } + + function InitStorage() + { + $this->Storage =& $this->Application->recallObject('SessionStorage'); + $this->Storage->setSessionTimeout($this->SessionTimeout); + } + + function Init($prefix,$special) + { + parent::Init($prefix,$special); + + $this->CheckIfCookiesAreOn(); + $this->Checkers = Array(); + $this->InitStorage(); + $this->Data =& new Params(); + + $tmp_sid = $this->GetPassedSIDValue(); + $expired_sids = $this->DeleteExpired(); + if( ( $expired_sids && in_array($tmp_sid,$expired_sids) ) || ( $tmp_sid && !$this->Check() ) ) + { + $this->SetSession(); + $this->Application->HandleEvent($event, 'login:OnSessionExpire'); + } + + if ($this->Check()) { + $this->SID = $this->GetPassedSIDValue(); + $this->Refresh(); + $this->LoadData(); + } + else { + $this->SetSession(); + } + } + + function CheckReferer() + { + $path = preg_replace("/admin$/", '', $this->CookiePath); // removing /admin for compatability with in-portal (in-link/admin/add_link.php) + $reg = '#^'.preg_quote(PROTOCOL.$this->CookieDomain.$path).'#'; + return preg_match($reg, $_SERVER['HTTP_REFERER']) || (defined('IS_POPUP') && IS_POPUP); + } + + function CheckIfCookiesAreOn() + { + if ($this->Mode == smGET_ONLY || (defined('INPORTAL_ENV')&&INPORTAL_ENV && defined('ADMIN')&&ADMIN && !$this->Application->GetVar('front')) ) + { + //we don't need to bother checking if we would not use it + $this->CookiesEnabled = false; + return; + } + $http_query =& $this->Application->recallObject('HTTPQuery'); + $cookies_on = isset($http_query->Cookie['cookies_on']); // not good here + + if (!$cookies_on) { + //If referer is our server, but we don't have our cookies_on, it's definetly off + if ($this->CheckReferer()) { + $this->CookiesEnabled = false; + } + else { + //Otherwise we still suppose cookies are on, because may be it's the first time user visits the site + //So we send cookies on to get it next time (when referal will tell us if they are realy off + setcookie( + 'cookies_on', + 1, + time()+31104000, //one year should be enough + $this->CookiePath, + $this->CookieDomain, + $this->CookieSecure + ); + } + } + else + $this->CookiesEnabled = true; + return $this->CookiesEnabled; + } + + function Check() + { + // we should check referer if cookies are disabled, and in combined mode + // auto mode would detect cookies, get only mode would turn it off - so we would get here + // and we don't care about referal in cookies only mode + if ( $this->Mode != smCOOKIES_ONLY && (!$this->CookiesEnabled || $this->Mode == smCOOKIES_AND_GET) ) { + if (!$this->CheckReferer()) + return false; + } + + $sid = $this->GetPassedSIDValue(); + + if (empty($sid)) return false; + + //try to load session by sid, if everything is fine + $result = $this->LoadSession($sid); + + return $result; + } + + function LoadSession($sid) + { + if( $this->Storage->LocateSession($sid) ) { + //if we have session with such SID - get its expiration + $this->Expiration = $this->Storage->GetExpiration(); + + //If session has expired + if ($this->Expiration < time()) return false; + + //Otherwise it's ok + return true; + } + else //fake or deleted due to expiration SID + return false; + } + + function GetPassedSIDValue($use_cache = 1) + { + if (!empty($this->CachedSID) && $use_cache) return $this->CachedSID; + $http_query =& $this->Application->recallObject('HTTPQuery'); + $get_sid = getArrayValue($http_query->Get, $this->GETName); + + if ($this->Application->GetVar('admin') == 1 && $get_sid) { + $sid = $get_sid; + } + else { + switch ($this->Mode) { + case smAUTO: + //Cookies has the priority - we ignore everything else + $sid=$this->CookiesEnabled ? getArrayValue($http_query->Cookie,$this->CookieName) : $get_sid; + break; + case smCOOKIES_ONLY: + $sid = $http_query->Cookie[$this->CookieName]; + break; + case smGET_ONLY: + $sid = $get_sid; + break; + case smCOOKIES_AND_GET: + $cookie_sid = $http_query->Cookie[$this->CookieName]; + //both sids should match if cookies are enabled + if (!$this->CookiesEnabled || ($cookie_sid == $get_sid)) + { + $sid = $get_sid; //we use get here just in case cookies are disabled + } + else + { + $sid = ''; + } + break; + } + } + + if ($this->Application->GetVar('front')) { + $this->CookiesEnabled = false; + } + + $this->CachedSID = $sid; + return $this->CachedSID; + } + + /** + * Returns session id + * + * @return int + * @access public + */ + function GetID() + { + return $this->SID; + } + + /** + * Generates new session id + * + * @return int + * @access private + */ + function GenerateSID() + { + list($usec, $sec) = explode(" ",microtime()); + + $sid_part_1 = substr($usec, 4, 4); + $sid_part_2 = mt_rand(1,9); + $sid_part_3 = substr($sec, 6, 4); + $digit_one = substr($sid_part_1, 0, 1); + if ($digit_one == 0) { + $digit_one = mt_rand(1,9); + $sid_part_1 = ereg_replace("^0","",$sid_part_1); + $sid_part_1=$digit_one.$sid_part_1; + } + $this->setSID($sid_part_1.$sid_part_2.$sid_part_3); + return $this->SID; + } + + /** + * Set's new session id + * + * @param int $new_sid + * @access private + */ + function setSID($new_sid) + { + $this->SID=$new_sid; + $this->Application->SetVar($this->GETName,$new_sid); + } + + function SetSession() + { + $this->GenerateSID(); + $this->Expiration = time() + $this->SessionTimeout; + switch ($this->Mode) { + case smAUTO: + if ($this->CookiesEnabled) { + $this->SetSessionCookie(); + } + break; + case smGET_ONLY: + break; + case smCOOKIES_ONLY: + case smCOOKIES_AND_GET: + $this->SetSessionCookie(); + break; + } + $this->Storage->StoreSession($this); + } + + function SetSessionCookie() + { + setcookie( + $this->CookieName, + $this->SID, + $this->Expiration, + $this->CookiePath, + $this->CookieDomain, + $this->CookieSecure + ); + } + + /** + * Refreshes session expiration time + * + * @access private + */ + function Refresh() + { + if ($this->CookiesEnabled) $this->SetSessionCookie(); //we need to refresh the cookie + $this->Storage->UpdateSession($this); + } + + function Destroy() + { + $this->Storage->DeleteSession($this); + $this->Data =& new Params(); + $this->SID = ''; + if ($this->CookiesEnabled) $this->SetSessionCookie(); //will remove the cookie due to value (sid) is empty + $this->SetSession(); //will create a new session + } + + function NeedQueryString($use_cache = 1) + { + if ($this->CachedNeedQueryString != null && $use_cache) return $this->CachedNeedQueryString; + switch ($this->Mode) { + case smAUTO: + if ($this->CookiesEnabled) { + $res = 0; + } + else { + $res = 1; + } + break; + case smCOOKIES_ONLY: + break; + case smGET_ONLY: + case smCOOKIES_AND_GET: + $res = 1; + break; + } + $this->CachedNeedQueryString = $res; + return $res; + } + + function LoadData() + { + $this->Data->AddParams($this->Storage->LoadData($this)); + } + + function PrintSession($comment='') + { + if( $this->Application->isDebugMode() && dbg_ConstOn('DBG_SHOW_SESSIONDATA') ) + { + global $debugger; + $debugger->appendHTML('SessionStorage ('.$comment.'):'); + $session_data = $this->Data->GetParams(); + ksort($session_data); + foreach($session_data as $session_key => $session_value) + { + if( IsSerialized($session_value) ) + { + $session_data[$session_key] = unserialize($session_value); + } + } + $debugger->dumpVars($session_data); + + // to insert after HTTPQuery if it's visible + $new_row = dbg_ConstOn('DBG_SHOW_HTTPQUERY') ? 4 : 2; + + //$debugger->moveAfterRow($new_row,2); + } + } + + function SaveData() + { + $this->StoreVar('last_template', basename($_SERVER['PHP_SELF']).'|'.substr($this->Application->BuildEnv($this->Application->GetVar('t'), Array('m_opener' => 'u'), 'all', true), strlen(ENV_VAR_NAME)+1 )); + $this->StoreVar('last_env', substr($this->Application->BuildEnv($this->Application->GetVar('t'),Array('pass'=>'all')), strlen(ENV_VAR_NAME)+1 )); + $this->PrintSession('after save'); + $this->Storage->SaveData($this); + } + + function StoreVar($name, $value) + { + $this->Data->Set($name, $value); + } + + function StoreVarDefault($name, $value) + { + $tmp = $this->RecallVar($name); + if($tmp === false || $tmp == '') + { + $this->StoreVar($name, $value); + } + } + + function RecallVar($name,$default=false) + { + $ret = $this->Data->Get($name); + return ($ret===false) ? $default : $ret; + } + + function RemoveVar($name) + { + $this->Storage->RemoveFromData($this, $name); + $this->Data->Remove($name); + } + + function GetField($var_name) + { + return $this->Storage->GetField($this, $var_name); + } + + function SetField($var_name, $value) + { + $this->Storage->SetField($this, $var_name, $value); + } + + /** + * Deletes expired sessions + * + * @return Array expired sids if any + * @access private + */ + function DeleteExpired() + { + return $this->Storage->DeleteExpired(); + } + +} + +?> \ No newline at end of file Index: trunk/core/kernel/utility/iterator.php =================================================================== diff -u -N --- trunk/core/kernel/utility/iterator.php (revision 0) +++ trunk/core/kernel/utility/iterator.php (revision 1560) @@ -0,0 +1,51 @@ +Items =& $items; + $this->IndexItems(); + $this->CurrentIndex = 0; + } + + function IndexItems() + { + $i = 0; + foreach ($this->Items as $key => $item) { + $this->Index[$i] = $key; // ?????? $i++ + $i++; + } + $this->Count = $i; + } + + function HasMore() + { + return $this->CurrentIndex < $this->Count; + } + + function GoNext() + { + $this->CurrentIndex++; + } + + function &GetItem() + { + return $this->Items[$this->Index[$this->CurrentIndex]]; + } + + function GetKey() + { + return $this->Index[$this->CurrentIndex]; + } + +} + +?> \ No newline at end of file Index: trunk/core/kernel/event_handler.php =================================================================== diff -u -N --- trunk/core/kernel/event_handler.php (revision 0) +++ trunk/core/kernel/event_handler.php (revision 1560) @@ -0,0 +1,136 @@ +getPrefixSpecial(true) instead of + * $event->Prefix_Special as usual. This is due PHP + * is converting "." symbols in variable names during + * submit info "_". $event->getPrefixSpecial optional + * 1st parameter returns correct corrent Prefix_Special + * for variables beeing submitted such way (e.g. variable + * name that will be converted by PHP: "users.read_only_id" + * will be submitted as "users_read_only_id". + * + * 2. When using $this->Application-LinkVar on variables submitted + * from form which contain $Prefix_Special then note 1st item. Example: + * LinkVar($event->getPrefixSpecial(true).'_varname',$event->Prefix_Special.'_varname') + * + */ + + + /** + * Default event handler. Mostly abstract class + * + */ + class kEventHandler extends kBase { + + /** + * In case if event should be handled with mehod, + * which name differs from event name, then it + * should be specified here. + * key - event name, value - event method + * + * @var Array + * @access protected + */ + var $eventMethods=Array(); + + /** + * Define alternative event processing method names + * + * @see $eventMethods + * @access protected + */ + function mapEvents() + { + + } + + function getPrefixSpecial() + { + trigger_error('Usage of getPrefixSpecial() this method is forbidden in kEventHandler class children. Use $event->getPrefixSpecial(true); instead', E_USER_ERROR); + } + + /** + * Set's prefix and special + * + * @param string $prefix + * @param string $special + * @access public + */ + function Init($prefix,$special) + { + parent::Init($prefix,$special); + $this->mapEvents(); + } + + /** + * Process Event + * + * @param kEvent $event + * @access public + */ + function processEvent(&$event) + { + $event_name=$event->Name; + if( isset($this->eventMethods[$event_name]) ) $event_name=$this->eventMethods[$event_name]; + + if( method_exists($this,$event_name) ) + { + $this->$event_name($event); + } + else + { + trigger_error('event '.$event->Name.' not implemented in class '.get_class($this).'', E_USER_ERROR); + } + } + + /** + * Sample dummy event + * + * @param kEvent $event + * @access protected + */ + function OnBuild(&$event) + { + /*echo 'building:
'; + print_pre($event);*/ + } + + /** + * Apply some special processing to + * object beeing recalled before using + * it in other events that call prepareObject + * + * @param Object $object + * @param kEvent $event + * @access protected + */ + function prepareObject(&$object,&$event) + { + // processing here + } + + /** + * Creates new event as child of + * event passed as $event param + * + * @param kEvent $event + * @access protected + */ + function &inheritEvent(&$event, $name=null) + { + $child_event = new kEvent(); + $child_event->MasterEvent =& $event; + $child_event->Prefix=$event->Prefix; + $child_event->Special=$event->Special; + $child_event->Prefix_Special=$event->Prefix_Special; + $child_event->Name = $name; + return $child_event; + } + } + + +?> \ No newline at end of file Index: trunk/core/kernel/utility/utilities.php =================================================================== diff -u -N --- trunk/core/kernel/utility/utilities.php (revision 0) +++ trunk/core/kernel/utility/utilities.php (revision 1560) @@ -0,0 +1,41 @@ +"; +// print_pre($array); + $rets = Array(); + foreach ($array as $name => $val) + { + $regs = Array(); +// echo "checking $name
"; + if (eregi($mask, $name, $regs)) { +// echo "matched
"; +// print_pre($regs); + if ($ret_mode == 1) { + $rets[$regs[$key_id]] = $val; + } + else { + array_push($regs, $val); + $a_key = $regs[$key_id]; + $i = 0; + while (array_key_exists($a_key, $rets)) { + $a_key.=$i; + $i++; + } + $rets[$a_key] = $regs; + } + } + } +// echo "returning "; +// print_pre($rets); + return $rets; + } + +} + +?> \ No newline at end of file Index: trunk/core/kernel/utility/factory.php =================================================================== diff -u -N --- trunk/core/kernel/utility/factory.php (revision 0) +++ trunk/core/kernel/utility/factory.php (revision 1560) @@ -0,0 +1,214 @@ +$regs[1].$regs[3].$regs[4], 'special'=>$regs[2]); + + $tmp=explode('_',$prefix_special,2); + $tmp[0]=explode('.',$tmp[0]); + + $prefix=$tmp[0][0]; + $prefix_special=$prefix; // new1 + if( isset($tmp[1]) ) + { + $prefix.='_'.$tmp[1]; + } + $special= isset($tmp[0][1]) ? $tmp[0][1] : ''; + $prefix_special.='.'.$special; // new2 + return Array('prefix'=>$prefix,'special'=>$special,'prefix_special'=>$prefix_special); + } + + + /** + * Returns object using params specified, + * creates it if is required + * + * @param string $name + * @param string $pseudo_class + * @param Array $event_params + * @return Object + */ + function &getObject($name,$pseudo_class='',$event_params=Array()) + { + // $name = 'l.pick', $pseudo_class = 'l' + //echo 'N: '.$name.' - P: '.$pseudo_class."\n"; + $ret=$this->processPrefix($name); + if (!$pseudo_class) $pseudo_class = $ret['prefix']; + $name=rtrim($name,'.'); + if( isset($this->Storage[$name]) ) return $this->Storage[$name]; + + if(!isset($this->realClasses[$pseudo_class])) + { + if( $this->Application->isDebugMode() ) $GLOBALS['debugger']->appendTrace(); + trigger_error('RealClass not defined for pseudo_class '.$pseudo_class.'', E_USER_ERROR); + } + + $funs_args = func_get_args(); + array_splice($funs_args, 0, 3, Array($pseudo_class) ); + + $this->Storage[$name] =& call_user_func_array( Array(&$this,'makeClass'), $funs_args); + $this->Storage[$name]->Init($ret['prefix'],$ret['special'],$event_params); + + $prefix=$this->Storage[$name]->Prefix; + $special=$this->Storage[$name]->Special; + + $event_manager =& $this->getObject('EventManager'); + $event =& $event_manager->getBuildEvent($pseudo_class); + if($event) + { + $event->Init($prefix,$special); + foreach($event_params as $param_name=>$param_value) + { + $event->setEventParam($param_name,$param_value); + } + $this->Application->HandleEvent($event); + } + + return $this->Storage[$name]; + } + + + /** + * Removes object from storage, so next time it could be created from scratch + * + * @param string $name Object's name in the Storage + */ + function DestroyObject($name) + { + unset($this->Storage[$name]); + } + + /** + * Includes file containing class + * definition for real class name + * + * @param string $real_class + * @access private + */ + function includeClassFile($real_class) + { + if (class_exists($real_class)) return; + + if(!$this->Files[$real_class]) trigger_error('Real Class '.$real_class.' is not registered with the Factory', E_USER_ERROR); + if(!file_exists($this->Files[$real_class])) trigger_error('Include file for class '.$real_class.' ('.$this->Files[$real_class].') does not exists', E_USER_ERROR); + + if ( $deps = getArrayValue($this->Dependencies, $real_class) ) { + foreach ($deps as $filename) { + k4_include_once($filename); + } + } + + k4_include_once($this->Files[$real_class]); + } + + /** + * Get's real class name for pseudo class, + * includes class file and creates class + * instance. + * All parameters except first one are passed to object constuctor + * through mediator method makeClass that creates instance of class + * + * @param string $pseudo_class + * @return Object + * @access private + */ + function &makeClass($pseudo_class) + { + $real_class=$this->realClasses[$pseudo_class]; + $this->includeClassFile($real_class); + + $mem_before = memory_get_usage(); + $time_before = getmicrotime(); + + if( func_num_args() == 1 ) + { + $class =& new $real_class(); + } + else + { + $func_args = func_get_args(); + $pseudo_class = array_shift($func_args); + + $class =& call_user_func_array( Array($real_class,'makeClass'), $func_args ); + } + + if (defined('DEBUG_MODE') && DEBUG_MODE && dbg_ConstOn('DBG_PROFILE_MEMORY') ) + { + $mem_after = memory_get_usage(); + $time_after = getmicrotime(); + $mem_used = $mem_after - $mem_before; + $time_used = $time_after - $time_before; + global $debugger; + $debugger->appendHTML('Factroy created '.$real_class.' - used '.round($mem_used/1024, 3).'Kb time: '.round($time_used, 5)); + $debugger->profilerAddTotal('objects', null, $mem_used); + } + return $class; + + } + + /** + * Registers new class in the factory + * + * @param string $real_class + * @param string $file + * @param string $pseudo_class + * @access public + */ + function registerClass($real_class,$file,$pseudo_class=null) + { + if(!isset($pseudo_class)) $pseudo_class = $real_class; + if(!isset($this->Files[$real_class])) $this->Files[$real_class]=$file; + + if (getArrayValue($this->realClasses, $pseudo_class)) { + $this->Dependencies[$real_class][] = $this->Files[ $this->realClasses[$pseudo_class] ]; + } + + $this->realClasses[$pseudo_class]=$real_class; + } + + } + +?> \ No newline at end of file Index: trunk/core/kernel/languages/phrases_cache.php =================================================================== diff -u -N --- trunk/core/kernel/languages/phrases_cache.php (revision 0) +++ trunk/core/kernel/languages/phrases_cache.php (revision 1560) @@ -0,0 +1,175 @@ +Phrases = Array(); + $this->LanguageId = $LanguageId; + $this->LoadPhrases( $this->GetCachedIds() ); + } + + function GetCachedIds() + { + $query = sprintf("SELECT PhraseList FROM %s WHERE Template = %s", + TABLE_PREFIX.'PhraseCache', + $this->Conn->Qstr(md5($this->Application->GetVar('t')))); + $phrases_ids = $this->Conn->GetOne($query); + if ($phrases_ids === false) return Array(); + return explode(',', $phrases_ids); + } + + function LoadPhrases($ids) + { + if ( !is_array($ids) || !implode('', $ids) ) return; + $query = sprintf("SELECT Translation,Phrase FROM %s WHERE LanguageId = %s AND PhraseId IN (%s)", + TABLE_PREFIX.'Phrase', + $this->LanguageId, + join(',', $ids)); + $phrases = $this->Conn->GetCol($query,'Phrase'); + foreach($phrases as $phrase => $tanslation) + { + $this->AddCachedPhrase(strtoupper($phrase), $tanslation); + } + $this->Ids = $ids; + $this->OriginalIds = $ids; + } + + function AddCachedPhrase($label, $value) + { + $label = strtoupper($label); + $this->Phrases[$label] = $value; + } + + function UpdateCache() + { + if (!is_array($this->Ids) || count($this->Ids) == 0) return; + if ($this->Ids == $this->OriginalIds) return; //nothing changed + $query = sprintf("REPLACE %s (PhraseList, CacheDate, Template) + VALUES (%s, %s, %s)", + TABLE_PREFIX.'PhraseCache', + $this->Conn->Qstr(join(',', $this->Ids)), + mktime(), + $this->Conn->Qstr(md5($this->Application->GetVar('t')))); + $this->Conn->Query($query); + } + + function GetPhrase($label) + { + if (ereg("^!.+!$", $label) > 0) + { + $label = substr($label, 1, -1); //cut exclamation marks + } + + $original_label = $label; + $label = strtoupper($label); + if( isset($this->Phrases[$label]) ) { + $translated_label = $this->Phrases[$label]; + if($this->Application->isDebugMode() && dbg_ConstOn('DBG_PHRASES_HILIGHT') && !$this->Application->IsAdmin()) + $translated_label = ''.$translated_label.''; + return $translated_label; + } + + $this->LoadPhraseByLabel($label, $original_label); + return $this->GetPhrase($label); + } + + function LoadPhraseByLabel($label, $original_label) + { + $query = sprintf("SELECT PhraseId, Translation FROM %s WHERE LanguageId = %s AND UPPER(Phrase) = UPPER(%s)", + TABLE_PREFIX.'Phrase', + $this->LanguageId, + $this->Conn->qstr($label)); + $res = $this->Conn->GetRow($query); + if ($res === false || count($res) == 0) + { + $translation = '!'.$label.'!'; + if( $this->Application->isDebugMode() && dbg_ConstOn('DBG_PHRASES') ) + { + list($edit_tpl, $index_file) = $this->Application->IsAdmin() ? Array('in-commerce/regional/phrases_edit', 'index4.php') : Array('organized/phrases_edit', 'index.php'); + + $edit_url = $this->Application->HREF($edit_tpl,'',Array('m_opener'=>'d','phrases_label'=>$original_label,'phrases_event'=>'OnNew', 'pass'=>'all,phrases'), $index_file ); + $translation = '!'.$label.'!'; + if($this->fromTag) $translation = $this->escapeTagReserved($translation); + } + $this->AddCachedPhrase($label, $translation); //add it as already cached, as long as we dont need to cache not found phrase + return false; + } + + $this->Phrases[$label] = $res['Translation']; + array_push($this->Ids, $res['PhraseId']); + $this->Ids = array_unique($this->Ids); //just to make sure + return true; + } + + /** + * Sort params by name and then by length + * + * @param string $a + * @param string $b + * @return int + * @access private + */ + function CmpParams($a, $b) + { + $a_len = strlen($a); + $b_len = strlen($b); + if ($a_len == $b_len) return 0; + return $a_len > $b_len ? -1 : 1; + } + + /** + * Replace language tags in exclamation marks found in text + * + * @param string $text + * @param bool $force_escape force escaping, not escaping of resulting string + * @return string + * @access public + */ + function ReplaceLanguageTags($text,$forse_escaping=null) + { + $this->fromTag = true; + if( isset($forse_escaping) ) $this->fromTag = $forse_escaping; + preg_match_all("(!(la|lu)[^!]+!)", $text, $res, PREG_PATTERN_ORDER); + $language_tags = $res[0]; + uasort($language_tags, Array(&$this, 'CmpParams') ); + + $values = Array(); + $i = 0; + foreach ($language_tags as $label) { + array_push($values, $this->GetPhrase($label) ); + //array_push($values, $this->Application->Phrase($label) ); + $language_tags[$i] = '/' . $language_tags[$i] . '/'; + $i++; + } + $this->fromTag = false; + return preg_replace($language_tags, $values, $text); + } + + /** + * Escape chars in phrase translation, that could harm parser to process tag + * + * @param string $text + * @return string + * @access private + */ + function escapeTagReserved($text) + { + $reserved = Array('"',"'"); // = + $replacement = Array('\"',"\'"); // \= + return str_replace($reserved,$replacement,$text); + } + +} + + +?> \ No newline at end of file Index: trunk/core/kernel/parser/template.php =================================================================== diff -u -N --- trunk/core/kernel/parser/template.php (revision 0) +++ trunk/core/kernel/parser/template.php (revision 1560) @@ -0,0 +1,192 @@ +SetBasePath($base_path)) { + if (isset($filename)) { + $this->Filename = $filename; + $this->LoadTemplate($silent); + } + } + } + + function SetBasePath($base_path=null) + { + if (isset($base_path)) { + $base_path = eregi_replace("/$", '', $base_path); //Cutting possible last slash + $this->BasePath = $base_path; + return true; + } + return false; + } + + function GetFullPath() + { + return $this->BasePath.'/'.$this->Filename.'.tpl'; + } + + function LoadTemplate($silent=0) + { + $filename = $this->GetFullPath(); + if(file_exists($filename)) { + if (filesize ($filename) == 0) { + trigger_error("Template file size is 0: $filename", ($silent ? E_USER_NOTICE : E_USER_ERROR) ); + } + + $handle = fopen ($filename, "r"); + $contents = fread ($handle, filesize ($filename)); + $this->SetBody($contents); + fclose ($handle); + return true; + } + else { + /*global $debugger; + if($debugger) $debugger->appendTrace();*/ + trigger_error("File or block not found: $filename", ($silent ? E_USER_NOTICE : E_USER_ERROR) ); + return false; + } + } + + function SetBody($body) + { + $this->Body = $body; + } + + function GetBody() + { + return $this->Body; + } +} + +class TemplatesCache extends kBase { + var $Templates = Array(); + var $BasePath; + var $FileNames = Array(); + + var $ModulesCache = Array(); + + function TemplatesCache() + { + parent::kBase(); + $this->SetThemePath(); + $this->BasePath = DOC_ROOT.BASE_PATH.THEMES_PATH; + + $conn =& $this->Application->GetADODBConnection(); + $this->ModulesCache = $conn->GetCol('SELECT LOWER(Name) FROM '.TABLE_PREFIX.'Modules'); + } + + function SetThemePath() + { + if ( defined('SPECIAL_TEMPLATES_FOLDER') ) { + safeDefine( 'THEMES_PATH', preg_replace('/'.preg_quote(DOC_ROOT.BASE_PATH, '/').'/i','',str_replace('\\', '/', FULL_PATH)). SPECIAL_TEMPLATES_FOLDER ); + } + else { + + $current_theme = $this->Application->GetVar('m_theme'); + $folder = $this->Application->DB->GetOne('SELECT Name FROM '.TABLE_PREFIX.'Theme WHERE ThemeId = '.$current_theme); + + safeDefine( 'THEMES_PATH', preg_replace('/'.preg_quote(DOC_ROOT.BASE_PATH, '/').'/i','',str_replace('\\', '/', FULL_PATH)). ( defined('ADMIN') ? '/admin/templates' : '/themes/'.$folder)); + } + } + + function LoadTemplate($filename, $title=NULL, $silent=0) + { + if (preg_match('#^[\/]{0,1}([^\/]*)\/(.*)#', $filename, $regs)) { + $module_filename = $regs[2]; + $first_dir = $regs[1]; + } + else { + $first_dir = ''; + $module_filename = $filename; + } + + if ( defined('ADMIN') && ADMIN && in_array(strtolower($first_dir), $this->ModulesCache)) { + $path = MODULES_PATH.'/'.strtolower($first_dir).'/admin_templates'; + } + else { + $path = $this->BasePath; + $module_filename = $first_dir.'/'.$module_filename; + } + + $template =& new Template($path, $module_filename, $silent); + if (!isset($title)) $title = $filename; + $this->SetTemplate($title, $template); + } + + function GetRealFilename($filename, $base=null) { + if (preg_match('#^[\/]{0,1}([^\/]*)\/(.*)#', $filename, $regs)) { + $module_filename = $regs[2]; + $first_dir = $regs[1]; + } + else { + $first_dir = ''; + $module_filename = $filename; + } + + if ( defined('ADMIN') && ADMIN && in_array(strtolower($first_dir), $this->ModulesCache)) { + $path = MODULES_PATH.'/'.strtolower($first_dir).'/admin_templates'; + } + else { + $path = isset($base) ? $base : $this->BasePath; + $module_filename = $first_dir.'/'.$module_filename; + } + return $path.'/'.$module_filename; + } + + function SetTemplate($title, &$template, $filename=null) + { + if (!isset($filename)) $filename=$title; + $this->Templates[$title] = $template; + $this->FileNames[$title] = $filename; + } + + function &GetTemplate($title, $silent=0) + { + if (!isset($this->Templates[$title])) { + $this->LoadTemplate($title, null, $silent); + } + return $this->Templates[$title]; + } + + function GetTemplateBody($title, $silent=0) + { + $template =& $this->GetTemplate($title, $silent); + if ( !is_object($template) ) { + return ''; + } + return $template->GetBody(); + } + + function GetTemplateFileName($title) + { + return $this->FileNames[$title]; + } + + function SetTemplateBody($title, $body) + { + $template =& new Template(); + $template->SetBody($body); + $this->SetTemplate($title, $template); + } + + function ParseTemplate($template_name) + { + $Parser =& new TemplateParser($this->Application); + return $Parser->Parse( $this->GetTemplateBody($template_name) ); + } + + function TemplateExists($filename) + { + if (!preg_match("/\.tpl$/", $filename)) $filename .= '.tpl'; + $real_file = $this->GetRealFilename($filename); + return file_exists($real_file); + } +} + + +?> \ No newline at end of file Index: trunk/core/kernel/processors/tag_processor.php =================================================================== diff -u -N --- trunk/core/kernel/processors/tag_processor.php (revision 0) +++ trunk/core/kernel/processors/tag_processor.php (revision 1560) @@ -0,0 +1,161 @@ +ProcessParsedTag($tag->Tag, $tag->NP, $tag->getPrefixSpecial()); + + /*$Method=$tag->Tag; + if(method_exists($this, $Method)) + { + //echo htmlspecialchars($tag->GetFullTag()).'
'; + return $this->$Method($tag->NP); + } + else + { + if ($this->Application->hasObject('TagsAggregator')) { + $aggregator = $this->Application->recallObject('TagsAggregator'); + $tag_mapping = $aggregator->GetArrayValue($tag->Prefix, $Method); + if ($tag_mapping) { + + $mapped_tag =& new Tag('', $this->Application->Parser); + $mapped_tag->CopyFrom($tag); + $mapped_tag->Processor = $tag_mapping[0]; + $mapped_tag->Tag = $tag_mapping[1]; + $mapped_tag->NP['PrefixSpecial'] = $tag->getPrefixSpecial(); + $mapped_tag->RebuildTagData(); + return $mapped_tag->DoProcessTag(); + } + } + trigger_error('Tag '.$Method.' Undefined in '.get_class($this).'[Agregated Tag]:
'.$tag->RebuildTagData().'',E_USER_WARNING); + return false; + }*/ + } + + function ProcessParsedTag($tag, $params, $prefix) + { + $Method=$tag; + if(method_exists($this, $Method)) + { + if( $this->Application->isDebugMode() && dbg_ConstOn('DBG_SHOW_TAGS') ) + { + global $debugger; + $debugger->appendHTML('Processing PreParsed Tag '.$Method.' in '.$this->Prefix); + } + + //echo htmlspecialchars($tag->GetFullTag()).'
'; + return $this->$Method($params); + } + else + { + if ($this->Application->hasObject('TagsAggregator')) { + $aggregator = $this->Application->recallObject('TagsAggregator'); + $tmp = $this->Application->processPrefix($prefix); + $tag_mapping = $aggregator->GetArrayValue($tmp['prefix'], $Method); + if ($tag_mapping) { + $tmp = $this->Application->processPrefix($tag_mapping[0]); + $__tag_processor = $tmp['prefix'].'_TagProcessor'; + $processor =& $this->Application->recallObject($__tag_processor); + $processor->Prefix = $tmp['prefix']; + $processor->Special = $tmp['special']; + $params['PrefixSpecial'] = $prefix; + return $processor->ProcessParsedTag($tag_mapping[1], $params, $prefix); + } + trigger_error('Tag '.$Method.' Undefined in '.get_class($this).'[Agregated Tag]:
'.$tag.'', E_USER_WARNING); + } + trigger_error('Tag Undefined:
'.$tag.'',E_USER_WARNING); + return false; + } + } + + /** + * Checks if object propery value matches value passed + * + * @param Array $params + * @return bool + */ + function PropertyEquals($params) + { + $object =& $this->Application->recallObject( $this->getPrefixSpecial(), $this->Prefix, $params ); + $property_name = $params['property']; + return $object->$property_name == $params['value']; + } + + /** + * Not tag, method for parameter + * selection from list in this TagProcessor + * + * @param Array $params + * @param string $possible_names + * @return string + * @access public + */ + function SelectParam($params, $possible_names) + { + if (!is_array($params)) return; + if (!is_array($possible_names)) + + $possible_names = explode(',', $possible_names); + foreach ($possible_names as $name) + { + if( isset($params[$name]) ) return $params[$name]; + } + return false; + } + } + +/*class ProcessorsPool { + var $Processors = Array(); + var $Application; + var $Prefixes = Array(); + var $S; + + function ProcessorsPool() + { + $this->Application =& KernelApplication::Instance(); + $this->S =& $this->Application->Session; + } + + function RegisterPrefix($prefix, $path, $class) + { + // echo " RegisterPrefix $prefix, $path, $class
"; + $prefix_item = Array( + 'path' => $path, + 'class' => $class + ); + $this->Prefixes[$prefix] = $prefix_item; + } + + function CreateProcessor($prefix, &$tag) + { + // echo " prefix : $prefix
"; + if (!isset($this->Prefixes[$prefix])) + die ("Filepath and ClassName for prefix $prefix not defined while processing ".htmlspecialchars($tag->GetFullTag())."!"); + include_once($this->Prefixes[$prefix]['path']); + $ClassName = $this->Prefixes[$prefix]['class']; + $a_processor =& new $ClassName($prefix); + $this->SetProcessor($prefix, $a_processor); + } + + function SetProcessor($prefix, &$a_processor) + { + $this->Processors[$prefix] =& $a_processor; + } + + function &GetProcessor($prefix, &$tag) + { + if (!isset($this->Processors[$prefix])) + $this->CreateProcessor($prefix, $tag); + return $this->Processors[$prefix]; + } +}*/ + +?> \ No newline at end of file Index: trunk/core/kernel/globals.php =================================================================== diff -u -N --- trunk/core/kernel/globals.php (revision 0) +++ trunk/core/kernel/globals.php (revision 1560) @@ -0,0 +1,297 @@ + $sValue2) + { + $paArray1[$sKey2] = array_merge_recursive2( getArrayValue($paArray1,$sKey2), $sValue2); + } + return $paArray1; + } + + if (!function_exists('print_pre')) { + /** + * Same as print_r, budet designed for viewing in web page + * + * @param Array $data + * @param string $label + */ + function print_pre($data, $label='') + { + if( defined('DEBUG_MODE') && DEBUG_MODE ) + { + global $debugger; + if($label) $debugger->appendHTML(''.$label.''); + $debugger->dumpVars($data); + } + else + { + if($label) echo '',$label,'
'; + echo '
',print_r($data,true),'
'; + } + } + } + + if (!function_exists('getArrayValue')) { + /** + * Returns array value if key exists + * + * @param Array $array searchable array + * @param int $key array key + * @return string + * @access public + */ + // + function getArrayValue(&$array,$key) + { + $ret = isset($array[$key]) ? $array[$key] : false; + if ($ret && func_num_args() > 2) { + for ($i = 2; $i < func_num_args(); $i++) { + $cur_key = func_get_arg($i); + $ret = getArrayValue( $ret, $cur_key ); + if ($ret === false) break; + } + } + return $ret; + } + } + + /** + * Rename key in associative array, maintaining keys order + * + * @param Array $array Associative Array + * @param mixed $old Old key name + * @param mixed $new New key name + * @access public + */ + function array_rename_key(&$array, $old, $new) + { + foreach ($array as $key => $val) + { + $new_array[ $key == $old ? $new : $key] = $val; + } + $array = $new_array; + } + + /** + * Define constant if it was not already defined before + * + * @param string $const_name + * @param string $const_value + * @access public + */ + function safeDefine($const_name, $const_value) + { + if(!defined($const_name)) define($const_name,$const_value); + } + + +if( !function_exists('parse_portal_ini') ) +{ + function parse_portal_ini($file, $parse_section = false) + { + if( !file_exists($file) && !is_readable($file) ) die('Could Not Open Ini File'); + + $contents = file($file); + + $retval = Array(); + $section = ''; + $ln = 1; + $resave = false; + foreach($contents as $line) { + if ($ln == 1 && $line != '<'.'?'.'php die() ?'.">\n") { + $resave = true; + } + $ln++; + $line = trim($line); + $line = eregi_replace(';[.]*','',$line); + if(strlen($line) > 0) { + //echo $line . " - "; + if(eregi('^[[a-z]+]$',str_replace(' ', '', $line))) { + //echo 'section'; + $section = substr($line,1,(strlen($line)-2)); + if ($parse_section) { + $retval[$section] = array(); + } + continue; + } elseif(eregi('=',$line)) { + //echo 'main element'; + list($key,$val) = explode(' = ',$line); + if (!$parse_section) { + $retval[trim($key)] = str_replace('"', '', $val); + } + else { + $retval[$section][trim($key)] = str_replace('"', '', $val); + } + } //end if + //echo '
'; + } //end if + } //end foreach + if($resave) + { + $fp = fopen($file, 'w'); + reset($contents); + fwrite($fp,'<'.'?'.'php die() ?'.">\n\n"); + foreach($contents as $line) fwrite($fp,"$line"); + fclose($fp); + } + + return $retval; + } +} + + +if( !function_exists('getmicrotime') ) +{ + function getmicrotime() + { + list($usec, $sec) = explode(" ",microtime()); + return ((float)$usec + (float)$sec); + } +} + +if( !function_exists('k4_include_once') ) +{ + function k4_include_once($file) + { + if ( defined('DEBUG_MODE') && DEBUG_MODE && dbg_ConstOn('DBG_PROFILE_INCLUDES') ) + { + + if ( in_array($file, get_required_files()) ) return; + global $debugger; + + $debugger->IncludeLevel++; + $before_time = getmicrotime(); + $before_mem = memory_get_usage(); + include_once($file); + $used_time = getmicrotime() - $before_time; + $used_mem = memory_get_usage() - $before_mem; + + $debugger->IncludeLevel--; + $debugger->IncludesData['file'][] = str_replace(DOC_ROOT.BASE_PATH, '', $file); + $debugger->IncludesData['mem'][] = $used_mem; + $debugger->IncludesData['time'][] = $used_time; + $debugger->IncludesData['level'][] = $debugger->IncludeLevel; + + + } + else + { + include_once($file); + } + } +} + + /** + * Checks if string passed is serialized array + * + * @param string $string + * @return bool + */ + function IsSerialized($string) + { + if( is_array($string) ) return false; + return preg_match('/a:([\d]+):{/', $string); + } + + if (!function_exists('makepassword4')){ + + function makepassword4($length=10) + { + $pass_length=$length; + + $p1=array('b','c','d','f','g','h','j','k','l','m','n','p','q','r','s','t','v','w','x','y','z'); + $p2=array('a','e','i','o','u'); + $p3=array('1','2','3','4','5','6','7','8','9'); + $p4=array('(','&',')',';','%'); // if you need real strong stuff + + // how much elements in the array + // can be done with a array count but counting once here is faster + + $s1=21;// this is the count of $p1 + $s2=5; // this is the count of $p2 + $s3=9; // this is the count of $p3 + $s4=5; // this is the count of $p4 + + // possible readable combinations + + $c1='121'; // will be like 'bab' + $c2='212'; // will be like 'aba' + $c3='12'; // will be like 'ab' + $c4='3'; // will be just a number '1 to 9' if you dont like number delete the 3 + // $c5='4'; // uncomment to active the strong stuff + + $comb='4'; // the amount of combinations you made above (and did not comment out) + + + + for ($p=0;$p<$pass_length;) + { + mt_srand((double)microtime()*1000000); + $strpart=mt_rand(1,$comb); + // checking if the stringpart is not the same as the previous one + if($strpart<>$previous) + { + $pass_structure.=${'c'.$strpart}; + + // shortcutting the loop a bit + $p=$p+strlen(${'c'.$strpart}); + } + $previous=$strpart; + } + + + // generating the password from the structure defined in $pass_structure + for ($g=0;$g \ No newline at end of file Index: trunk/core/kernel/parser/construct_tags.php =================================================================== diff -u -N --- trunk/core/kernel/parser/construct_tags.php (revision 0) +++ trunk/core/kernel/parser/construct_tags.php (revision 1560) @@ -0,0 +1,260 @@ +StopTag = $tag; + } + + function StoreSkipMode() + { + $this->SkipMode = $this->Parser->SkipMode; + } + + function RestoreSkipMode() + { + $this->Parser->SetSkipMode($this->SkipMode); + } + + function RestoreThisLevelSkipMode() + { + $this->SkipMode = $this->Parser->Recursion[$this->Parser->RecursionIndex]->SkipMode; + } + + function SuggestSkipMode($mode) + { + if ($mode >= 1) //if we need to skip - always forcing it + $this->Parser->SetSkipMode($mode); + else { //if we need to turn of skipping + if ($this->SkipMode == parse) //check if initially skipping was off + $this->Parser->SetSkipMode(parse); //set it to off only then + } + } + + function GetLogic() + { + $check = $this->GetParam('check'); + if ($check) { + if (strpos($check, '_') !== false) { + list($prefix, $function) = explode('_', $check, 2); + } + else { + $function = $check; + $prefix = $this->Parser->GetParam('PrefixSpecial'); + } + } + else { + $prefix = $this->GetParam('prefix'); + $function = $this->GetParam('function'); + } + $this->NP['prefix'] = $prefix; + $this->NP['function'] = $function; + + $inverse = $this->GetParam('inverse'); + + if ($prefix !== false) { + $tag =& new Tag('', $this->Parser); + $tag->Tag = $function; + + $tmp = $this->Application->processPrefix($prefix); + $tag->Processor = $tmp['prefix']; + + $tag->Prefix=$tmp['prefix']; + $tag->Special=$tmp['special']; + $tag->NamedParams = $this->NP; + $this->Logic = $tag->DoProcessTag(); + // echo " this->Logic : ".$this->Logic."
"; + } + else + { + $this->Logic = $function; + } + if($inverse) $this->Logic = !$this->Logic; + } + + function Process() + { + switch ($this->Tag) { + case 'if': + case 'ifnot': + $this->GetLogic(); + + $this->SetStopTag('endif'); //This recursion level should end when 'endif' is found + $this->Parser->Recurve($this); //Saving current tag in parser recursion array + $this->StoreSkipMode(); //Storing initial SkipMode + + if ($this->Logic) { + $this->SuggestSkipMode(parse); //suggest we parse it + } + else { + $this->SuggestSkipMode(skip); //suggest we don't parse it + } + break; + case 'elseif': + $if_logic = $this->Parser->Recursion[$this->Parser->RecursionIndex]->Logic; + if (!$if_logic) { //if IF or ELSEIF above have not worked + $this->GetLogic(); + + if ($this->Logic) { //ELSEIF should run + $this->Parser->Recursion[$this->Parser->RecursionIndex]->Logic = $this->Logic; //To escape running ELSE or ELSEIF below + $this->SuggestSkipMode(parse); + } + else { //ELSEIF should NOT run + $this->SuggestSkipMode(skip); + } + } + else //IF or ELSEIF above HAVE worked - this ELSEIF should NOT run + $this->SuggestSkipMode(skip); + break; + case 'else': + $if_logic = $this->Parser->Recursion[$this->Parser->RecursionIndex]->Logic; + $this->RestoreThisLevelSkipMode(); + if (!$if_logic) { //IF was false - ELSE should run + $this->SuggestSkipMode(parse); + } + else { //IF was true - ELSE should not run + $this->SuggestSkipMode(skip); + } + break; + } + } + + function CheckRecursion(&$tag) + { + if ($this->CheckEndRecursion($tag)) { + if (defined('EXPERIMENTAL_PRE_PARSE')) { + if ($tag->Tag == 'if' || $tag->Tag == 'endif') { + $this->Parser->AppendCompiledCode('}'); + $this->Parser->ResetCode(); + } + } + $this->RestoreSkipMode(); //Restoring original SkipMode + return true; + } + else { + if (defined('EXPERIMENTAL_PRE_PARSE')) { + $this->Parser->AppendCode($tag->GetCode()); +// $this->Parser->AppendCompiledCode( $tag->GetCode() ); + } + } + return false; + } + + function CheckEndRecursion(&$tag) + { + if ($tag->GetParam('_closing_tag_') == 1 && $tag->Tag == $this->Tag) { + return true; + } + return ($tag->Tag == $this->StopTag); //Tag matches StopTag we are waiting for to close current recursion + } +} + +class BlockTag extends ConstructTag { + var $BlockName = ''; + var $InsideBlock = 0; + + function Process() + { + switch ($this->Tag) { + case 'block': + case 'DefineElement': + if ($this->Tag == 'DefineElement') { + $this->SetStopTag('end_define'); //This recursion level should end when 'blockend' is found + } + else { + $this->SetStopTag('blockend'); //This recursion level should end when 'blockend' is found + } + $this->Parser->Recurve($this); //Saving current tag in parser recursion array + $this->Parser->SetBuffer(''); + $this->BlockName = $this->NP['name']; //Stroing BlockName + if (isset($this->NP['args']) ) { + $this->Parser->Args = explode(',', $this->NP['args']); + } + $this->StoreSkipMode(); + $this->SuggestSkipMode(skip_tags); //We need to skip tags from now + break; + } + } + + function CheckRecursion(&$tag) + { + if (parent::CheckRecursion($tag)) { //if endtag matches (SkipMode would be restored then) + //Creating template from buffer + + //if (defined('EXPERIMENTAL_PRE_PARSE') && isset($this->Application->PreParsedBlocks[$this->BlockName])) return true; + + $template = new Template(); + $template->SetBody($this->Parser->GetBuffer()); + + $templates_cache =& $this->Application->recallObject('TemplatesCache'); + + //Adding template to application' cache + $templates_cache->SetTemplate($this->BlockName, $template, $this->Parser->TemplateName); + + if (defined('EXPERIMENTAL_PRE_PARSE')) { + $code = $this->Parser->GetCode(); + array_unshift($code, '$o = \'\';'); + array_unshift($code, '$application =& kApplication::Instance();'); + array_unshift($code, 'extract($params);'); + + $code[] = 'return $o;'; + + global $debugger; + + $dbg_functions = $this->Application->isDebugMode() && dbg_ConstOn('DBG_PRINT_PREPARSED'); + + $f_body = ''; + //echo "
";
+				$l = 0;
+				if ($dbg_functions) echo "function ".$this->BlockName." {
"; + foreach ($code as $line) { + $l++; + if ($dbg_functions) { + echo $l.' '.$debugger->highlightString(trim($line)."\n", true); + } + $f_body .= "\t\t".rtrim($line, "\n")."\n"; + } + if ($dbg_functions) echo "} // function ".$this->BlockName." end

"; + //echo "
"; + + //caching func body + $this->Application->PreParsedCache[$this->BlockName] = $f_body; + + $func = create_function('$params', $f_body); + $this->Application->PreParsedBlocks[$this->BlockName] = $func; + $this->Parser->Args = null; + $this->Parser->ResetCode(); + + $this->Parser->AppendCompiledFunction($this->BlockName, $f_body); + } + return true; + } + else { + // append the tag itself to the block - while in block, we check every tag to be 'blockend' + // if it is not - we need to append the tag to the buffer, which we'll parse later in 'parse_block' + if ($tag->Tag != 'block') { + if (defined('EXPERIMENTAL_PRE_PARSE') && isset($this->Application->PreParsedBlocks[$this->BlockName])) { + return; + } + if (defined('EXPERIMENTAL_PRE_PARSE')) { + // $this->Parser->AppendCode($tag->GetCode()); + } + else { + $this->Parser->AppendOutput($tag->GetFullTag()); + } + } + return false; + } + } +} + +?> \ No newline at end of file Index: trunk/core/kernel/utility/smtp_client.php =================================================================== diff -u -N --- trunk/core/kernel/utility/smtp_client.php (revision 0) +++ trunk/core/kernel/utility/smtp_client.php (revision 1560) @@ -0,0 +1,829 @@ + + ** pass - Password for authentication Default: + ** timeout - The timeout in seconds for the call Default: 5 + ** to fsockopen() + ***************************************/ + + function kSmtpClient($params = array()){ + + $this->timeout = 5; + $this->status = SMTP_STATUS_NOT_CONNECTED; + $this->host = 'localhost'; + $this->port = 25; + $this->helo = 'localhost'; + $this->auth = FALSE; + $this->user = ''; + $this->pass = ''; + $this->errors = array(); + $this->buffer = array(); + $this->debug=0; + foreach($params as $key => $value){ + $this->$key = $value; + } + } + + /*************************************** + ** Connect function. This will, when called + ** statically, create a new smtp object, + ** call the connect function (ie this function) + ** and return it. When not called statically, + ** it will connect to the server and send + ** the HELO command. + ***************************************/ + + function connect($params = array()){ + + if(!isset($this->status)) + { + $obj = new kSmtpClient($params); + if($obj->connect()){ + $obj->status = SMTP_STATUS_CONNECTED; + } + + return $obj; + + } + else + { + foreach($params as $key => $value){ + $this->$key = $value; + } + $this->connection = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout); + if(is_resource($this->connection)) + { + socket_set_timeout($this->connection, 0, 250000); + socket_set_blocking($this->connection,TRUE); + $greeting = $this->get_data(); + $this->status = SMTP_STATUS_CONNECTED; + return $this->auth ? $this->ehlo() : $this->helo(); + } + else + { + $this->errors[] = 'Failed to connect to server: '.$errstr; + return FALSE; + } + } + } + + function disconnect() + { + if(is_resource($this->connection)) + fclose($this->connection); + unset($this->connection); + $this->status=SMTP_STATUS_NOT_CONNECTED; + } + /*************************************** + ** Function which handles sending the mail. + ** Arguments: + ** $params - Optional assoc array of parameters. + ** Can contain: + ** recipients - Indexed array of recipients + ** from - The from address. (used in MAIL FROM:), + ** this will be the return path + ** headers - Indexed array of headers, one header per array entry + ** body - The body of the email + ** It can also contain any of the parameters from the connect() + ** function + ***************************************/ + + function send($params = array()){ + + foreach($params as $key => $value){ + $this->set($key, $value); + } + if($this->is_connected()){ + + // Do we auth or not? Note the distinction between the auth variable and auth() function + if($this->auth){ + if(!$this->auth()) + return FALSE; + } + $this->mail($this->from); + if(is_array($this->recipients)) + foreach($this->recipients as $value) + $this->rcpt($value); + else + $this->rcpt($this->recipients); + + if(!$this->data()) + return FALSE; + + // Transparency + $headers = str_replace($this->CRLF.'.', $this->CRLF.'..', trim(implode($this->CRLF, $this->headers))); + $body = str_replace($this->CRLF.'.', $this->CRLF.'..', $this->body); + $body = $body[0] == '.' ? '.'.$body : $body; + + $this->send_data($headers); + $this->send_data(''); + $this->send_data($body); + $this->send_data($this->CRLF."."); + + return (substr(trim($this->get_data()), 0, 3) === '250'); + }else{ + $this->errors[] = 'Not connected!'; + return FALSE; + } + } + + /*************************************** + ** Function to implement HELO cmd + ***************************************/ + + function helo(){ + if(is_resource($this->connection) + AND $this->send_data('HELO '.$this->helo) + AND substr(trim($error = $this->get_data()), 0, 3) === '250' ){ + + return TRUE; + + }else{ + $this->errors[] = 'HELO command failed, output: ' . trim(substr(trim($error),3)); + return FALSE; + } + } + + /*************************************** + ** Function to implement EHLO cmd + ***************************************/ + + function ehlo() + { + $ret_status=is_resource($this->connection) AND $this->send_data('EHLO '.$this->helo); + $success=$this->_parseResponse(250); + if(!$ret_status && $success !== true) + { + $this->errors[] = 'EHLO command failed, output: ' . trim(substr(trim($error),3)); + return FALSE; + } + + foreach ($this->_arguments as $argument) { + $verb = strtok($argument, ' '); + $arguments = substr($argument, strlen($verb) + 1, + strlen($argument) - strlen($verb) - 1); + $this->_esmtp[$verb] = $arguments; + } + + return TRUE; + + + } + + /*************************************** + ** Function to implement AUTH cmd + ***************************************/ + + function _getBestAuthMethod() + { + if ($this->authmethod) return $this->authmethod; + + if (!isset($this->_esmtp['AUTH'])) return 'NOAUTH'; + + $available_methods = explode(' ', $this->_esmtp['AUTH']); + + + + foreach ($this->auth_methods as $method) + { + if (in_array($method, $available_methods)) return $method; + } + return false; + } + + + function auth(){ + if(is_resource($this->connection)) + { + $method=$this->_getBestAuthMethod(); + + if ($method == 'NOAUTH') return true; + + switch ($method) { + case 'DIGEST-MD5': + $result = $this->_authDigest_MD5($this->user, $this->pass); + break; + case 'CRAM-MD5': + $result = $this->_authCRAM_MD5($this->user, $this->pass); + break; + case 'LOGIN': + $result = $this->_authLogin($this->user, $this->pass); + break; + case 'PLAIN': + $result = $this->_authPlain($this->user, $this->pass); + break; + default: + $this->errors[] = 'AUTH command failed: no supported authentication methods'; + return false; + break; + } + if($result!==true) + { + $this->errors[] = 'AUTH command failed: '.$result; + return FALSE; + } + return true; + + }else{ + $this->errors[] = 'AUTH command failed: ' . trim(substr(trim($error),3)); + return FALSE; + } + } + +// ============= AUTH METHODS: BEGIN ========================== +/** + * Authenticates the user using the DIGEST-MD5 method. + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * + * @return mixed Returns a PEAR_Error with an error message on any + * kind of failure, or true on success. + * @access private + * @since 1.1.0 + */ + function _authDigest_MD5($uid, $pwd) + { + $this->send_data('AUTH DIGEST-MD5'); + + /* 334: Continue authentication request */ + if(($error=$this->_parseResponse(334)) !== true) + { + /* 503: Error: already authenticated */ + if ($this->_code === 503) { + return true; + } + return $error; + } + + $challenge = base64_decode($this->_arguments[0]); + $auth_str = base64_encode($this->get_digestMD5Auth($uid, $pwd, $challenge, + $this->host, "smtp")); + + + $this->send_data($auth_str); + + /* 334: Continue authentication request */ + if(($error=$this->_parseResponse(334)) !== true) return $error; + + /* + * We don't use the protocol's third step because SMTP doesn't allow + * subsequent authentication, so we just silently ignore it. + */ + $this->send_data(' '); + + /* 235: Authentication successful */ + if(($error=$this->_parseResponse(235)) !== true) return $error; + } + + + /** + * Provides the (main) client response for DIGEST-MD5 + * requires a few extra parameters than the other + * mechanisms, which are unavoidable. + * + * @param string $authcid Authentication id (username) + * @param string $pass Password + * @param string $challenge The digest challenge sent by the server + * @param string $hostname The hostname of the machine you're connecting to + * @param string $service The servicename (eg. imap, pop, acap etc) + * @param string $authzid Authorization id (username to proxy as) + * @return string The digest response (NOT base64 encoded) + * @access public + */ + function get_digestMD5Auth($authcid, $pass, $challenge, $hostname, $service, $authzid = '') + { + $challenge = $this->_parseChallenge($challenge); + $authzid_string = ''; + if ($authzid != '') { + $authzid_string = ',authzid="' . $authzid . '"'; + } + + if (!empty($challenge)) { + $cnonce = $this->_getCnonce(); + $digest_uri = sprintf('%s/%s', $service, $hostname); + $response_value = $this->_getResponseValue($authcid, $pass, $challenge['realm'], $challenge['nonce'], $cnonce, $digest_uri, $authzid); + + return sprintf('username="%s",realm="%s"' . $authzid_string . ',nonce="%s",cnonce="%s",nc="00000001",qop=auth,digest-uri="%s",response=%s,%d', $authcid, $challenge['realm'], $challenge['nonce'], $cnonce, $digest_uri, $response_value, $challenge['maxbuf']); + } else { + return PEAR::raiseError('Invalid digest challenge'); + } + } + + /** + * Parses and verifies the digest challenge* + * + * @param string $challenge The digest challenge + * @return array The parsed challenge as an assoc + * array in the form "directive => value". + * @access private + */ + function _parseChallenge($challenge) + { + $tokens = array(); + while (preg_match('/^([a-z-]+)=("[^"]+(?send_data('AUTH CRAM-MD5'); + + /* 334: Continue authentication request */ + if(($error=$this->_parseResponse(334)) !== true) + { + /* 503: Error: already authenticated */ + if ($this->_code === 503) { + return true; + } + return $error; + } + + $challenge = base64_decode($this->_arguments[0]); + $auth_str = base64_encode($uid . ' ' . $this->_HMAC_MD5($pwd, $challenge)); + + $this->send_data($auth_str); + + /* 235: Authentication successful */ + if ( ($error = $this->_parseResponse(235)) ) { + return $error; + } + } + + /** + * Authenticates the user using the LOGIN method. + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * + * @return mixed Returns a PEAR_Error with an error message on any + * kind of failure, or true on success. + * @access private + * @since 1.1.0 + */ + function _authLogin($uid, $pwd) + { + $this->send_data('AUTH LOGIN'); + + /* 334: Continue authentication request */ + if(($error=$this->_parseResponse(334)) !== true) + { + /* 503: Error: already authenticated */ + if ($this->_code === 503) { + return true; + } + return $error; + } + + $this->send_data( base64_encode($uid) ); + + /* 334: Continue authentication request */ + if(($error=$this->_parseResponse(334)) !== true) return $error; + + $this->send_data( base64_encode($pwd) ); + + /* 235: Authentication successful */ + if (($error=$this->_parseResponse(235)) !== true) return $error; + + return true; + } + + /** + * Authenticates the user using the PLAIN method. + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * + * @return mixed Returns a PEAR_Error with an error message on any + * kind of failure, or true on success. + * @access private + * @since 1.1.0 + */ + function _authPlain($uid, $pwd) + { + $this->send_data('AUTH PLAIN'); + + /* 334: Continue authentication request */ + if(($error=$this->_parseResponse(334)) !== true) + { + /* 503: Error: already authenticated */ + if ($this->_code === 503) { + return true; + } + return $error; + } + + $auth_str = base64_encode(chr(0) . $uid . chr(0) . $pwd); + $this->send_data($auth_str); + + /* 235: Authentication successful */ + if (($error=$this->_parseResponse(235)) !== true) return $error; + + return true; + } +// ============= AUTH METHODS: END ========================== + + /** + * Function which implements HMAC MD5 digest + * + * @param string $key The secret key + * @param string $data The data to protect + * @return string The HMAC MD5 digest + */ + function _HMAC_MD5($key, $data) + { + if (strlen($key) > 64) { + $key = pack('H32', md5($key)); + } + + if (strlen($key) < 64) { + $key = str_pad($key, 64, chr(0)); + } + + $k_ipad = substr($key, 0, 64) ^ str_repeat(chr(0x36), 64); + $k_opad = substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64); + + $inner = pack('H32', md5($k_ipad . $data)); + $digest = md5($k_opad . $inner); + + return $digest; + } + + + + /** + * Read a reply from the SMTP server. The reply consists of a response + * code and a response message. + * + * @param mixed $valid The set of valid response codes. These + * may be specified as an array of integer + * values or as a single integer value. + * + * @return mixed True if the server returned a valid response code or + * a PEAR_Error object is an error condition is reached. + * + * @access private + * @since 1.1.0 + * + * @see getResponse + */ + function _parseResponse($valid) + { + + $this->_code = -1; + $this->_arguments = array(); + + if(!is_resource($this->connection)) return false; + + while ($line = fgets($this->connection, 512)) { + if ($this->debug) { + $this->debugtext.= "DEBUG: Recv: $line\n"; + } + + /* If we receive an empty line, the connection has been closed. */ + if (empty($line)) { + $this->disconnect(); + return 'Connection was unexpectedly closed'; + } + + /* Read the code and store the rest in the arguments array. */ + $code = substr($line, 0, 3); + $this->_arguments[] = trim(substr($line, 4)); + + /* Check the syntax of the response code. */ + if (is_numeric($code)) { + $this->_code = (int)$code; + } else { + $this->_code = -1; + break; + } + + /* If this is not a multiline response, we're done. */ + if (substr($line, 3, 1) != '-') { + break; + } + } + + /* Compare the server's response code with the valid code. */ + if (is_int($valid) && ($this->_code === $valid)) { + return true; + } + + /* If we were given an array of valid response codes, check each one. */ + if (is_array($valid)) { + foreach ($valid as $valid_code) { + if ($this->_code === $valid_code) { + return true; + } + } + } + + return 'Invalid response code received from server'; + } + + + /*************************************** + ** Function that handles the MAIL FROM: cmd + ***************************************/ + + function mail($from){ + + if($this->is_connected() + AND $this->send_data('MAIL FROM:'.$from.'') + AND substr(trim($this->get_data()), 0, 2) === '250' ){ + + return TRUE; + + }else + return FALSE; + } + + /*************************************** + ** Function that handles the RCPT TO: cmd + ***************************************/ + + function rcpt($to){ + + if($this->is_connected() + AND $this->send_data('RCPT TO:'.$to.'') + AND substr(trim($error = $this->get_data()), 0, 2) === '25' ){ + + return TRUE; + + }else{ + $this->errors[] = trim(substr(trim($error), 3)); + return FALSE; + } + } + + /*************************************** + ** Function that sends the DATA cmd + ***************************************/ + + function data(){ + + if($this->is_connected() + AND $this->send_data('DATA') + AND substr(trim($error = $this->get_data()), 0, 3) === '354' ){ + + return TRUE; + + }else{ + $this->errors[] = trim(substr(trim($error), 3)); + return FALSE; + } + } + + /*************************************** + ** Function to determine if this object + ** is connected to the server or not. + ***************************************/ + + function is_connected(){ + + return (is_resource($this->connection) AND ($this->status === SMTP_STATUS_CONNECTED)); + } + + /*************************************** + ** Function to send a bit of data + ***************************************/ + + function send_data($data){ + + if($this->debug) + { + $this->buffer[] = "SEND: $data\n"; + } + if ($this->debug) { + $this->debugtext.= "DEBUG: Send: $data\n"; + } + if(is_resource($this->connection)){ + return fwrite($this->connection, $data.$this->CRLF, strlen($data)+2); + }else + return FALSE; + } + + function bytes_left($fp) + { + $status = socket_get_status ($fp); + //print_r($status); + $bytes = $status["unread_bytes"]; + return $bytes; + } + + /*************************************** + ** Function to get data. + ***************************************/ + function &get_data(){ + + $return = ''; + $line = ''; + + if(is_resource($this->connection)) + { + while(strpos($return, $this->CRLF) === FALSE OR substr($line,3,1) !== ' ') + { + $line = fgets($this->connection, 512); + $return .= $line; + } + if($this->debug) + { + $this->buffer[] = "GET: ".$return."\n"; + } + return $return; + + }else + return FALSE; + } + + /*************************************** + ** Sets a variable + ***************************************/ + + function set($var, $value){ + + $this->$var = $value; + return TRUE; + } + + +} + +?> \ No newline at end of file Index: trunk/core/kernel/utility/event.php =================================================================== diff -u -N --- trunk/core/kernel/utility/event.php (revision 0) +++ trunk/core/kernel/utility/event.php (revision 1560) @@ -0,0 +1,278 @@ +Init($prefix,$special); + $this->Name = getArrayValue($params,'name'); + } + elseif ($params && is_string($params)) { + if (preg_match('/([^.:]*)[.]{0,1}([^:]*):(.*)/', $params, $regs)) { + $prefix = $regs[1]; + $special = $regs[2]; + if($prefix) $this->Init($prefix,$special); + $this->Name = $regs[3]; + } + else { + trigger_error('Invalid event string '.$params.' should be prefix[.special]:OnEvent ', E_USER_ERROR); + } + } + if (isset($specificParams)) $this->specificParams = $specificParams; + } + + function setEventParam($name,$value) + { + $this->specificParams[$name]=$value; + } + + function getEventParam($name) + { + return getArrayValue($this->specificParams, $name); + } + + function getPrefixSpecial($from_submit=false) + { + $separator=!$from_submit?'.':'_'; + $ret=$this->Prefix.$separator.$this->Special; + return rtrim($ret,$separator); + } + + /** + * Set's pseudo class that differs from + * the one specified in $Prefix + * + * @param string $appendix + * @access public + */ + function setPseudoClass($appendix) + { + $this->pseudoClass=$this->Prefix.$appendix; + } + + function Init($prefix,$special='') + { + $this->Prefix=$prefix; + $this->pseudoClass=$prefix; // default value + $this->Special=$special; + $this->Prefix_Special = rtrim($this->Prefix.'.'.$this->Special,'.'); + } + + /** + * Returns object used in event + * + * @access public + * @return kDBBase + */ + function &getObject($params=Array()) + { + return $this->Application->recallObject($this->Prefix_Special,$this->pseudoClass,$params); + } + + /** + * Calls passed event by name in current prefix/special environment + * Called event gets this event as MasterEvent, + * but its results (status and redirect* properties are copied back to current event) + * + * @param string $name EventName to call + */ + function CallSubEvent($name) + { + $child_event = new kEvent(); + $child_event->MasterEvent =& $this; + $child_event->Prefix = $this->Prefix; + $child_event->Special = $this->Special; + $child_event->Prefix_Special = $this->Prefix_Special; + $child_event->redirect = $this->redirect; + $child_event->redirect_params = $this->redirect_params; + $child_event->redirect_script = $this->redirect_script; + $child_event->Name = $name; + + $this->Application->HandleEvent( $child_event ); + + $this->status = $child_event->status; + $this->redirect = $child_event->redirect; + $this->redirect_params = $child_event->redirect_params; + $this->redirect_script = $child_event->redirect_script; + } + + /** + * Set's redirect param for event + * + * @param string $name + * @param string $value + * @access public + */ + function SetRedirectParam($name, $value) + { + $this->redirect_params[$name] = $value; + } + + /** + * Allows to merge passed redirect params hash with existing ones + * + * @param Array $params + * @access public + */ + function setRedirectParams($params) + { + $this->redirect_params = array_merge_recursive2($this->redirect_params, $params); + } + + /** + * Returns Master event name if any + * + * @return mixed + * @access public + */ + function hasMasterEvent() + { + return is_object($this->MasterEvent) ? $this->MasterEvent->Name : false; + } + + } + +?> \ No newline at end of file Index: trunk/core/kernel/utility/params.php =================================================================== diff -u -N --- trunk/core/kernel/utility/params.php (revision 0) +++ trunk/core/kernel/utility/params.php (revision 1560) @@ -0,0 +1,139 @@ +SplitParamsStr($params_str); + } + + /** + * Splits tag params into associative array + * + * @param string $params_str + * @access private + */ + function SplitParamsStr($params_str) + { + preg_match_all('/([\${}a-zA-Z0-9_.]+)=(["\']{1,1})(.*?)(? $val){ + $values[$val[1]] = str_replace('\\' . $val[2], $val[2], $val[3]); + } + $this->AddParams($values); + } + + /** + * Sets new parameter value + * + * @param string $name + * @param string $val + * @access public + */ + function Set($name, $val) + { + //echo "sessing params: [$name] = [$val] (class: ".get_class($this).")
"; +// $this->_Params[strtolower($name)] = $val; + $this->_Params[$name] = $val; + } + + /** + * Removes parameter + * + * @param string $name + * @access public + */ + function Remove($name) + { + unset($this->_Params[$name]); + } + + /** + * Gets parameter value by parameter name + * + * @param string $name + * @param int $mode + * @return string + * @access public + */ + function Get($name, $mode=FALSE_ON_NULL) + { + // echo " name : '$name' || mode : $mode
"; + //$name = strtolower($name); + if (array_key_exists($name, $this->_Params)) + return $this->_Params[$name]; + else + return $mode == FALSE_ON_NULL ? false : ''; + } + + /** + * Mass parameter setting from hash + * + * @param Array $params + * @access public + */ + function AddParams($params) + { + if (!is_array($params)) return; + /*if (count($this->_Params) == 0) { + $this->_Params = $params; + } + else {*/ + foreach ($params as $name => $val) +// $this->Set(strtolower($name), $val); + $this->Set($name, $val); + //} + } + + /** + * Return all paramters as hash + * + * @return Array + * @access public + */ + function GetParams() + { + return $this->_Params; + } +} + +class kArray extends kBase { + var $_Array; + + /** + * Returns array value with any deep key + * + * @return mixed + * @todo array_unshift doesn't accept parameters by reference, fix it's usage in this method + */ + function GetArrayValue() + { + $args = func_get_args(); + array_unshift($args, &$this->_Array); + return call_user_func_array('getArrayValue', $args); + } + + function SetArrayValue() + { + $args = func_get_args(); + $value = array_pop($args); + + $arr =& $this->_Array; + for ($i=0; $i \ No newline at end of file Index: trunk/core/kernel/utility/email.php =================================================================== diff -u -N --- trunk/core/kernel/utility/email.php (revision 0) +++ trunk/core/kernel/utility/email.php (revision 1560) @@ -0,0 +1,412 @@ +TextBoundary = uniqid(time()); + $this->EmailBoundary = uniqid(time()); + $this->LineFeed = "\r\n"; + $this->Charset='ISO-8859-1'; + $this->TransferEncoding='8bit'; + $this->Body = ''; + $this->setHeader('Subject', 'Automatically generated message'); + $this->HeadersArray=array(); + + $this->setHeader('Mime-Version', '1.0'); + } + + function setHeader($header, $value){ + $this->HeadersArray[$header] = $value; + $this->Compiled = false; + } + + function setHeaders($headers){ + $headers=str_replace("\r", "", $headers); + $headers_lines = explode("\n", $headers); + foreach ($headers_lines as $line_num => $line){ + list($header_field, $header_value) = explode(':', $line, 2); + $header_value = trim($header_value); + $this->setHeader($header_field, $header_value); + } + + } + + function appendHeader($header, $value){ + $this->HeadersArray[$header][] = $value; + $this->Compiled = false; + } + + + function setFrom($from, $name=''){ + + $this->From = $from; + + if ($name!=''){ + $from = trim($name).' <'.$from.'>'; + } + $this->setHeader('From', trim($from)); + } + + function setTo($to, $name=''){ + + $this->To = $to; + + if ($name!=''){ + $to = trim($name).' <'.$to.'>'; + } + + $this->setHeader('To', trim($to)); + } + + function setSubject($subj){ + $this->setHeader('Subject', $subj); + } + + function SetCC($to_cc){ + $this->appendHeader('CC', $to_cc); + } + + function SetBCC($to_bcc){ + $this->appendHeader('BCC', $to_bcc); + } + + function setReplyTo($reply_to){ + $this->setHeader('Reply-to', $reply_to); + } + + function setTextBody($body_text){ + + $this->BodyText = $body_text; + $this->Compiled = false; + } + + function setHTMLBody($body_html){ + + $this->BodyHtml = $body_html; + $this->IsMultipart = true; + $this->Compiled = false; + } + + function compileBody(){ + if($this->BodyHtml){ + $search = array ( + "'(<\/td>.*)[\r\n]+(.*)|(<\/p>)|(<\/div>)|(<\/tr>)'i", + "'(.*?)'si", + "''si", + "'(.*?)'si", + "''si", +// "'^[\s\n\r\t]+'", //strip all spacers & newlines in the begin of document +// "'[\s\n\r\t]+$'", //strip all spacers & newlines in the end of document + "'&(quot|#34);'i", + "'&(amp|#38);'i", + "'&(lt|#60);'i", + "'&(gt|#62);'i", + "'&(nbsp|#160);'i", + "'&(iexcl|#161);'i", + "'&(cent|#162);'i", + "'&(pound|#163);'i", + "'&(copy|#169);'i", + "'&#(\d+);'e" + ); + + $replace = array ( + "\\1\t\\2", + "\n\r", + "", + "", + "", + "", +// "", +// "", + "\"", + "&", + "<", + ">", + " ", + chr(161), + chr(162), + chr(163), + chr(169), + "chr(\\1)" + ); + + $not_html = preg_replace ($search, $replace, $this->BodyHtml); + $not_html = strip_tags($not_html); +// $not_html = $this->removeBlankLines($not_html); + // Fixing problem with add exclamation characters "!" into the body of the email. + $not_html = wordwrap($not_html, 72); + $this->BodyHtml = wordwrap($this->BodyHtml, 72); + + + //$this->Body .= 'Content-Type: multipart/alternative;'.$this->LF()." ".'boundary="----='.$this->TextBoundary.'"'.$this->LF(); + //$this->Body .= $this->LF(); + $this->Body .= '------='.$this->EmailBoundary.$this->LF(); + $this->Body .= 'Content-Type: text/plain;'.$this->LF()." ".'charset="'.$this->Charset.'"'.$this->LF(); + $this->Body .= $this->LF(); + $this->Body .= $not_html.$this->LF().$this->LF(); + $this->Body .= '------='.$this->EmailBoundary.$this->LF(); + $this->Body .= 'Content-Type: text/html;'.$this->LF()." ".'charset="'.$this->Charset.'"'.$this->LF(); + $this->Body .= 'Content-Transfer-Encoding: '.$this->TransferEncoding.$this->LF(); + $this->Body .= 'Content-Description: HTML part'.$this->LF(); + $this->Body .= 'Content-Disposition: inline'.$this->LF(); + $this->Body .= $this->LF(); + + $this->BodyHtml = str_replace("\r", "", $this->BodyHtml); + $this->BodyHtml = str_replace("\n", $this->LF(), $this->BodyHtml); + + $this->Body .= $this->BodyHtml; + $this->Body .= $this->LF().$this->LF(); + + + $this->IsMultipart = true; + + }else{ + if ($this->IsMultipart){ + $this->Body .= '------='.$this->EmailBoundary.$this->LF(); + $this->Body .= 'Content-Type: text/plain;'.$this->LF()." ".'charset="'.$this->Charset.'"'.$this->LF(); + $this->Body .= 'Content-Disposition: inline'.$this->LF(); + $this->Body .= $this->LF(); + $this->Body .= $not_html.$this->LF().$this->LF(); + }else{ + $this->BodyText = str_replace("\r", "", $this->BodyText); + $this->BodyText = str_replace("\n", $this->LF(), $this->BodyText); + $this->Body = $this->BodyText; + } + + } + + } + + + + function setCharset($charset){ + $this->Charset = $charset; + $this->Compiled = false; + } + + function setLineFeed($linefeed){ + $this->LineFeed=$linefeed; + } + + + + function attachFile($filepath, $mime="application/octet-stream"){ + + if(is_file($filepath)){ + $file_info = array('path'=>$filepath, 'mime'=>$mime); + $this->Files[] = $file_info; + }else{ + die('File "'.$filepath.'" not exist...'."\n"); + } + + $this->IsMultipart = true; + $this->Compiled = false; + } + + function LF($str=''){ + $str = str_replace("\n", "", $str); + $str = str_replace("\r", "", $str); + return $str.$this->LineFeed; + } + + function getContentType(){ + $content_type=""; + if ($this->IsMultipart){ + $content_type .= $this->LF('multipart/alternative;'); + $content_type .= $this->LF(" ".'boundary="----='.$this->EmailBoundary.'"'); + }else{ + $content_type .= $this->LF('text/plain;'); + $content_type .= $this->LF(" ".'charset='.$this->Charset); + } + + return $content_type; + + } + + // ======================================== + + function compileHeaders(){ + + $this->Headers=""; + +// $this->Headers .= "From: ".$this->LF($this->From); + //if ($this->ReplyTo){ +// $this->Headers .= "Reply-To: ".$this->ReplyTo.$this->LF(); + //} + //$this->Headers .= "To: ".$this->LF($this->To); + //$this->Headers .= "Subject: ".$this->LF($this->Subject); + /* + if (sizeof($this->CC)){ + $this->Headers .= "Cc: "; + foreach ($this->Cc as $key => $addr){ + $this->Headers.=$this->LF($addr.','); + } + } + + if (sizeof($this->BCC)){ + $this->Headers .= "Bcc: "; + foreach ($this->BCC as $key => $addr){ + $this->Headers.=$this->LF($addr.','); + } + } + */ + + foreach ($this->HeadersArray as $key => $val){ + if ($key=="To" || $key=="") continue; // avoid duplicate "To" field in headers + if (is_array($val)){ + $val = chunk_split(implode(', ', $val),72, $this->LF().' '); + + + $this->Headers .= $key.": ".$val.$this->LF(); + }else{ + $this->Headers .= $key.": ".$val.$this->LF(); + } + + } + + $this->Headers .= "Content-Type: ".$this->getContentType(); + + //$this->Headers .= "Content-Transfer-Encoding: ".$this->TransferEncoding.$this->LF(); + + + } + + function compileFiles(){ + if ($this->Files){ + foreach($this->Files as $key => $file_info) + { + $filepath = $file_info['path']; + $mime = $file_info['mime']; + $attachment_header = $this->LF('------='.$this->EmailBoundary); + $attachment_header .= $this->LF('Content-Type: '.$mime.'; name="'.basename($filepath).'"'); + $attachment_header .= $this->LF('Content-Transfer-Encoding: base64'); + $attachment_header .= $this->LF('Content-Description: '.basename($filepath)); + $attachment_header .= $this->LF('Content-Disposition: attachment; filename="'.basename($filepath).'"'); + $attachment_header .= $this->LF(''); + + $attachment_data = fread(fopen($filepath,"rb"),filesize($filepath)); + $attachment_data = base64_encode($attachment_data); + $attachment_data = chunk_split($attachment_data,72); + + $this->Body .= $attachment_header.$attachment_data.$this->LF(''); + $this->IsMultipart = true; + } + } + } + + function compile(){ + + if ($this->Compiled) return; + + $this->compileBody(); + + $this->compileFiles(); + + $this->compileHeaders(); + + // Compile + if ($this->IsMultipart){ + $this->Body .= '------='.$this->EmailBoundary.'--'.$this->LF(); + } + + $this->Compiled = true; + } + + // ======================================== + + function getHeaders(){ + + $this->Compile(); + return $this->Headers; + } + + function getBody(){ + + $this->Compile(); + return $this->Body; + } + + function send(){ + + //mail($this->HeadersArray['To'], $this->HeadersArray['Subject'], "", $this->getHeaders().$this->LF().$this->LF().$this->getBody()); + return mail($this->HeadersArray['To'], $this->HeadersArray['Subject'], "", $this->getHeaders().$this->LF().$this->LF().$this->getBody(), '-f'.$this->From); + + } + + function sendSMTP(&$smtp_object, $smtp_host, $user, $pass, $auth_used = 0){ + + $params['host'] = $smtp_host; // The smtp server host/ip + $params['port'] = 25; // The smtp server port + $params['helo'] = $_SERVER['HTTP_HOST']; // What to use when sending the helo command. Typically, your domain/hostname + $params['auth'] = TRUE; // Whether to use basic authentication or not + $params['user'] = $user; // Username for authentication + + + $params['pass'] = $pass; // Password for authentication + $params['debug'] = 1; + if ($auth_used == 0){ + $params['authmethod'] = 'NOAUTH'; // forse disabling auth + } + + + $send_params['recipients'] = array($this->To); // The recipients (can be multiple) + $send_params['headers'] = array( + 'From: '.$this->HeadersArray['From'], // Headers + 'To: '.$this->HeadersArray['To'], + 'Subject: '.$this->HeadersArray['Subject'], + 'Content-type: '.$this->getContentType(), + $this->getBody(), + ); + $send_params['from'] = $this->From; // This is used as in the MAIL FROM: cmd + // It should end up as the Return-Path: header + $send_params['body']="\r\n"; + + + if($smtp_object->connect($params) && $smtp_object->send($send_params)){ + return true; + }else { + + if (defined('DEBUG_MODE')){ + global $debugger; + $debugger->appendHTML('
'.$smtp_object->debugtext.'
'); + //define('DBG_REDIRECT', 1); + } + return false; + } + + } + + +} + + + +?> \ No newline at end of file Index: trunk/core/kernel/processors/main_processor.php =================================================================== diff -u -N --- trunk/core/kernel/processors/main_processor.php (revision 0) +++ trunk/core/kernel/processors/main_processor.php (revision 1560) @@ -0,0 +1,735 @@ +Application->recallObject('kActions'); + $actions->Set('t', $this->Application->GetVar('t')); + $actions->Set('sid', $this->Application->GetSID()); + $actions->Set('m_opener', $this->Application->GetVar('m_opener') ); + + } + + /** + * Used to handle calls where tag name + * match with existing php function name + * + * @param Tag $tag + * @return string + */ + function ProcessTag(&$tag) + { + if ($tag->Tag=='include') $tag->Tag='MyInclude'; + return parent::ProcessTag($tag); + } + + /** + * Creates HTML tag for all templates + * affects future css, js files and href params of links + * + * @return string + * @access public + */ + function Base_Ref() + { + $url = $this->Application->BaseURL().substr(THEMES_PATH,1).'/'; + return ''; + } + + /** + * Returns base url for web-site + * + * @return string + * @access public + */ + function BaseURL() + { + return $this->Application->BaseURL(); + } + + function TemplatesBase($params) + { + return $this->Application->BaseURL().THEMES_PATH; + } + + function ProjectBase($params) + { + return $this->Application->BaseURL(); + } + + /*function Base($params) + { + return $this->Application->BaseURL().$params['add']; + }*/ + + /** + * Used to create link to any template. + * use "pass" paramter if "t" tag to specify + * prefix & special of object to be represented + * in resulting url + * + * @param Array $params + * @return string + * @access public + */ + function T($params) + { + //by default link to current template + $t = $this->SelectParam($params, 't,template'); + if ($t === false) { + $t = $this->Application->GetVar('t'); + } + unset($params['t']); + unset($params['template']); + $prefix=isset($params['prefix']) ? $params['prefix'] : ''; unset($params['prefix']); + $index_file = isset($params['index_file']) ? $params['index_file'] : null; unset($params['index_file']); + + /*$pass=isset($params['pass']) ? $params['pass'] : $this->Application->GetVar('t_pass'); unset($params['pass']); + $this->Application->SetVar('t_pass', $pass); + + $pass_events = isset($params['pass_events']) && $params['pass_events'] ? 1 : 0; unset($params['pass_events']); + $this->Application->SetVar('t_pass_events', $pass_events);*/ + + //Use only implicit params passing, do not set into APP +// $this->Set($params); // set other params as application vars + return str_replace('&', '&', $this->Application->HREF($t,$prefix,$params,$index_file)); + } + + function Link($params) + { + if (isset($params['template'])) { + $params['t'] = $params['template']; + unset($params['template']); + } + if (!isset($params['pass'])) $params['pass'] = 'm'; + return $this->T($params); + } + + function Env($params) + { + $t = $params['template']; + unset($params['template']); + return $this->Application->BuildEnv($t, $params, 'm', null, false); + } + + function FormAction($params) + { + return $this->Application->ProcessParsedTag('m', 't', Array( 'pass'=>'all,m' ) ); + } + + /*// NEEDS TEST + function Config($params) + { + return $this->Application->ConfigOption($params['var']); + } + + function Object($params) + { + $name = $params['name']; + $method = $params['method']; + + $tmp =& $this->Application->recallObject($name); + if ($tmp != null) { + if (method_exists($tmp, $method)) + return $tmp->$method($params); + else + echo "Method $method does not exist in object ".get_class($tmp)." named $name
"; + } + else + echo "Object $name does not exist in the appliaction
"; + }*/ + + /** + * Tag, that always returns true. + * For parser testing purposes + * + * @param Array $params + * @return bool + * @access public + */ + function True($params) + { + return true; + } + + /** + * Tag, that always returns false. + * For parser testing purposes + * + * @param Array $params + * @return bool + * @access public + */ + function False($params) + { + return false; + } + + /** + * Returns block parameter by name + * + * @param Array $params + * @return stirng + * @access public + */ + function Param($params) + { + //$parser =& $this->Application->recallObject('TemplateParser'); + $res = $this->Application->Parser->GetParam($params['name']); + if ($res === false) $res = ''; + if (isset($params['plus'])) + $res += $params['plus']; + return $res; + } + + /** + * Compares block parameter with value specified + * + * @param Array $params + * @return bool + * @access public + */ + function ParamEquals($params) + { + //$parser =& $this->Application->recallObject('TemplateParser'); + $name = $this->SelectParam($params, 'name,var,param'); + $value = $params['value']; + return ($this->Application->Parser->GetParam($name) == $value); + } + + /*function PHP_Self($params) + { + return $HTTP_SERVER_VARS['PHP_SELF']; + } + */ + + /** + * Returns session variable value by name + * + * @param Array $params + * @return string + * @access public + */ + function Recall($params) + { + $ret = $this->Application->RecallVar( $this->SelectParam($params,'name,var,param') ); + $ret = ($ret === false && isset($params['no_null'])) ? '' : $ret; + if( getArrayValue($params,'special') || getArrayValue($params,'htmlchars')) $ret = htmlspecialchars($ret); + return $ret; + } + + // bad style to store something from template to session !!! (by Alex) + // Used here only to test how session works, nothing more + function Store($params) + { + //echo"Store $params[name]
"; + $name = $params['name']; + $value = $params['value']; + $this->Application->StoreVar($name,$value); + } + + /** + * Sets application variable value(-s) + * + * @param Array $params + * @access public + */ + function Set($params) + { + foreach ($params as $param => $value) { + $this->Application->SetVar($param, $value); + } + } + + /** + * Increment application variable + * specified by number specified + * + * @param Array $params + * @access public + */ + function Inc($params) + { + $this->Application->SetVar($params['param'], $this->Application->GetVar($params['param']) + $params['by']); + } + + /** + * Retrieves application variable + * value by name + * + * @param Array $params + * @return string + * @access public + */ + function Get($params) + { + $ret = $this->Application->GetVar($this->SelectParam($params, 'name,var,param'), EMPTY_ON_NULL); + return getArrayValue($params, 'htmlchars') ? htmlspecialchars($ret) : $ret; + } + + /** + * Retrieves application constant + * value by name + * + * @param Array $params + * @return string + * @access public + */ + function GetConst($params) + { + return defined($this->SelectParam($params, 'name,const')) ? constant($this->SelectParam($params, 'name,const,param')) : ''; + } + + /*function Config_Equals($params) + { + foreach ($params as $name => $val) { + if (in_array($name, Array( 'prefix', 'function'))) continue; + return $this->Application->ConfigOption($name) == $val; + } + return false; + }*/ + + /** + * Creates all hidden fields + * needed for kernel_form + * + * @param Array $params + * @return string + * @access public + */ + function DumpSystemInfo($params) + { + $actions =& $this->Application->recallObject('kActions'); + $actions->Set('t', $this->Application->GetVar('t') ); + + $params = $actions->GetParams(); + $o=''; + foreach ($params AS $name => $val) + { + $o .= "\n"; + } + return $o; + } + + function GetFormHiddens($params) + { + $sid = $this->Application->GetSID(); + $t = $this->SelectParam($params, 'template,t'); + unset($params['template']); + $env = $this->Application->BuildEnv($t, $params, 'm', null, false); + $o = ''; + if (defined('MOD_REWRITE') && MOD_REWRITE) { + $session =& $this->Application->recallObject('Session'); + if ($session->NeedQueryString()) { + $o .= "\n"; + } + } + else { + $o .= "\n"; + } + return $o; + } + + function Odd_Even($params) + { + $odd = $params['odd']; + $even = $params['even']; + if (!isset($params['var'])) { + $var = 'odd_even'; + } + else { + $var = $params['var']; + } + + if ($this->Application->GetVar($var) == 'even') { + $this->Application->SetVar($var, 'odd'); + return $even; + } + else { + $this->Application->SetVar($var, 'even'); + return $odd; + } + } + + /** + * Returns phrase translation by name + * + * @param Array $params + * @return string + * @access public + */ + function Phrase($params) + { + // m:phrase name="phrase_name" default="Tr-alala" updated="2004-01-29 12:49" + if (array_key_exists('default', $params)) return $params['default']; //backward compatibility + return $this->Application->Phrase($this->SelectParam($params, 'label,name,title')); + } + + // for tabs + function is_active($params) + { + $test_templ = $this->SelectParam($params, 'templ,template,t'); + if ( !getArrayValue($params,'allow_empty') ) + { + $if_true=getArrayValue($params,'true') ? $params['true'] : 1; + $if_false=getArrayValue($params,'false') ? $params['false'] : 0; + } + else + { + $if_true=$params['true']; + $if_false=$params['false']; + } + + if ( preg_match("/^".str_replace('/', '\/', $test_templ)."/", $this->Application->GetVar('t'))) { + return $if_true; + } + else { + return $if_false; + } + } + + function IsNotActive($params) + { + return !$this->is_active($params); + } + + function IsActive($params) + { + return $this->is_active($params); + } + + function is_t_active($params) + { + return $this->is_active($params); + } + + function CurrentTemplate($params) + { + return $this->is_active($params); + } + + /** + * Checks if session variable + * specified by name value match + * value passed as parameter + * + * @param Array $params + * @return string + * @access public + */ + function RecallEquals($params) + { + $name = $params['var']; + $value = $params['value']; + return ($this->Application->RecallVar($name) == $value); + } + + /** + * Checks if application variable + * specified by name value match + * value passed as parameter + * + * @param Array $params + * @return bool + * @access public + */ + function GetEquals($params) + { + $name = $this->SelectParam($params, 'var,name,param'); + $value = $params['value']; + if ($this->Application->GetVar($name) == $value) { + return 1; + } + } + + /** + * Includes template + * and returns it's + * parsed version + * + * @param Array $params + * @return string + * @access public + */ + function MyInclude($params) + { + $BlockParser =& $this->Application->makeClass('TemplateParser'); + $BlockParser->SetParams($params); + $parser =& $this->Application->Parser; + $this->Application->Parser =& $BlockParser; + + $t = $this->SelectParam($params, 't,template,block,name'); + $t = eregi_replace("\.tpl$", '', $t); + + $templates_cache =& $this->Application->recallObject('TemplatesCache'); + + $res = $BlockParser->Parse( $templates_cache->GetTemplateBody($t), $t ); + + if ( !$BlockParser->DataExists && (isset($params['data_exists']) || isset($params['block_no_data'])) ) { + if ($block_no_data = getArrayValue($params, 'block_no_data')) { + $res = $BlockParser->Parse( + $templates_cache->GetTemplateBody($block_no_data, $silent), + $t + ); + } + else { + $res = ''; + } + } + $this->Application->Parser =& $parser; + $this->Application->Parser->DataExists = $this->Application->Parser->DataExists || $BlockParser->DataExists; + return $res; + } + + /*function Kernel_Scripts($params) + { + return ''; + }*/ + + + /*function GetUserPermission($params) + { + // echo"GetUserPermission $params[name]"; + if ($this->Application->RecallVar('user_type') == 1) + return 1; + else { + $perm_name = $params[name]; + $aPermissions = unserialize($this->Application->RecallVar('user_permissions')); + if ($aPermissions) + return $aPermissions[$perm_name]; + } + }*/ + + + /** + * Set's parser block param value + * + * @param Array $params + * @access public + */ + function AddParam($params) + { + $parser =& $this->Application->Parser; // recallObject('TemplateParser'); + foreach ($params as $param => $value) { + $this->Application->SetVar($param, $value); + $parser->SetParam($param, $value); + $parser->AddParam('/\$'.$param.'/', $value); + } + } + + /*function ParseToVar($params) + { + $var = $params['var']; + $tagdata = $params['tag']; + $parser =& $this->Application->Parser; //recallObject('TemplateParser'); + $res = $this->Application->ProcessTag($tagdata); + + $parser->SetParam($var, $res); + $parser->AddParam('/\$'.$var.'/', $res); + return ''; + }*/ + + /*function TagNotEmpty($params) + { + $tagdata = $params['tag']; + $res = $this->Application->ProcessTag($tagdata); + return $res != ''; + }*/ + + /*function TagEmpty($params) + { + return !$this->TagNotEmpty($params); + }*/ + + /** + * Parses block and returns result + * + * @param Array $params + * @return string + * @access public + */ + function ParseBlock($params) + { + $parser =& $this->Application->Parser; // recallObject('TemplateParser'); + return $parser->ParseBlock($params); + } + + /** + * Checks if debug mode is on + * + * @return bool + * @access public + */ + function IsDebugMode() + { + return $this->Application->isDebugMode(); + } + + function MassParse($params) + { + $qty = $params['qty']; + $block = $params['block']; + $mode = $params['mode']; + + $o = ''; + if ($mode == 'func') { + $func = create_function('$params', ' + $o = \'\'; + $o.= \'a\'.$params[\'param1\'].\'\'; + $o.= \'a\'.$params[\'param2\'].\'\'; + $o.= \'a\'.$params[\'param3\'].\'\'; + $o.= \'a\'.$params[\'param4\'].\'\'; + $o.= \'\'; + return $o; + '); + for ($i=1; $i<$qty; $i++) { + $block_params['param1'] = rand(1, 10000); + $block_params['param2'] = rand(1, 10000); + $block_params['param3'] = rand(1, 10000); + $block_params['param4'] = rand(1, 10000); + $o .= $func($block_params); + } + return $o; + } + + $block_params['name'] = $block; + + for ($i=0; $i<$qty; $i++) { + $block_params['param1'] = rand(1, 10000); + $block_params['param2'] = rand(1, 10000); + $block_params['param3'] = rand(1, 10000); + $block_params['param4'] = rand(1, 10000); + $block_params['passed'] = $params['passed']; + $block_params['prefix'] = 'm'; + + $o.= $this->Application->ParseBlock($block_params, 1); + } + return $o; + } + + function AfterScript($params) + { + $after_script = $this->Application->GetVar('after_script'); + if ( $after_script ) { + return ''; + } + return ''; + } + + function LoggedIn($params) + { + return $this->Application->LoggedIn(); + } + + /** + * Checks if user is logged in and if not redirects it to template passed + * + * @param Array $params + */ + function RequireLogin($params) + { + if($permission_groups = getArrayValue($params, 'permissions')) + { + $permission_groups = explode('|', $permission_groups); + $group_has_permission = false; + foreach($permission_groups as $permission_group) + { + $permissions = explode(',', $permission_group); + $has_permission = true; + foreach($permissions as $permission) + { + $has_permission = $has_permission && $this->Application->CheckPermission($permission); + } + $group_has_permission = $group_has_permission || $has_permission; + + if($group_has_permission) + { + return; + } + } + + if( !$this->Application->LoggedIn() ) + { + $t = $this->Application->GetVar('t'); + $this->Application->Redirect( $params['login_template'], Array('next_template'=>$t) ); + } + else + { + $this->Application->Redirect( $params['no_permissions_template'] ); + } + } + + $condition = getArrayValue($params,'condition'); + if(!$condition) + { + $condition = true; + } + else + { + if( substr($condition,0,1) == '!' ) + { + $condition = !$this->Application->ConfigValue( substr($condition,1) ); + } + else + { + $condition = $this->Application->ConfigValue($condition); + } + + } + + if( !$this->Application->LoggedIn() && $condition ) + { + $t = $this->Application->GetVar('t'); + $this->Application->Redirect( $params['login_template'], Array('next_template'=>$t) ); + } + } + + function SaveReturnScript($params) + { + // admin/save_redirect.php?do= + $url = str_replace($this->Application->BaseURL(), '', $this->T($params) ); + $url = explode('?', $url, 2); + $url = 'save_redirect.php?'.$url[1].'&do='.$url[0]; + + $this->Application->StoreVar('ReturnScript', $url); + } + +/* + function Login($params) + { + $user_prefix = 'users'; + $this->parser->registerprefix($user_prefix); + $user_class = $this->parser->processors[$user_prefix]->item_class; + + $candidate = new $user_class(NULL, $this->parser->processors[$user_prefix]); + //print_pre($this->Session->Property); + + $special = array_shift($params); + //echo"$special
"; + $candidate_id = $candidate->Login($this->Session->GetProperty('username'), $this->Session->GetProperty('password'), $special); + + if ($candidate_id !== false) { + $this->Session->SetField('user_id', $candidate_id); + $this->Session->Update(); + $this->Session->AfterLogin(); + + $this->parser->register_prefix('m'); + $template = array_shift($params); + if ($template == '') $template = 'index'; + $location = $this->parser->do_process_tag('m', 't', Array($template)); + header("Location: $location"); + exit; + } + elseif ($this->Session->GetProperty('username') != '') { + $this->Session->SetProperty('login_error', 'Incorrect username or password'); + } + } + */ + +} + + +?> \ No newline at end of file Index: trunk/core/kernel/kbase.php =================================================================== diff -u -N --- trunk/core/kernel/kbase.php (revision 0) +++ trunk/core/kernel/kbase.php (revision 1560) @@ -0,0 +1,454 @@ +Application =& kApplication::Instance(); + } + + /** + * Create new instance of object + * + * @return kBase + */ + function &makeClass() + { + return new kBase(); + } + + /** + * Set's prefix and special + * + * @param string $prefix + * @param string $special + * @access public + */ + function Init($prefix,$special,$event_params=null) + { + $prefix=explode('_',$prefix,2); + $this->Prefix=$prefix[0]; + $this->Special=$special; + $this->OriginalParams = $event_params; + } + + /** + * Returns joined prefix + * and special if any + * + * @return string + * @access protected + */ + function getPrefixSpecial() + { + return rtrim($this->Prefix.'.'.$this->Special,'.'); + } + + function &getProperty($property_name) + { + return $this->$property_name; + } + + function setProperty($property_name, &$property_value) + { + $this->$property_name =& $property_value; + } +} + +class kDBBase extends kBase { + /** + * Description + * + * @var DBConnection + * @access public + */ + var $Conn; + + /** + * Description + * + * @var string Name of primary key field for the item + * @access public + */ + var $IDField; + + /** + * Holds SELECT, FROM, JOIN parts of SELECT query + * + * @var string + * @access public + */ + var $SelectClause; + + /** + * Display queries executed by the class + * + * @var bool + * @access public + */ + var $DisplayQueries = false; + + /** + * Fields allowed to be set (from table + virtual) + * + * @var Array + * @access private + */ + var $Fields=Array(); + + /** + * All virtual field names + * + * @var Array + * @access private + */ + var $VirtualFields=Array(); + + /** + * Fields that need to be queried using custom expression, e.g. IF(...) AS value + * + * @var Array + * @access private + */ + var $CalculatedFields = Array(); + + /** + * Description + * + * @var string Item' database table name, without prefix + * @access public + */ + var $TableName; + + /** + * Allows to determine object's table status ('temp' - temp table, '' - live table) + * + * @var string + * @access public + */ + var $mode=''; + + function kDBBase() + { + parent::kBase(); + $this->Conn =& $this->Application->GetADODBConnection(); + } + + /** + * Set current item' database table name + * + * @access public + * @param string $table_name + * @return void + */ + function setTableName($table_name) + { + $this->TableName = $table_name; + } + + /** + * Set object' TableName to Live table from config + * + * @access public + */ + function SwitchToLive() + { + $this->TableName = $this->Application->getUnitOption($this->Prefix, 'TableName'); + } + + /** + * Set object' TableName to Temp table from config + * + * @access public + */ + function SwitchToTemp() + { + $this->TableName = $this->Application->getUnitOption($this->Prefix, 'TableName'); + $this->SetTableName( kTempTablesHandler::GetTempName($this->TableName) ); + $this->mode = 't'; + } + + /** + * Checks if object uses temp table + * + * @return bool + */ + function IsTempTable() + { + return kTempTablesHandler::IsTempTable($this->TableName); + } + + /** + * Sets SELECT part of list' query + * + * @access public + * @param string $sql SELECT and FROM [JOIN] part of the query up to WHERE + * @return void + */ + function SetSelectSQL($sql) + { + $this->SelectClause = $sql; + } + + function GetSelectSQL($base_query=null) + { + if( !isset($base_query) ) $base_query = $this->SelectClause; + return $q = str_replace( Array('%1$s','%s'), $this->TableName, $base_query); + } + + /** + * Insert calculated fields sql into query in place of %2$s, + * return processed query. + * + * @param string $query + * @return string + */ + function addCalculatedFields($query) + { + if($this->CalculatedFields) + { + $sql = Array(); + foreach($this->CalculatedFields as $field_name => $field_expression) + { + $sql[] = '('.$field_expression.') AS '.$field_name; + } + $sql = implode(',',$sql); + return $this->Application->ReplaceLanguageTags( str_replace('%2$s', ','.$sql, $query) ); + } + else + { + return str_replace('%2$s', '', $query); + } + } + + /** + * Sets ID Field name used as primary key for loading items + * + * @access public + * @param string $field_name + * @return void + * @see kDBBase::IDField + */ + function setIDField($field_name) + { + $this->IDField = $field_name; + } + + function Configure() + { + $this->setTableName( $this->Application->getUnitOption($this->Prefix, 'TableName') ); + $this->setIDField( $this->Application->getUnitOption($this->Prefix, 'IDField') ); + $this->setConfigFields( $this->Application->getUnitOption($this->Prefix, 'Fields') ); + $this->setVirtualFields( $this->Application->getUnitOption($this->Prefix, 'VirtualFields') ); + $this->setCalculatedFields( $this->Application->getUnitOption($this->Prefix, 'CalculatedFields') ); + $this->prepareConfigOptions(); // this should go last, but before setDefaultValues, order is significant! + $this->SetDefaultValues(); + } + + function setCalculatedFields($fields) + { + $this->CalculatedFields = isset($fields[$this->Special]) ? $fields[$this->Special] : (isset($fields['']) ? $fields[''] : false); + } + + /** + * Set's field names from table + * from config + * + * @param Array $fields + * @access public + */ + function setConfigFields($fields) + { + $this->Fields=$fields; + } + + /** + * Set fields (+options) for fields that physically doesn't exist in database + * + * @param Array $fields + * @access public + */ + function setVirtualFields($fields) + { + if($fields) + { + $this->VirtualFields = $fields; + $this->Fields = array_merge_recursive2($this->VirtualFields, $this->Fields); + } + } + + function SetDefaultValues() + { + + } + + function SetFieldOptions($field, $options) + { + $this->Fields[$field] = $options; + } + + function GetFieldOptions($field) + { + return isset($this->Fields[$field]) ? $this->Fields[$field] : Array(); + } + + /** + * Returns formatted field value + * + * @param string $field + * @return string + * @access public + */ + function GetField($name, $format=null) + { + $options = $this->GetFieldOptions($name); + $val = $this->GetDBField($name); + $res = $val; + if (isset($options['formatter'])) { + $formatter =& $this->Application->recallObject($options['formatter']); + $res = $formatter->Format($val, $name, $this, $format ); + } + return $res; + } + + function HasField($name) + { + + } + + function GetFieldValues() + { + + } + + function UpdateFormattersSubFields() + { + foreach ($this->Fields as $field => $options) { + if (isset($options['formatter'])) { + $formatter =& $this->Application->recallObject($options['formatter']); + $formatter->UpdateSubFields($field, $this->GetDBField($field), $options, $this); + } + } + } + + function prepareConfigOptions() + { + foreach (array_keys($this->Fields) as $field_name) + { + $field_options =& $this->Fields[$field_name]; + if( isset($field_options['options_sql']) ) + { + // replace with query result + $select_clause = $field_options['option_title_field'].','.$field_options['option_key_field']; + $sql = sprintf($field_options['options_sql'], $select_clause); + + $options_hash = getArrayValue($field_options,'options'); + if($options_hash === false) $options_hash = Array(); + + $dynamic_options = $this->Conn->GetCol($sql, $field_options['option_key_field']); + $field_options['options'] = array_merge_recursive2($options_hash, $dynamic_options); + + unset($field_options['options_sql']); + } + $this->PrepareOptions($field_name); + } + } + + function PrepareOptions($field_name) + { + if( $formatter_class = getArrayValue($this->Fields[$field_name], 'formatter') ) + { + $formatter =& $this->Application->recallObject($formatter_class); + $formatter->PrepareOptions($field_name, $this->Fields[$field_name], $this); + } + } + + /** + * Returns unformatted field value + * + * @param string $field + * @return string + * @access public + */ + function GetDBField($field) + { + + } + + /** + * Returns ID of currently processed record + * + * @return int + * @access public + */ + function GetID() + { + return $this->GetDBField($this->IDField); + } + + /** + * Returns parent table information + * + * @param bool $from_temp load parent item from temp table + * @return Array + */ + function getLinkedInfo() + { + $parent_prefix = $this->Application->getUnitOption($this->Prefix, 'ParentPrefix'); + if ( $parent_prefix ) + { + // if this is linked table, then set id from main table + $table_info = Array( + 'TableName' => $this->Application->getUnitOption($this->Prefix,'TableName'), + 'IdField' => $this->Application->getUnitOption($this->Prefix,'IDField'), + 'ForeignKey' => $this->Application->getUnitOption($this->Prefix,'ForeignKey'), + 'ParentTableKey' => $this->Application->getUnitOption($this->Prefix,'ParentTableKey'), + 'ParentPrefix' => $parent_prefix + ); + + $main_object = $this->Application->recallObject($parent_prefix); + return array_merge($table_info, Array('ParentId'=> $main_object->GetDBField( $table_info['ParentTableKey'] ) ) ); + } + return false; + } + +} + +?> \ No newline at end of file