Index: branches/5.2.x/core/kernel/event_handler.php =================================================================== --- branches/5.2.x/core/kernel/event_handler.php (revision 14674) +++ branches/5.2.x/core/kernel/event_handler.php (revision 14675) @@ -1,191 +1,191 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); /** * Note: * 1. When adressing variables from submit containing * Prefix_Special as part of their name use * $event->getPrefixSpecial(true) instead of * $event->getPrefixSpecial() 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 the form which contains $Prefix_Special then note 1st item. * Example: LinkVar($event->getPrefixSpecial(true).'_varname', $event->getPrefixSpecial().'_varname') * */ /** * Default event handler. Mostly abstract class * */ class kEventHandler extends kBase { /** * In case if event should be handled with method, which name differs from * event name, then it should be specified here. * key - event name, value - event method * * @var Array * @access protected */ protected $eventMethods = Array (); /** * Defines mapping vs event names and permission names * * @var Array * @access protected */ protected $permMapping = Array (); public function __construct() { parent::__construct(); $this->mapEvents(); $this->mapPermissions(); } /** * Define alternative event processing method names * * @see kEventHandler::$eventMethods * @access protected */ protected function mapEvents() { } /** - * Allows to override standart permission mapping + * Allows to override standard permission mapping * * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { } /** * Returns prefix and special (when present) joined by a "." * * @return string * @access private */ public function getPrefixSpecial() { throw new Exception('Usage of getPrefixSpecial() method is forbidden in kEventHandler class children. Use $event->getPrefixSpecial(true); instead'); } /** * Executes event, specified in $event variable * * @param kEvent $event * @access public */ public function processEvent(&$event) { $event_name = $event->Name; if ( array_key_exists($event_name, $this->eventMethods) ) { $event_name = $this->eventMethods[$event_name]; } if ( method_exists($this, $event_name) ) { $this->$event_name($event); } else { throw new Exception('Event <strong>' . $event->Name . '</strong> not implemented in class <strong>' . get_class($this) . '</strong>'); } } /** * Sample dummy event * * @param kEvent $event * @access protected */ protected function OnBuild(&$event) { } /** * Returns to previous template in opener stack * * @param kEvent $event * @access protected */ protected function OnGoBack(&$event) { $url = $this->Application->RecallVar('export_finish_url'); if ($url) { $this->Application->Redirect('external:' . $url); } $event->SetRedirectParam('opener', 'u'); } /** * Apply some special processing to object being * recalled before using it in other events that * call prepareObject * * @param kDBItem|kDBList $object * @param kEvent $event * @return void * @access protected */ protected function prepareObject(&$object, &$event) { } /** * Checks user permission to execute given $event * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(&$event) { $perm_helper =& $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ return $perm_helper->CheckEventPermission($event, $this->permMapping); } /** * Occurs, when config was parsed, allows to change config data dynamically * * @param kEvent $event */ protected function OnAfterConfigRead(&$event) { } } \ No newline at end of file Index: branches/5.2.x/core/kernel/nparser/nparser.php =================================================================== --- branches/5.2.x/core/kernel/nparser/nparser.php (revision 14674) +++ branches/5.2.x/core/kernel/nparser/nparser.php (revision 14675) @@ -1,1202 +1,1204 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); include_once(KERNEL_PATH.'/nparser/ntags.php'); define('TAG_NAMESPACE', 'inp2:'); define('TAG_NAMESPACE_LENGTH', 5); class NParser extends kBase { var $Stack = Array (); var $Level = 0; var $Buffers = array(); var $InsideComment = false; /** * Parse tags inside HTML comments * * @var bool */ var $SkipComments = true; var $Params = array(); var $ParamsStack = array(); var $ParamsLevel = 0; var $Definitions = ''; /** * Holds dynamic elements to function names mapping during execution * * @var Array */ var $Elements = Array (); /** * Holds location of element definitions inside templates. * key - element function name, value - array of 2 keys: {from_pos, to_pos} * * @var Array */ var $ElementLocations = Array (); var $DataExists = false; var $TemplateName = null; var $TempalteFullPath = null; var $CachePointers = Array (); var $Cachable = Array (); /** * Deep level during parsing - * + * * @var int */ var $CacheLevel = 0; /** * Caching in templates enabled * * @var bool */ var $CachingEnabled = false; /** * Completely cache given page * * @var bool */ var $FullCachePage = false; /** * Prefixes, that are used on current page * * @var Array */ var $PrefixesInUse = Array (); /** * Parser parameter names, that are created via m_Capture tag are listed here * * @var Array */ var $Captures = array(); /** * Phrases, used on "Edit" buttons, that parser adds during block decoration * * @var Array */ var $_btnPhrases = Array (); /** * Mod-rewrite system enabled * * @var bool */ var $RewriteUrls = false; /** * Current user is logged-in * * @var bool */ var $UserLoggedIn = false; /** * Creates template parser object * * @access public */ public function __construct() { parent::__construct(); if (defined('EDITING_MODE') && (EDITING_MODE == EDITING_MODE_DESIGN)) { $this->_btnPhrases['design'] = $this->Application->Phrase('la_btn_EditDesign', false, true); $this->_btnPhrases['block'] = $this->Application->Phrase('la_btn_EditBlock', false, true); } $this->RewriteUrls = $this->Application->RewriteURLs(); $this->UserLoggedIn = $this->Application->LoggedIn(); // cache only Front-End templated, when memory caching is available and template caching is enabled in configuration $this->CachingEnabled = !$this->Application->isAdmin && $this->Application->ConfigValue('SystemTagCache') && $this->Application->isCachingType(CACHING_TYPE_MEMORY); } function Compile($pre_parsed, $template_name = 'unknown') { $data = file_get_contents($pre_parsed['tname']); if (!$this->CompileRaw($data, $pre_parsed['tname'], $template_name)) { // compilation failed during errors in template // trigger_error('Template "<strong>' . $template_name . '</strong>" not compiled because of errors', E_USER_WARNING); return false; } // saving compiled version (only when compilation was successful) $this->Application->TemplatesCache->saveTemplate($pre_parsed['fname'], $this->Buffers[0]); return true; } function Parse($raw_template, $name = null) { $this->CompileRaw($raw_template, $name); ob_start(); $_parser =& $this; eval('?'.'>'.$this->Buffers[0]); return ob_get_clean(); } function CompileRaw($data, $t_name, $template_name = 'unknown') { $code = "extract (\$_parser->Params);\n"; $code .= "\$_parser->ElementLocations['{$template_name}'] = Array('template' => '{$template_name}', 'start_pos' => 0, 'end_pos' => " . strlen($data) . ");\n"; // $code .= "__@@__DefinitionsMarker__@@__\n"; // $code .= "if (!\$this->CacheStart('".abs(crc32($t_name))."_0')) {\n"; $this->Buffers[0] = '<?'."php $code ?>\n"; $this->Cacheable[0] = true; $this->Definitions = ''; // finding all the tags $reg = '(.*?)(<[\\/]?)' . TAG_NAMESPACE . '([^>]*?)([\\/]?>)(\r\n){0,1}'; preg_match_all('/'.$reg.'/s', $data, $results, PREG_SET_ORDER + PREG_OFFSET_CAPTURE); $this->InsideComment = false; foreach ($results as $tag_data) { $tag = array( 'opening' => $tag_data[2][0], 'tag' => $tag_data[3][0], 'closing' => $tag_data[4][0], 'line' => substr_count(substr($data, 0, $tag_data[2][1]), "\n")+1, 'pos' => $tag_data[2][1], 'file' => $t_name, 'template' => $template_name, ); // the idea is to count number of comment openings and closings before current tag // if the numbers do not match we inverse the status of InsideComment if ($this->SkipComments && (substr_count($tag_data[1][0], '<!--') != substr_count($tag_data[1][0], '-->'))) { $this->InsideComment = !$this->InsideComment; } // appending any text/html data found before tag $this->Buffers[$this->Level] .= $tag_data[1][0]; if (!$this->InsideComment) { $tmp_tag = $this->Application->CurrentNTag; $this->Application->CurrentNTag = $tag; if ($this->ProcessTag($tag) === false) { $this->Application->CurrentNTag = $tmp_tag; return false; } $this->Application->CurrentNTag = $tmp_tag; } else { $this->Buffers[$this->Level] .= $tag_data[2][0].$tag_data[3][0].$tag_data[4][0]; } } if ($this->Level > 0) { $error_tag = Array ( 'file' => $this->Stack[$this->Level]->Tag['file'], 'line' => $this->Stack[$this->Level]->Tag['line'], ); throw new ParserException('Unclosed tag opened by ' . $this->TagInfo($this->Stack[$this->Level]->Tag), 0, null, $error_tag); return false; } // appending text data after last tag (after its closing pos), // if no tag was found at all ($tag_data is not set) - append the whole $data $this->Buffers[$this->Level] .= isset($tag_data) ? substr($data, $tag_data[4][1]+strlen($tag_data[4][0])) : $data; $this->Buffers[$this->Level] = preg_replace('/<!--##(.*?)##-->/s', '', $this->Buffers[$this->Level]); // remove hidden comments IB#23065 // $this->Buffers[$this->Level] .= '<?'.'php '."\n\$_parser->CacheEnd();\n}\n"." ?".">\n"; // $this->Buffers[$this->Level] = str_replace('__@@__DefinitionsMarker__@@__', $this->Definitions, $this->Buffers[$this->Level]); return true; } function SplitParamsStr($params_str) { preg_match_all('/([\${}a-zA-Z0-9_.\\-\\\\#\\[\\]]+)=(["\']{1,1})(.*?)(?<!\\\)\\2/s', $params_str, $rets, PREG_SET_ORDER); $values = Array(); // we need to replace all occurences of any current param $key with {$key} for correct variable substitution foreach ($rets AS $key => $val){ $values[$val[1]] = str_replace('\\' . $val[2], $val[2], $val[3]); } return $values; } function SplitTag($tag) { if (!preg_match('/([^_ \t\r\n]*)[_]?([^ \t\r\n]*)[ \t\r\n]*(.*)$$/s', $tag['tag'], $parts)) { // this is virtually impossible, but just in case throw new ParserException('Incorrect tag format: ' . $tag['tag'], 0, null, $tag); return false; } $splited['prefix'] = $parts[2] ? $parts[1] : '__auto__'; $splited['name'] = $parts[2] ? $parts[2] : $parts[1]; $splited['attrs'] = $parts[3]; return $splited; } function ProcessTag($tag) { $splited = $this->SplitTag($tag); if ($splited === false) { return false; } $tag = array_merge($tag, $splited); $tag['processed'] = false; $tag['NP'] = $this->SplitParamsStr($tag['attrs']); $o = ''; $tag['is_closing'] = $tag['opening'] == '</' || $tag['closing'] == '/>'; if (class_exists('_Tag_'.$tag['name'])) { // block tags should have special handling class if ($tag['opening'] == '<') { $class = '_Tag_'.$tag['name']; $instance = new $class($tag); $instance->Parser =& $this; /* @var $instance _BlockTag */ $this->Stack[++$this->Level] =& $instance; $this->Buffers[$this->Level] = ''; $this->Cachable[$this->Level] = true; $open_code = $instance->Open($tag); if ($open_code === false) { return false; } $o .= $open_code; } if ($tag['is_closing']) { // not ELSE here, because tag may be <empty/> and still has a handler-class if ($this->Level == 0) { $dump = array(); foreach ($this->Stack as $instance) { $dump[] = $instance->Tag; } if ( $this->Application->isDebugMode() ) { $this->Application->Debugger->dumpVars($dump); } $error_msg = 'Closing tag without an opening: ' . $this->TagInfo($tag) . ' - <strong>probably opening tag was removed or nested tags error</strong>'; throw new ParserException($error_msg, 0, null, $tag); return false; } if ($this->Stack[$this->Level]->Tag['name'] != $tag['name']) { $opening_tag = $this->Stack[$this->Level]->Tag; $error_msg = ' Closing tag ' . $this->TagInfo($tag) . ' does not match opening tag at current nesting level (' . $this->TagInfo($opening_tag) . ' opened at line ' . $opening_tag['line'] . ')'; throw new ParserException($error_msg, 0, null, $tag); return false; } $o .= $this->Stack[$this->Level]->Close($tag); // DO NOT use $this->Level-- here because it's used inside Close $this->Level--; } } else { // regular tags - just compile if (!$tag['is_closing']) { $error_msg = 'Tag without a handler: ' . $this->TagInfo($tag) . ' - <strong>probably missing <empty <span style="color: red">/</span>> tag closing</strong>'; throw new ParserException($error_msg, 0, null, $tag); return false; } if ($this->Level > 0) $o .= $this->Stack[$this->Level]->PassThrough($tag); if (!$tag['processed']) { $compiled = $this->CompileTag($tag); if ($compiled === false) return false; if (isset($tag['NP']['cachable']) && (!$tag['NP']['cachable'] || $tag['NP']['cachable'] == 'false')) { $this->Cachable[$this->Level] = false; } $o .= '<?'.'php ' . $compiled . " ?>\n"; // $o .= '<?'.'php '; // $o .= (isset($tag['NP']['cachable']) && (!$tag['NP']['cachable'] || $tag['NP']['cachable'] == 'false')) ? $this->BreakCache($compiled, $this->GetPointer($tag)) : $compiled; // $o .= " ?".">\n"; } } $this->Buffers[$this->Level] .= $o; return true; } function GetPointer($tag) { return abs(crc32($tag['file'])).'_'.$tag['line']; } function BreakCache($code, $pointer, $condition='') { return "\$_parser->CacheEnd();\n}\n" . $code."\nif ( !\$_parser->CacheStart('{$pointer}'" . ($condition ? ", {$condition}" : '') . ") ) {\n"; } function TagInfo($tag, $with_params=false) { return "<b>{$tag['prefix']}_{$tag['name']}".($with_params ? ' '.$tag['attrs'] : '')."</b>"; } function CompileParamsArray($arr) { $to_pass = 'Array('; foreach ($arr as $name => $val) { $to_pass .= '"'.$name.'" => "'.str_replace('"', '\"', $val).'",'; } $to_pass .= ')'; return $to_pass; } function CompileTag($tag) { $code = ''; $to_pass = $this->CompileParamsArray($tag['NP']); if ($tag['prefix'] == '__auto__') { $prefix = $this->GetParam('PrefixSpecial'); $code .= '$_p_ =& $_parser->GetProcessor($PrefixSpecial);'."\n"; $code .= 'echo $_p_->ProcessParsedTag(\''.$tag['name'].'\', '.$to_pass.', "$PrefixSpecial", \''.$tag['file'].'\', '.$tag['line'].');'."\n"; } else { $prefix = $tag['prefix']; $code .= '$_p_ =& $_parser->GetProcessor("'.$tag['prefix'].'");'."\n"; $code .= 'echo $_p_->ProcessParsedTag(\''.$tag['name'].'\', '.$to_pass.', "'.$tag['prefix'].'", \''.$tag['file'].'\', '.$tag['line'].');'."\n"; } if (array_key_exists('result_to_var', $tag['NP']) && $tag['NP']['result_to_var']) { $code .= "\$params['{$tag['NP']['result_to_var']}'] = \$_parser->GetParam('{$tag['NP']['result_to_var']}');\n"; $code .= "\${$tag['NP']['result_to_var']} = \$params['{$tag['NP']['result_to_var']}'];\n"; } if ($prefix && strpos($prefix, '$') === false) { $p =& $this->GetProcessor($prefix); if (!is_object($p) || !$p->CheckTag($tag['name'], $tag['prefix'])) { $error_msg = 'Unknown tag: ' . $this->TagInfo($tag) . ' - <strong>incorrect tag name or prefix</strong>'; throw new ParserException($error_msg, 0, null, $tag); return false; } } return $code; } function CheckTemplate($t, $silent = null) { $pre_parsed = $this->Application->TemplatesCache->GetPreParsed($t); if (!$pre_parsed) { if (!$silent) { throw new ParserException('Cannot include "<strong>' . $t . '</strong>" - file does not exist'); } return false; } $force_compile = defined('DBG_NPARSER_FORCE_COMPILE') && DBG_NPARSER_FORCE_COMPILE; if (!$pre_parsed || !$pre_parsed['active'] || $force_compile) { $inc_parser = new NParser(); if ($force_compile) { // remove Front-End theme markings during total compilation $t = preg_replace('/^theme:.*?\//', '', $t); } if (!$inc_parser->Compile($pre_parsed, $t)) { return false; } } return $pre_parsed; } function Run($t, $silent = null) { if ((strpos($t, '../') !== false) || (trim($t) !== $t)) { // when relative paths or special chars are found template names from url, then it's hacking attempt return false; } $pre_parsed = $this->CheckTemplate($t, $silent); if (!$pre_parsed) { return false; } $backup_template = $this->TemplateName; $backup_fullpath = $this->TempalteFullPath; $this->TemplateName = $t; $this->TempalteFullPath = $pre_parsed['tname']; if (!isset($backup_template) && $this->CachingEnabled && !$this->UserLoggedIn && !EDITING_MODE) { // this is main page template -> check for page-based aggressive caching settings $output =& $this->RunMainPage($pre_parsed); } else { $output =& $this->Application->TemplatesCache->runTemplate($this, $pre_parsed); } $this->TemplateName = $backup_template; $this->TempalteFullPath = $backup_fullpath; return $output; } function &RunMainPage($pre_parsed) { $page =& $this->Application->recallObject('st.-virtual'); /* @var $page kDBItem */ if ($page->isLoaded()) { // page found in database $debug_mode = $this->Application->isDebugMode(); // don't cache debug output $template_path = preg_replace('/^' . preg_quote(FULL_PATH, '/') . '/', '', $this->TempalteFullPath, 1); $element = ($debug_mode ? 'DEBUG_MODE:' : '') . 'file=' . $template_path; $this->FullCachePage = $page->GetDBField('EnablePageCache'); if ($this->FullCachePage && $page->GetDBField('PageCacheKey')) { // page caching enabled -> try to get from cache $cache_key = $this->FormCacheKey($element, $page->GetDBField('PageCacheKey')); $output = $this->getCache($cache_key); if ($output !== false) { return $output; } } // page not cached OR cache expired $output =& $this->Application->TemplatesCache->runTemplate($this, $pre_parsed); $this->generatePageCacheKey($page); if ($this->FullCachePage && $page->GetDBField('PageCacheKey')) { $cache_key = $this->FormCacheKey($element, $page->GetDBField('PageCacheKey')); $this->setCache($cache_key, $output, (int)$page->GetDBField('PageExpiration')); } } else { // page not found in database $output =& $this->Application->TemplatesCache->runTemplate($this, $pre_parsed); } return $output; } /** * Generate page caching key based on prefixes used on it + prefix IDs passed in url * * @param kDBItem $page */ function generatePageCacheKey(&$page) { if (!$page->isLoaded() || $page->GetDBField('OverridePageCacheKey')) { return ; } $page_cache_key = Array (); // nobody resets "m" prefix serial, don't count no user too unset($this->PrefixesInUse['m'], $this->PrefixesInUse['u']); if (array_key_exists('st', $this->PrefixesInUse)) { // prefix "st" serial will never be changed unset($this->PrefixesInUse['st']); $this->PrefixesInUse['c'] = 1; } $prefix_ids = Array (); $prefixes = array_keys($this->PrefixesInUse); asort($prefixes); foreach ($prefixes as $index => $prefix) { $id = $this->Application->GetVar($prefix . '_id'); if (is_numeric($id)) { if (defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode()) { $this->Application->Debugger->appendHTML('Found: "' . $prefix . '_id" = ' . $id . ' during PageCacheKey forming.'); } $prefix_ids[] = $prefix; unset($prefixes[$index]); } } if ($prefix_ids) { $page_cache_key[] = 'prefix_id:' . implode(',', $prefix_ids); } if ($prefixes) { $page_cache_key[] = 'prefix:' . implode(',', $prefixes); } $page_cache_key = implode(';', $page_cache_key); if ($page_cache_key != $page->GetOriginalField('PageCacheKey')) { if (defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode()) { $this->Application->Debugger->appendHTML('Canging PageCacheKey from "<strong>' . $page->GetOriginalField('PageCacheKey') . '</strong>" to "<strong>' . $page_cache_key . '</strong>".'); } $page->SetDBField('PageCacheKey', $page_cache_key); // don't use kDBItem::Update(), because it will change ModifiedById to current front-end user $sql = 'UPDATE ' . $page->TableName . ' SET PageCacheKey = ' . $page->Conn->qstr($page_cache_key) . ' WHERE ' . $page->IDField . ' = ' . $page->GetID(); $page->Conn->Query($sql); // increment serial, because we issue direct sql above! $this->Application->incrementCacheSerial('c'); $this->Application->incrementCacheSerial('c', $page->GetID()); } } /** * Creates tag processor and stores it in local cache + factory * * @param string $prefix * @return kTagProcessor */ function &GetProcessor($prefix) { static $processors = Array (); if ( !isset($processors[$prefix]) ) { $processors[$prefix] = $this->Application->recallObject($prefix . '_TagProcessor'); } return $processors[$prefix]; } /** * Not tag. Method for parameter selection from list in this TagProcessor * * @param Array $params * @param Array $possible_names * * @return string * @access protected */ protected 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 ''; } function SetParams($params) { $this->Params = $params; $keys = array_keys($this->Params); } function GetParam($name) { return isset($this->Params[$name]) ? $this->Params[$name] : false; } function SetParam($name, $value) { $this->Params[$name] = $value; } function PushParams($params) { $this->ParamsStack[$this->ParamsLevel++] = $this->Params; $this->Params = $params; } function PopParams() { $this->Params = $this->ParamsStack[--$this->ParamsLevel]; } function ParseBlock($params, $pass_params=false) { if (array_key_exists('cache_timeout', $params) && $params['cache_timeout']) { $ret = $this->getCache( $this->FormCacheKey('element_' . $params['name']) ); if ($ret) { return $ret; } } if (substr($params['name'], 0, 5) == 'html:') { return substr($params['name'], 5); } if (!array_key_exists($params['name'], $this->Elements) && array_key_exists('default_element', $params)) { // when given element not found, but default element name given, then render it instead $params['name'] = $params['default_element']; unset($params['default_element']); return $this->ParseBlock($params, $pass_params); } $original_params = $params; if ($pass_params || isset($params['pass_params'])) $params = array_merge($this->Params, $params); $this->PushParams($params); $data_exists_bak = $this->DataExists; // if we are parsing design block and we have block_no_data - we need to wrap block_no_data into design, // so we should set DataExists to true manually, otherwise the design block will be skipped because of data_exists in params (by Kostja) // // keep_data_exists is used by block RenderElement (always added in ntags.php), to keep the DataExists value // from inside-content block, otherwise when parsing the design block DataExists will be reset to false resulting missing design block (by Kostja) // // Inside-content block parsing result is given to design block in "content" parameter (ntags.php) and "keep_data_exists" // is only passed, when parsing design block. In case, when $this->DataExists is set to true, but // zero-length content (in 2 cases: method NParser::CheckNoData set it OR really empty block content) // is returned from inside-content block, then design block also should not be shown (by Alex) $this->DataExists = (isset($params['keep_data_exists']) && isset($params['content']) && $params['content'] != '' && $this->DataExists) || (isset($params['design']) && isset($params['block_no_data']) && $params['name'] == $params['design']); if (!array_key_exists($params['name'], $this->Elements)) { $pre_parsed = $this->Application->TemplatesCache->GetPreParsed($params['name']); if ($pre_parsed) { $ret = $this->IncludeTemplate($params); if (array_key_exists('no_editing', $params) && $params['no_editing']) { // when individual render element don't want to be edited return $ret; } return defined('EDITING_MODE') ? $this->DecorateBlock($ret, $params, true) : $ret; } $trace_results = debug_backtrace(); $error_tag = Array ( 'file' => $trace_results[0]['file'], 'line' => $trace_results[0]['line'], ); $error_msg = '<strong>Rendering of undefined element ' . $params['name'] . '</strong>'; throw new ParserException($error_msg, 0, null, $error_tag); return false; } $m_processor =& $this->GetProcessor('m'); $flag_values = $m_processor->PreparePostProcess($params); $f_name = $this->Elements[$params['name']]; + /* @var $f_name Closure */ + $ret = $f_name($this, $params); $ret = $m_processor->PostProcess($ret, $flag_values); $block_params = $this->Params; // input parameters, but modified inside rendered block $this->PopParams(); if (array_key_exists('result_to_var', $flag_values) && $flag_values['result_to_var']) { // when "result_to_var" used inside ParseBlock, then $$result_to_var parameter is set inside ParseBlock, // but not outside it as expected and got lost at all after PopParams is called, so make it work by // setting it's value on current parameter deep level (from where ParseBlock was called) $this->SetParam($flag_values['result_to_var'], $block_params[ $flag_values['result_to_var'] ]); } $this->CheckNoData($ret, $params); $this->DataExists = $data_exists_bak || $this->DataExists; if (array_key_exists('cache_timeout', $original_params) && $original_params['cache_timeout']) { $cache_key = $this->FormCacheKey('element_' . $original_params['name']); $this->setCache($cache_key, $ret, (int)$original_params['cache_timeout']); } if (array_key_exists('no_editing', $block_params) && $block_params['no_editing']) { // when individual render element don't want to be edited return $ret; } return defined('EDITING_MODE') ? $this->DecorateBlock($ret, $params) : $ret; } /** * Checks, that given block is defined * * @param string $name * @return bool */ function blockFound($name) { return array_key_exists($name, $this->Elements); } function DecorateBlock($block_content, $block_params, $is_template = false) { static $used_ids = Array (), $base_url = null; if (!isset($base_url)) { $base_url = $this->Application->BaseURL(); } // $prepend = '[name: ' . $block_params['name'] . '] [params: ' . implode(', ', array_keys($block_params)) . ']'; $decorate = false; $design = false; if (EDITING_MODE == EDITING_MODE_DESIGN) { $decorate = true; if ($is_template) { // content inside pair RenderElement tag } else { if (strpos($block_params['name'], '__capture_') === 0) { // capture tag (usually inside pair RenderElement) $decorate = false; } elseif (array_key_exists('content', $block_params)) { // pair RenderElement (on template, were it's used) $design = true; } } } if (!$decorate) { return $block_content; } /*else { $block_content = $prepend . $block_content; }*/ $block_name = $block_params['name']; $function_name = $is_template ? $block_name : $this->Elements[$block_name]; $block_title = ''; if (array_key_exists($function_name, $this->Application->Parser->ElementLocations)) { $element_location = $this->Application->Parser->ElementLocations[$function_name]; $block_title .= $element_location['template'] . '.tpl'; $block_title .= ' (' . $element_location['start_pos'] . ' - ' . $element_location['end_pos'] . ')'; } // ensure unique id for every div (used from print lists) $container_num = 1; $container_id = 'parser_block[' . $function_name . ']'; while (in_array($container_id . '_' . $container_num, $used_ids)) { $container_num++; } $container_id .= '_' . $container_num; $used_ids[] = $container_id; // prepare parameter string $param_string = $block_name . ':' . $function_name; if ($design) { $btn_text = $this->_btnPhrases['design']; $btn_class = 'cms-edit-design-btn'; $btn_container_class = 'block-edit-design-btn-container'; $btn_name = 'design'; } else { $btn_text = $this->_btnPhrases['block']; $btn_class = 'cms-edit-block-btn'; $btn_container_class = 'block-edit-block-btn-container'; $btn_name = 'content'; } $block_editor = ' <div id="' . $container_id . '" params="' . $param_string . '" class="' . $btn_container_class . '" title="' . htmlspecialchars($block_title) . '"> <div class="' . $btn_class . '"> <div class="cms-btn-image"> <img src="' . $base_url . 'core/admin_templates/img/top_frame/icons/' . $btn_name . '_mode.png" width="15" height="16" alt=""/> </div> <div class="cms-btn-text" id="' . $container_id . '_btn">' . $btn_text . '</div> </div> <div class="cms-btn-content"> %s </div> </div>'; // 1 - text before, 2 - open tag, 3 - open tag attributes, 4 - content inside tag, 5 - closing tag, 6 - text after closing tag if (preg_match('/^(\s*)<(td|span)(.*?)>(.*)<\/(td|span)>(.*)$/is', $block_content, $regs)) { // div inside span -> put div outside span return $regs[1] . '<' . $regs[2] . ' ' . $regs[3] . '>' . str_replace('%s', $regs[4], $block_editor) . '</' . $regs[5] . '>' . $regs[6]; } return str_replace('%s', $block_content, $block_editor); } function IncludeTemplate($params, $silent=null) { $t = is_array($params) ? $this->SelectParam($params, 't,template,block,name') : $params; $cache_timeout = array_key_exists('cache_timeout', $params) ? $params['cache_timeout'] : false; if ($cache_timeout) { $cache_key = $this->FormCacheKey('template:' . $t); $ret = $this->getCache($cache_key); if ($ret !== false) { return $ret; } } $t = preg_replace('/\.tpl$/', '', $t); $data_exists_bak = $this->DataExists; $this->DataExists = false; if (!isset($silent) && array_key_exists('is_silent', $params)) { $silent = $params['is_silent']; } if (isset($params['pass_params'])) { // ability to pass params from block to template $params = array_merge($this->Params, $params); } $this->PushParams($params); $ret = $this->Run($t, $silent); $this->PopParams(); $this->CheckNoData($ret, $params); $this->DataExists = $data_exists_bak || $this->DataExists; if ($cache_timeout) { $this->setCache($cache_key, $ret, (int)$cache_timeout); } return $ret; } function CheckNoData(&$ret, $params) { if (array_key_exists('data_exists', $params) && $params['data_exists'] && !$this->DataExists) { $block_no_data = isset($params['BlockNoData']) ? $params['BlockNoData'] : (isset($params['block_no_data']) ? $params['block_no_data'] : false); if ($block_no_data) { $ret = $this->ParseBlock(array('name'=>$block_no_data)); } else { $ret = ''; } } } function getCache($name) { if (!$this->CachingEnabled) { return false; } $ret = $this->Application->getCache($name, false); if (preg_match('/^\[DE_MARK:(.*?)\]$/', substr($ret, -11), $regs)) { $this->DataExists = $regs[1] ? true : false; $ret = substr($ret, 0, -11); } return $ret; } function setCache($name, $value, $expiration = 0) { if (!$this->CachingEnabled) { return false; } // remeber DataExists in cache, because after cache will be restored // it will not be available naturally (no tags, that set it will be called) $value .= '[DE_MARK:' . (int)$this->DataExists . ']'; return $this->Application->setCache($name, $value, $expiration); } function FormCacheKey($element, $key_string = '') { if (strpos($key_string, 'guest_only') !== false && $this->UserLoggedIn) { // don't cache, when user is logged-in "guest_only" is specified in key return ''; } $parts = Array (); // 1. replace INLINE variable (from request) into key parts if (preg_match_all('/\(%(.*?)\)/', $key_string, $regs)) { // parts in form "(%variable_name)" were found foreach ($regs[1] as $variable_name) { $variable_value = $this->Application->GetVar($variable_name); $key_string = str_replace('(%' . $variable_name . ')', $variable_value, $key_string); } } // 2. replace INLINE serial numbers (they may not be related to any prefix at all) // Serial number also could be composed of inline variables! if (preg_match_all('/\[%(.*?)%\]/', $key_string, $regs)) { // format "[%LangSerial%]" - prefix-wide serial in case of any change in "lang" prefix // format "[%LangIDSerial:5%]" - one id-wide serial in case of data, associated with given id was changed // format "[%CiIDSerial:ItemResourceId:5%]" - foreign key-based serial in case of data, associated with given foreign key was changed foreach ($regs[1] as $serial_name) { $serial_value = $this->Application->getCache('[%' . $serial_name . '%]'); $key_string = str_replace('[%' . $serial_name . '%]', '[%' . $serial_name . '=' . $serial_value . '%]', $key_string); } } /* Always add: =========== * "var:m_lang" - show content on current language * "var:t" - template from url, used to differ multiple pages using same physical template (like as design) * "var:admin,editing_mode" - differ cached content when different editing modes are used * "var:m_cat_id,m_cat_page" - pass current category * "var:page,per_page,sort_by" - list pagination/sorting parameters * "prefix:theme-file" - to be able to reset all cached templated using "Rebuild Theme Files" function * "prefix:phrases" - use latest phrase translations * "prefix:conf" - output could slighly differ based on configuration settings */ $key_string = rtrim('var:m_lang,t,admin,editing_mode,m_cat_id,m_cat_page,page,per_page,sort_by;prefix:theme-file,phrases,conf;' . $key_string, ';'); $keys = explode(';', $key_string); /* Possible parts of a $key_string (all can have multiple occurencies): ==================================================================== * prefix:<prefixA>[,<prefixB>,<prefixC>] - include global serial for given prefix(-es) * skip_prefix:<prefix1>[,<prefix2>,<prefix3>] - exclude global serial for given prefix(-es) * prefix_id:<prefixA>[,<prefixB>,<prefixC>] - include id-based serial for given prefix(-es) * skip_prefix_id:<prefix1>[,<prefix2>,<prefix3>] - exclude id-based serial for given prefix(-es) * var:<aaa>[,<bbb>,<ccc>] - include request variable value(-s) * skip_var:<varA>[,<varB>,<varC>] - exclude request variable value(-s) * (%variable_name) - include request variable value (only value without variable name ifself, like in "var:variable_name") * [%SerialName%] - use to retrieve serial value in free form */ // 3. get variable names, prefixes and prefix ids, that should be skipped $skip_prefixes = $skip_prefix_ids = $skip_variables = Array (); foreach ($keys as $index => $key) { if (preg_match('/^(skip_var|skip_prefix|skip_prefix_id):(.*?)$/i', $key, $regs)) { unset($keys[$index]); $tmp_parts = explode(',', $regs[2]); switch ($regs[1]) { case 'skip_var': $skip_variables = array_merge($skip_variables, $tmp_parts); break; case 'skip_prefix': $skip_prefixes = array_merge($skip_prefixes, $tmp_parts); break; case 'skip_prefix_id': $skip_prefix_ids = array_merge($skip_prefix_ids, $tmp_parts); break; } } } $skip_prefixes = array_unique($skip_prefixes); $skip_variables = array_unique($skip_variables); $skip_prefix_ids = array_unique($skip_prefix_ids); // 4. process keys foreach ($keys as $key) { if (preg_match('/^(var|prefix|prefix_id):(.*?)$/i', $key, $regs)) { $tmp_parts = explode(',', $regs[2]); switch ($regs[1]) { case 'var': // format: "var:country_id" will become "country_id=<country_id>" $tmp_parts = array_diff($tmp_parts, $skip_variables); foreach ($tmp_parts as $variable_name) { $variable_value = $this->Application->GetVar($variable_name); if ($variable_value !== false) { $parts[] = $variable_name . '=' . $variable_value; } } break; case 'prefix': // format: "prefix:country" will become "[%CountrySerial%]" $tmp_parts = array_diff($tmp_parts, $skip_prefixes); foreach ($tmp_parts as $prefix) { $serial_name = $this->Application->incrementCacheSerial($prefix, null, false); $parts[] = '[%' . $serial_name . '=' . $this->Application->getCache($serial_name) . '%]'; if (!$this->RewriteUrls) { // add env-style page and per-page variable, when mod-rewrite is off $prefix_variables = Array ($prefix . '_Page', $prefix . '_PerPage'); foreach ($prefix_variables as $variable_name) { $variable_value = $this->Application->GetVar($variable_name); if ($variable_value !== false) { $parts[] = $variable_name . '=' . $variable_value; } } } } break; case 'prefix_id': // format: "id:country" will become "[%CountryIDSerial:5%]" $tmp_parts = array_diff($tmp_parts, $skip_prefix_ids); foreach ($tmp_parts as $prefix_id) { $id = $this->Application->GetVar($prefix_id . '_id'); if ($id !== false) { $serial_name = $this->Application->incrementCacheSerial($prefix_id, $id, false); $parts[] = '[%' . $serial_name . '=' . $this->Application->getCache($serial_name) . '%]'; } } break; } } elseif ($key == 'currency') { // based on current currency $parts[] = 'curr_iso=' . $this->Application->RecallVar('curr_iso'); } elseif ($key == 'groups') { // based on logged-in user groups $parts[] = 'groups=' . $this->Application->RecallVar('UserGroups'); } elseif ($key == 'guest_only') { // we know this key, but process it at method beginning } else { throw new ParserException('Unknown key part "<strong>' . $key . '</strong>" used in "<strong>key</strong>" parameter of <inp2:m_Cache key="..."/> tag'); } } // 5. add unique given cache key identifier on this page $parts[] = $element; $key = implode(':', $parts); if (defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode()) { $this->Application->Debugger->appendHTML('Parser Key: ' . $key); } return 'parser_' . crc32($key); } function PushPointer($pointer, $key) { $cache_key = $this->FullCachePage || !$this->CachingEnabled ? '' : $this->FormCacheKey('pointer:' . $pointer, $key); $this->CachePointers[++$this->CacheLevel] = $cache_key; return $this->CachePointers[$this->CacheLevel]; } function PopPointer() { return $this->CachePointers[$this->CacheLevel--]; } function CacheStart($pointer, $key) { $pointer = $this->PushPointer($pointer, $key); if ($pointer) { $ret = $this->getCache($pointer); $debug_mode = defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode(); if ($ret !== false) { echo $debug_mode ? '<!-- CACHED OUTPUT START -->' . $ret . '<!-- /CACHED OUTPUT END -->' : $ret; $this->PopPointer(); return true; } if ($debug_mode) { echo '<!-- NO CACHE FOR POINTER: ' . $pointer . ' -->'; } } ob_start(); return false; } function CacheEnd($expiration = 0) { $ret = ob_get_clean(); $pointer = $this->PopPointer(); if ($pointer) { $res = $this->setCache($pointer, $ret, $expiration); if (defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode()) { echo '<!-- STORING CACHE FOR POINTER: ' . $pointer . ' [' . $res . '] -->'; } } echo $ret; } /** * Performs compression of given files or text * * @param mixed $data * @param bool $raw_script * @param string $file_extension * @return string */ function CompressScript($data, $raw_script = false, $file_extension = '') { $minify_helper =& $this->Application->recallObject('MinifyHelper'); /* @var $minify_helper MinifyHelper */ if ($raw_script) { $minify_helper->compressString($data, $file_extension); return $data; } return $minify_helper->CompressScriptTag($data); } } class ParserException extends Exception { public function __construct($message = null, $code = 0, Exception $previous = null, $tag = null) { parent::__construct($message, $code, $previous); if ( isset($tag) ) { $this->file = $tag['file']; $this->line = $tag['line']; } } } \ No newline at end of file Index: branches/5.2.x/core/units/priorites/priority_eh.php =================================================================== --- branches/5.2.x/core/units/priorites/priority_eh.php (revision 14674) +++ branches/5.2.x/core/units/priorites/priority_eh.php (revision 14675) @@ -1,370 +1,370 @@ <?php class PriorityEventHandler extends kDBEventHandler { /** - * Allows to override standart permission mapping + * Allows to override standard permission mapping * */ function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnRecalculatePriorities' => Array ('self' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); } function mapEvents() { parent::mapEvents(); $events_map = Array ( 'OnMassMoveUp' => 'OnChangePriority', 'OnMassMoveDown' => 'OnChangePriority', ); $this->eventMethods = array_merge($this->eventMethods, $events_map); } /** * Enter description here... * * @param kEvent $event */ function OnAfterConfigRead(&$event) { $hooks = Array( Array( 'Mode' => hAFTER, 'Conditional' => false, 'HookToPrefix' => '', 'HookToSpecial' => '*', 'HookToEvent' => Array('OnAfterItemLoad', 'OnPreCreate', 'OnListBuild'), 'DoPrefix' => 'priority', 'DoSpecial' => '*', 'DoEvent' => 'OnPreparePriorities', 'Conditional' => false, ), Array( 'Mode' => hBEFORE, 'Conditional' => false, 'HookToPrefix' => '', 'HookToSpecial' => '*', 'HookToEvent' => Array('OnPreSaveCreated'), 'DoPrefix' => 'priority', 'DoSpecial' => '*', 'DoEvent' => 'OnPreparePriorities', 'Conditional' => false, ), Array( 'Mode' => hAFTER, 'Conditional' => false, 'HookToPrefix' => '', 'HookToSpecial' => '*', 'HookToEvent' => Array('OnPreSave', 'OnPreSaveCreated', 'OnSave', 'OnUpdate'), 'DoPrefix' => 'priority', 'DoSpecial' => '*', 'DoEvent' => 'OnSavePriorityChanges', 'Conditional' => false, ), Array( 'Mode' => hAFTER, 'Conditional' => false, 'HookToPrefix' => '', 'HookToSpecial' => '*', 'HookToEvent' => Array('OnSave'), 'DoPrefix' => 'priority', 'DoSpecial' => '*', 'DoEvent' => 'OnSaveItems', 'Conditional' => false, ), Array( 'Mode' => hBEFORE, 'Conditional' => false, 'HookToPrefix' => '', 'HookToSpecial' => '*', 'HookToEvent' => Array('OnBeforeItemCreate'), 'DoPrefix' => 'priority', 'DoSpecial' => '*', 'DoEvent' => 'OnItemCreate', 'Conditional' => false, ), Array( 'Mode' => hBEFORE, 'Conditional' => false, 'HookToPrefix' => '', 'HookToSpecial' => '*', 'HookToEvent' => Array('OnAfterItemDelete'), 'DoPrefix' => 'priority', 'DoSpecial' => '*', 'DoEvent' => 'OnItemDelete', 'Conditional' => false, ) ); $prefixes = $this->Application->getUnitOption($event->Prefix, 'ProcessPrefixes', Array ()); /* @var $prefixes Array */ foreach ($prefixes as $prefix) { foreach ($hooks as $hook) { if ( !is_array($hook['HookToEvent']) ) { $hook['HookToEvent'] = Array($hook['HookToEvent']); } foreach ($hook['HookToEvent'] as $hook_event) { $this->Application->registerHook( $prefix . '.' . $hook['HookToSpecial'] . ':' . $hook_event, $event->Prefix . '.' . $hook['DoSpecial'] . ':' . $hook['DoEvent'], $hook['Mode'], $hook['Conditional'] ); } } } } /** * Should be hooked to OnAfterItemLoad, OnPreSaveCreated (why latter?) * * @param kEvent $event */ function OnPreparePriorities(&$event) { if ( !$this->Application->isAdminUser ) { return ; } $priority_helper =& $this->Application->recallObject('PriorityHelper'); /* @var $priority_helper kPriorityHelper */ list ($constrain, $joins) = $this->getConstrainInfo($event); $is_new = $event->MasterEvent->Name == 'OnPreCreate' || $event->MasterEvent->Name == 'OnPreSaveCreated'; $priority_helper->preparePriorities($event->MasterEvent, $is_new, $constrain, $joins); } /** * Enter description here... * * @param kEvent $event */ function OnSavePriorityChanges(&$event) { if ($event->MasterEvent->status != kEvent::erSUCCESS) { // don't update priorities, when OnSave validation failed return ; } $object =& $event->MasterEvent->getObject(); $tmp = $this->Application->RecallVar('priority_changes'.$this->Application->GetVar('m_wid')); $changes = $tmp ? unserialize($tmp) : array(); if (!isset($changes[$object->GetID()])) { $changes[$object->GetId()]['old'] = $object->GetID() == 0 ? 'new' : $object->GetDBField('OldPriority'); } if ($changes[$object->GetId()]['old'] == $object->GetDBField('Priority')) return ; $changes[$object->GetId()]['new'] = $object->GetDBField('Priority'); list ($constrain, $joins) = $this->getConstrainInfo($event); if ($constrain) { $changes[$object->GetId()]['constrain'] = $constrain; } $this->Application->StoreVar('priority_changes'.$this->Application->GetVar('m_wid'), serialize($changes)); } /** * Enter description here... * * @param kEvent $event */ function OnItemDelete(&$event) { // just store the prefix in which the items were deleted $del = $this->Application->RecallVar('priority_deleted' . $this->Application->GetVar('m_wid')); $del = $del ? unserialize($del) : array(); list ($constrain, $joins) = $this->getConstrainInfo($event); $cache_key = crc32($event->MasterEvent->Prefix . ':' . $constrain . ':' . $joins); if ( !isset($del[$cache_key]) ) { $del[$cache_key] = Array ( 'prefix' => $event->MasterEvent->Prefix, 'constrain' => $constrain, 'joins' => $joins, ); $this->Application->StoreVar('priority_deleted' . $this->Application->GetVar('m_wid'), serialize($del)); } } /** * Called before script shut-down and recalculate all deleted prefixes, to avoid recalculation on each deleted item * * @param kEvent $event */ function OnBeforeShutDown(&$event) { $del = $this->Application->RecallVar('priority_deleted'.$this->Application->GetVar('m_wid')); $del = $del ? unserialize($del) : array(); $priority_helper =& $this->Application->recallObject('PriorityHelper'); /* @var $priority_helper kPriorityHelper */ foreach ($del as $del_info) { $dummy_event = new kEvent( array('prefix'=>$del_info['prefix'], 'name'=>'Dummy' ) ); $ids = $priority_helper->recalculatePriorities($dummy_event, $del_info['constrain'], $del_info['joins']); if ($ids) { $priority_helper->massUpdateChanged($del_info['prefix'], $ids); } } $this->Application->RemoveVar('priority_deleted'.$this->Application->GetVar('m_wid')); } /** * Enter description here... * * @param kEvent $event */ function OnSaveItems(&$event) { $tmp = $this->Application->RecallVar('priority_changes'.$this->Application->GetVar('m_wid')); $changes = $tmp ? unserialize($tmp) : array(); $priority_helper =& $this->Application->recallObject('PriorityHelper'); /* @var $priority_helper kPriorityHelper */ list ($constrain, $joins) = $this->getConstrainInfo($event); $ids = $priority_helper->updatePriorities($event->MasterEvent, $changes, Array (0 => $event->MasterEvent->getEventParam('ids')), $constrain, $joins); if ($ids) { $priority_helper->massUpdateChanged($event->MasterEvent->Prefix, $ids); } } function OnItemCreate(&$event) { $obj =& $event->MasterEvent->getObject(); if ($obj->GetDBField('Priority') == 0) { $priority_helper =& $this->Application->recallObject('PriorityHelper'); /* @var $priority_helper kPriorityHelper */ list ($constrain, $joins) = $this->getConstrainInfo($event); $priority_helper->preparePriorities($event->MasterEvent, true, $constrain, $joins); } } /** * Processes OnMassMoveUp, OnMassMoveDown events * * @param kEvent $event */ function OnChangePriority(&$event) { $prefix = $this->Application->GetVar('priority_prefix'); $dummy_event = new kEvent( array('prefix'=>$prefix, 'name'=>'Dummy' ) ); $ids = $this->StoreSelectedIDs($dummy_event); if ($ids) { $id_field = $this->Application->getUnitOption($prefix, 'IDField'); $table_name = $this->Application->getUnitOption($prefix, 'TableName'); if ( $this->Application->IsTempMode($prefix) ) { $table_name = $this->Application->GetTempName($table_name, 'prefix:' . $prefix); } $sql = 'SELECT Priority, '.$id_field.' FROM '.$table_name.' WHERE '.$id_field.' IN ('.implode(',', $ids).') ORDER BY Priority DESC'; $priorities = $this->Conn->GetCol($sql, $id_field); $priority_helper =& $this->Application->recallObject('PriorityHelper'); /* @var $priority_helper kPriorityHelper */ list ($constrain, $joins) = $this->getConstrainInfo($event); $sql = 'SELECT IFNULL(MIN(item_table.Priority), -1) FROM '.$table_name . ' item_table ' . $joins; if ( $constrain ) { $sql .= ' WHERE ' . $priority_helper->normalizeConstrain($constrain); } $min_priority = $this->Conn->GetOne($sql); foreach ($ids as $id) { $new_priority = $priorities[$id] + ($event->Name == 'OnMassMoveUp' ? +1 : -1); if ($new_priority > -1 || $new_priority < $min_priority) { continue; } $changes = Array ( $id => Array ('old' => $priorities[$id], 'new' => $new_priority), ); if ($constrain) { $changes[$id]['constrain'] = $constrain; } $sql = 'UPDATE '.$table_name.' SET Priority = '.$new_priority.' WHERE '.$id_field.' = '.$id; $this->Conn->Query($sql); $ids = $priority_helper->updatePriorities($dummy_event, $changes, Array ($id => $id), $constrain, $joins); if ($ids) { $priority_helper->massUpdateChanged($prefix, $ids); } } } $this->clearSelectedIDs($dummy_event); } /** * Completely recalculates priorities in current category * * @param kEvent $event */ function OnRecalculatePriorities(&$event) { $priority_helper =& $this->Application->recallObject('PriorityHelper'); /* @var $priority_helper kPriorityHelper */ $prefix = $this->Application->GetVar('priority_prefix'); $dummy_event = new kEvent( array('prefix'=>$prefix, 'name'=>'Dummy' ) ); list ($constrain, $joins) = $this->getConstrainInfo($event); $ids = $priority_helper->recalculatePriorities($dummy_event, $constrain, $joins); if ($ids) { $priority_helper->massUpdateChanged($prefix, $ids); } } /** * Returns constrain for current priority calculations * * @param kEvent $event * @return Array */ function getConstrainInfo(&$event) { $constrain_event = new kEvent($event->MasterEvent->getPrefixSpecial() . ':OnGetConstrainInfo'); $constrain_event->setEventParam('actual_event', $event->Name); $constrain_event->setEventParam('original_event', $event->MasterEvent->Name); $this->Application->HandleEvent($constrain_event); return $constrain_event->getEventParam('constrain_info'); } } Index: branches/5.2.x/core/units/visits/visits_event_handler.php =================================================================== --- branches/5.2.x/core/units/visits/visits_event_handler.php (revision 14674) +++ branches/5.2.x/core/units/visits/visits_event_handler.php (revision 14675) @@ -1,138 +1,138 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class VisitsEventHandler extends kDBEventHandler { /** - * Allows to override standart permission mapping + * Allows to override standard permission mapping * */ function mapPermissions() { parent::mapPermissions(); $permissions = Array( 'OnItemBuild' => Array('self' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Registers user visit to site * * @param kEvent $event * * @return void * @access protected */ protected function OnRegisterVisit(&$event) { if ( $this->Application->isAdmin || !$this->Application->ConfigValue('UseVisitorTracking') || $this->Application->RecallVar('visit_id') ) { // admin logins are not registered in visits list return ; } $object =& $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ $object->SetDBField('VisitDate_date', adodb_mktime()); $object->SetDBField('VisitDate_time', adodb_mktime()); $object->SetDBField('Referer', getArrayValue($_SERVER, 'HTTP_REFERER')); $object->SetDBField('IPAddress', $_SERVER['REMOTE_ADDR']); if ( $object->Create() ) { $this->Application->StoreVar('visit_id', $object->GetID()); $this->Application->SetVar('visits_id', $object->GetID()); } } /** * Apply any custom changes to list's sql query * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(&$event) { $object =& $event->getObject(); /* @var $object kDBList */ $types = $event->getEventParam('types'); if ( $types == 'myvisitors' ) { $user_id = $this->Application->RecallVar('user_id'); $object->addFilter('myitems_user1', 'au.PortalUserId = ' . $user_id); $object->addFilter('myitems_user2', 'au.PortalUserId >0'); //$object->AddGroupByField('VisitDate'); $object->AddGroupByField('%1$s.VisitId'); } if ( $types == 'myvisitororders' && $event->Special == 'incommerce' ) { $user_id = $this->Application->RecallVar('user_id'); $object->addFilter('myitems_orders', 'ord.OrderId IS NOT NULL'); $object->addFilter('myitems_user1', 'au.PortalUserId = ' . $user_id); $object->addFilter('myitems_user2', 'au.PortalUserId >0'); $object->addFilter('myitems_orders_processed', 'ord.Status = 4'); } } /** * Apply some special processing to object being * recalled before using it in other events that * call prepareObject * * @param kDBItem|kDBList $object * @param kEvent $event * @return void * @access protected */ protected function prepareObject(&$object, &$event) { $types = $event->getEventParam('types'); if(method_exists($object, 'AddGroupByField')) { if( ($types == 'myvisitors' || !$types) && $object->Special == 'incommerce') { $object->addCalculatedField('OrderTotalAmountSum', 'SUM(IF(ord.Status = 4, ord.SubTotal+ord.ShippingCost+ord.VAT, 0))'); $object->addCalculatedField('OrderAffiliateCommissionSum', 'SUM( IF(ord.Status = 4,ord.AffiliateCommission,0))'); $object->addCalculatedField('OrderCountByVisit', 'SUM( IF(ord.Status = 4, 1, 0) )'); } if (!$types){ $object->AddGroupByField('%1$s.VisitId'); } } } /** * [HOOK] Updates user_id in current visit * * @param kEvent $event */ function OnUserLogin(&$event) { if ($event->MasterEvent->status == kEvent::erSUCCESS) { $user_id = $this->Application->RecallVar('user_id'); if ($user_id > 0) { // for real users only, not root,guest $this->Application->setVisitField('PortalUserId', $user_id); } } } } \ No newline at end of file Index: branches/5.2.x/core/units/theme_files/theme_file_eh.php =================================================================== --- branches/5.2.x/core/units/theme_files/theme_file_eh.php (revision 14674) +++ branches/5.2.x/core/units/theme_files/theme_file_eh.php (revision 14675) @@ -1,228 +1,228 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class ThemeFileEventHandler extends kDBEventHandler { /** - * Allows to override standart permission mapping + * Allows to override standard permission mapping * */ function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnLoadBlock' => Array ('subitem' => true), 'OnSaveBlock' => Array ('subitem' => true), 'OnSaveLayout' => Array ('subitem' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Checks user permission to execute given $event * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(&$event) { if ($event->Name == 'OnLoadBlock' || $event->Name == 'OnSaveBlock') { return $this->Application->isAdminUser; } return parent::CheckPermission($event); } /** * Loads template contents into virtual field * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemLoad(&$event) { parent::OnAfterItemLoad($event); $object =& $event->getObject(); /* @var $object kDBItem */ $filename = $this->_getTemplatePath($object); if ( file_exists($filename) ) { $object->SetDBField('FileContents', file_get_contents($filename)); } else { $object->SetError('FileContents', 'template_file_missing', 'la_error_TemplateFileMissing'); } } /** * Trim contents of edited template * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(&$event) { parent::OnBeforeItemUpdate($event); $object =& $event->getObject(); /* @var $object kDBItem */ $file_data = $object->GetDBField('FileContents'); $file_data = str_replace("\r\n", "\n", $file_data); $file_data = str_replace("\r", "\n", $file_data); $object->SetDBField('FileContents', trim($file_data)); } /** * Saves updated content to template * * @param kEvent $event */ function OnAfterItemUpdate(&$event) { parent::OnAfterItemUpdate($event); $object =& $event->getObject(); /* @var $object kDBItem */ $filename = $this->_getTemplatePath($object); if (file_exists($filename) && is_writable($filename)) { $fp = fopen($filename, 'w'); fwrite($fp, $object->GetDBField('FileContents')); fclose($fp); $themes_helper =& $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ $meta_info = $themes_helper->parseTemplateMetaInfo($filename); $file_description = array_key_exists('desc', $meta_info) ? $meta_info['desc'] : ''; $object->SetDBField('Description', $file_description); $object->SetDBField('FileMetaInfo', serialize($meta_info)); $object->Update(); } } /** * Returns full path to template file * * @param kDBItem $object * @return string */ function _getTemplatePath(&$object) { $theme =& $this->Application->recallObject('theme'); /* @var $theme kDBItem */ $path = FULL_PATH . '/themes/' . $theme->GetDBField('Name'); $path .= $object->GetDBField('FilePath') . '/' . $object->GetDBField('FileName'); return $path; } /** * Loads block data based on it's name in request * * @param kEvent $event */ function OnLoadBlock(&$event) { parent::OnNew($event); $object =& $event->getObject(); /* @var $object kDBItem */ $template_helper =& $this->Application->recallObject('TemplateHelper'); /* @var $template_helper TemplateHelper */ $template_helper->InitHelper($object); $object->SetDBField('FileName', $template_helper->blockInfo('template_file')); $object->SetDBField('BlockPosition', $template_helper->blockInfo('start_pos') . ' - ' . $template_helper->blockInfo('end_pos')); $object->SetDBField('FileContents', $template_helper->blockInfo('content')); } /** * Saves changed template block * * @param kEvent $event */ function OnSaveBlock(&$event) { $object =& $event->getObject( Array('skip_autoload' => true) ); /* @var $object kDBItem */ $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); if ($items_info) { list ($id, $field_values) = each($items_info); $object->SetFieldsFromHash($field_values); $object->setID($id); } $status = $object->Validate(); $template_helper =& $this->Application->recallObject('TemplateHelper'); /* @var $template_helper TemplateHelper */ $template_helper->InitHelper($object); $status = $status && $template_helper->saveBlock($object); if ($status) { $event->SetRedirectParam('opener', 'u'); } else { $event->status = kEvent::erFAIL; } } /** * Saves layout on given template * * @param kEvent $event */ function OnSaveLayout(&$event) { $event->status = kEvent::erSTOP; if (($this->Application->GetVar('ajax') != 'yes') || (EDITING_MODE != EDITING_MODE_DESIGN)) { return ; } $target_order = $this->Application->GetVar('target_order'); $template_helper =& $this->Application->recallObject('TemplateHelper'); /* @var $template_helper TemplateHelper */ if ($template_helper->moveTemplateElements($target_order)) { echo 'OK'; return ; } echo 'FAILED'; } } \ No newline at end of file Index: branches/5.2.x/core/units/categories/categories_tag_processor.php =================================================================== --- branches/5.2.x/core/units/categories/categories_tag_processor.php (revision 14674) +++ branches/5.2.x/core/units/categories/categories_tag_processor.php (revision 14675) @@ -1,2009 +1,2020 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ class CategoriesTagProcessor extends kDBTagProcessor { function SubCatCount($params) { $object =& $this->getObject($params); + /* @var $object kDBItem */ - if (isset($params['today']) && $params['today']) { + if ( isset($params['today']) && $params['today'] ) { $sql = 'SELECT COUNT(*) - FROM '.$object->TableName.' - WHERE (ParentPath LIKE "'.$object->GetDBField('ParentPath').'%") AND (CreatedOn > '.(adodb_mktime() - 86400).')'; + FROM ' . $object->TableName . ' + WHERE (ParentPath LIKE "' . $object->GetDBField('ParentPath') . '%") AND (CreatedOn > ' . (adodb_mktime() - 86400) . ')'; return $this->Conn->GetOne($sql) - 1; } return $object->GetDBField('CachedDescendantCatsQty'); } /** * Returns category count in system * * @param Array $params * @return int */ function CategoryCount($params) { $count_helper =& $this->Application->recallObject('CountHelper'); /* @var $count_helper kCountHelper */ $today_only = isset($params['today']) && $params['today']; return $count_helper->CategoryCount($today_only); } function IsNew($params) { $object =& $this->getObject($params); + /* @var $object kDBItem */ + return $object->GetDBField('IsNew') ? 1 : 0; } function IsPick($params) { return $this->IsEditorsPick($params); } /** * Returns item's editors pick status (using not formatted value) * * @param Array $params * @return bool */ function IsEditorsPick($params) { $object =& $this->getObject($params); + /* @var $object kDBItem */ return $object->GetDBField('EditorsPick') == 1; } function ItemIcon($params) { $grids = $this->Application->getUnitOption($this->Prefix, 'Grids'); $grid = $grids[ $params['grid'] ]; if (!array_key_exists('Icons', $grid)) { return ''; } $icons = $grid['Icons']; $icon_prefix = array_key_exists('icon_prefix', $params)? $params['icon_prefix'] : 'icon16_'; if (array_key_exists('name', $params)) { $icon_name = $params['name']; return array_key_exists($icon_name, $icons) ? $icons[$icon_name] : ''; } $object =& $this->getObject($params); /* @var $object kDBList */ if ($object->GetDBField('ThemeId') > 0) { if (!$object->GetDBField('IsMenu')) { return $icon_prefix . 'section_menuhidden_system.png'; } return $icon_prefix . 'section_system.png'; } $status = $object->GetDBField('Status'); if ($status == STATUS_DISABLED) { return $icon_prefix . 'section_disabled.png'; } if (!$object->GetDBField('IsMenu')) { return $icon_prefix . 'section_menuhidden.png'; } if ($status == STATUS_PENDING) { return $icon_prefix . 'section_pending.png'; } if ($object->GetDBField('IsNew') && ($icon_prefix == 'icon16_')) { return $icon_prefix . 'section_new.png'; // show gris icon only in grids } return $icon_prefix . 'section.png'; } function ItemCount($params) { $object =& $this->getObject($params); /* @var $object kDBItem */ $ci_table = $this->Application->getUnitOption('ci', 'TableName'); $sql = 'SELECT COUNT(*) FROM ' . $object->TableName . ' c LEFT JOIN ' . $ci_table . ' ci ON c.CategoryId = ci.CategoryId WHERE (c.TreeLeft BETWEEN ' . $object->GetDBField('TreeLeft') . ' AND ' . $object->GetDBField('TreeRight') . ') AND NOT (ci.CategoryId IS NULL)'; return $this->Conn->GetOne($sql); } function ListCategories($params) { return $this->PrintList2($params); } function RootCategoryName($params) { return $this->Application->ProcessParsedTag('m', 'RootCategoryName', $params); } function CheckModuleRoot($params) { $module_name = getArrayValue($params, 'module') ? $params['module'] : 'In-Commerce'; $module_root_cat = $this->Application->findModule('Name', $module_name, 'RootCat'); $additional_cats = $this->SelectParam($params, 'add_cats'); if ($additional_cats) { $additional_cats = explode(',', $additional_cats); } else { $additional_cats = array(); } if ($this->Application->GetVar('m_cat_id') == $module_root_cat || in_array($this->Application->GetVar('m_cat_id'), $additional_cats)) { $home_template = getArrayValue($params, 'home_template'); if (!$home_template) return; $this->Application->Redirect($home_template, Array('pass'=>'all')); }; } function CategoryPath($params) { $category_helper =& $this->Application->recallObject('CategoryHelper'); /* @var $category_helper CategoryHelper */ return $category_helper->NavigationBar($params); } /** * Shows category path to specified category * * @param Array $params * @return string */ function FieldCategoryPath($params) { $object =& $this->getObject(); /* @var $object kDBItem */ $field = $this->SelectParam($params, 'name,field'); $category_id = $object->GetDBField($field); if ($category_id) { $params['cat_id'] = $category_id; return $this->CategoryPath($params); } return ''; } function CurrentCategoryName($params) { $cat_object =& $this->Application->recallObject($this->getPrefixSpecial(), $this->Prefix.'_List'); /* @var $cat_object kDBList */ $sql = 'SELECT '.$this->getTitleField().' FROM '.$cat_object->TableName.' WHERE CategoryId = '.(int)$this->Application->GetVar('m_cat_id'); return $this->Conn->GetOne($sql); } /** * Returns current category name * * @param Array $params * @return string * @todo Find where it's used */ function CurrentCategory($params) { return $this->CurrentCategoryName($params); } function getTitleField() { $ml_formatter =& $this->Application->recallObject('kMultiLanguage'); /* @var $ml_formatter kMultiLanguage */ return $ml_formatter->LangFieldName('Name'); } /** * Returns symlinked category for given category * * @param int $category_id * @return int */ function getCategorySymLink($category_id) { if (!$category_id) { // don't bother to get symlink for "Home" category return $category_id; } $cache_key = 'category_symlinks[%CSerial%]'; $cache = $this->Application->getCache($cache_key); if ($cache === false) { $id_field = $this->Application->getUnitOption($this->Prefix, 'IDField'); $table_name = $this->Application->getUnitOption($this->Prefix, 'TableName'); // get symlinked categories, that are not yet deleted $this->Conn->nextQueryCachable = true; $sql = 'SELECT c1.SymLinkCategoryId, c1.' . $id_field . ' FROM ' . $table_name . ' c1 JOIN ' . $table_name . ' c2 ON c1.SymLinkCategoryId = c2.' . $id_field; $cache = $this->Conn->GetCol($sql, $id_field); $this->Application->setCache($cache_key, $cache); } return array_key_exists($category_id, $cache) ? $cache[$category_id] : $category_id; } function CategoryLink($params) { $category_id = getArrayValue($params, 'cat_id'); if ( $category_id === false ) { $category_id = $this->Application->GetVar($this->getPrefixSpecial() . '_id'); } if ( "$category_id" == 'Root' ) { $category_id = $this->Application->findModule('Name', $params['module'], 'RootCat'); } elseif ( "$category_id" == 'current' ) { $category_id = $this->Application->GetVar('m_cat_id'); } if ( !array_key_exists('direct_link', $params) || !$params['direct_link'] ) { $category_id = $this->getCategorySymLink((int)$category_id); } else { unset($params['direct_link']); } $virtual_template = $this->Application->getVirtualPageTemplate($category_id); if ( ($virtual_template !== false) && preg_match('/external:(.*)/', $virtual_template, $rets) ) { // external url (return here, instead of always replacing $params['t'] for kApplication::HREF to find it) return $rets[1]; } unset($params['cat_id'], $params['module']); $new_params = Array ('pass' => 'm', 'm_cat_id' => $category_id, 'pass_category' => 1); $params = array_merge($params, $new_params); return $this->Application->ProcessParsedTag('m', 't', $params); } function CategoryList($params) { //$object =& $this->Application->recallObject( $this->getPrefixSpecial() , $this->Prefix.'_List', $params ); $object =& $this->GetList($params); if ($object->GetRecordsCount() == 0) { if (isset($params['block_no_cats'])) { $params['name'] = $params['block_no_cats']; return $this->Application->ParseBlock($params); } else { return ''; } } if (isset($params['block'])) { return $this->PrintList($params); } else { $params['block'] = $params['block_main']; if (isset($params['block_row_start'])) { $params['row_start_block'] = $params['block_row_start']; } if (isset($params['block_row_end'])) { $params['row_end_block'] = $params['block_row_end']; } return $this->PrintList2($params); } } function Meta($params) { $object =& $this->Application->recallObject($this->Prefix); // .'.-item' /* @var $object CategoriesItem */ $meta_type = $params['name']; if ($object->isLoaded()) { // 1. get module prefix by current category $category_helper =& $this->Application->recallObject('CategoryHelper'); /* @var $category_helper CategoryHelper */ $category_path = explode('|', substr($object->GetDBField('ParentPath'), 1, -1)); $module_info = $category_helper->getCategoryModule($params, $category_path); // In-Edit & Proj-CMS module prefixes doesn't have custom field with item template if ($module_info && $module_info['Var'] != 'adm' && $module_info['Var'] != 'st') { // 2. get item template by current category & module prefix $mod_rewrite_helper = $this->Application->recallObject('ModRewriteHelper'); /* @var $mod_rewrite_helper kModRewriteHelper */ $category_params = Array ( 'CategoryId' => $object->GetID(), 'ParentPath' => $object->GetDBField('ParentPath'), ); $item_template = $mod_rewrite_helper->GetItemTemplate($category_params, $module_info['Var']); if ($this->Application->GetVar('t') == $item_template) { // we are located on item's details page $item =& $this->Application->recallObject($module_info['Var']); /* @var $item kCatDBItem */ // 3. get item's meta data $value = $item->GetField('Meta'.$meta_type); if ($value) { return $value; } } // 4. get category meta data $value = $object->GetField('Meta'.$meta_type); if ($value) { return $value; } } } // 5. get default meta data switch ($meta_type) { case 'Description': $config_name = 'Category_MetaDesc'; break; case 'Keywords': $config_name = 'Category_MetaKey'; break; } return $this->Application->ConfigValue($config_name); } function BuildListSpecial($params) { if (($this->Special != '') && !is_numeric($this->Special)) { // When recursive category list is printed (like in sitemap), then special // should be generated even if it's already present. Without it list on this // level will erase list on previous level, because it will be stored in same object. return $this->Special; } if ( isset($params['parent_cat_id']) ) { $parent_cat_id = $params['parent_cat_id']; } else { $parent_cat_id = $this->Application->GetVar($this->Prefix.'_id'); if (!$parent_cat_id) { $parent_cat_id = $this->Application->GetVar('m_cat_id'); } if (!$parent_cat_id) { $parent_cat_id = 0; } } $list_unique_key = $this->getUniqueListKey($params); // check for "admin" variable, because we are parsing front-end template from admin when using template editor feature if ($this->Application->GetVar('admin') || !$this->Application->isAdmin) { // add parent category to special, when on Front-End, // because there can be many category lists on same page $list_unique_key .= $parent_cat_id; } if ($list_unique_key == '') { return parent::BuildListSpecial($params); } return crc32($list_unique_key); } function IsCurrent($params) { $object =& $this->getObject($params); if ($object->GetID() == $this->Application->GetVar('m_cat_id')) { return true; } else { return false; } } /** * Substitutes category in last template base on current category * This is required becasue when you navigate catalog using AJAX, last_template is not updated * but when you open item edit from catalog last_template is used to build opener_stack * So, if we don't substitute m_cat_id in last_template, after saving item we'll get redirected * to the first category we've opened, not the one we navigated to using AJAX * * @param Array $params */ function UpdateLastTemplate($params) { $category_id = $this->Application->GetVar('m_cat_id'); $wid = $this->Application->GetVar('m_wid'); list($index_file, $env) = explode('|', $this->Application->RecallVar(rtrim('last_template_'.$wid, '_')), 2); $vars_backup = Array (); $vars = $this->Application->HttpQuery->processQueryString( str_replace('%5C', '\\', $env) ); foreach ($vars as $var_name => $var_value) { $vars_backup[$var_name] = $this->Application->GetVar($var_name); $this->Application->SetVar($var_name, $var_value); } // update required fields $this->Application->SetVar('m_cat_id', $category_id); $this->Application->Session->SaveLastTemplate($params['template']); foreach ($vars_backup as $var_name => $var_value) { $this->Application->SetVar($var_name, $var_value); } } function GetParentCategory($params) { $parent_id = $this->Application->getBaseCategory(); $category_id = $this->Application->GetVar('m_cat_id'); if ($category_id != $parent_id) { $sql = 'SELECT ParentId FROM ' . $this->Application->getUnitOption($this->Prefix, 'TableName') . ' WHERE ' . $this->Application->getUnitOption($this->Prefix, 'IDField') . ' = ' . $category_id; $parent_id = $this->Conn->GetOne($sql); } return $parent_id; } function InitCacheUpdater($params) { kUtil::safeDefine('CACHE_PERM_CHUNK_SIZE', 30); $continue = $this->Application->GetVar('continue'); $total_cats = (int) $this->Conn->GetOne('SELECT COUNT(*) FROM '.TABLE_PREFIX.'Category'); if ($continue === false && $total_cats > CACHE_PERM_CHUNK_SIZE) { // first step, if category count > CACHE_PERM_CHUNK_SIZE, then ask for cache update return true; } if ($continue === false) { // if we don't have to ask, then assume user selected "Yes" in permcache update dialog $continue = 1; } $updater =& $this->Application->makeClass('kPermCacheUpdater', Array($continue)); /* @var $updater kPermCacheUpdater */ if ($continue === '0') { // No in dialog $updater->clearData(); $this->Application->Redirect($params['destination_template']); } $ret = false; // don't ask for update if ($continue == 1) { // Initial run $updater->setData(); } if ($continue == 2) { // Continuing // called from AJAX request => returns percent $needs_more = true; while ($needs_more && $updater->iteration <= CACHE_PERM_CHUNK_SIZE) { // until proceeeded in this step category count exceeds category per step limit $needs_more = $updater->DoTheJob(); } if ($needs_more) { // still some categories are left for next step $updater->setData(); } else { // all done, update left tree and redirect $updater->SaveData(); $this->Application->HandleEvent($event, 'c:OnResetCMSMenuCache'); $this->Application->RemoveVar('PermCache_UpdateRequired'); $this->Application->StoreVar('RefreshStructureTree', 1); $this->Application->Redirect($params['destination_template']); } $ret = $updater->getDonePercent(); } return $ret; } /** * Parses warning block, but with style="display: none;". Used during permissions saving from AJAX * * @param Array $params * @return string * @access protected */ protected function SaveWarning($params) { if ( $this->Prefix == 'st' ) { // don't use this method for other prefixes then Category, that use this tag processor return parent::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 = substr($this->Application->GetVar($top_prefix . '_mode'), 0, 1) == 't'; $modified = $this->Application->RecallVar($top_prefix . '_modified'); if ( !$temp_tables ) { $this->Application->RemoveVar($top_prefix . '_modified'); return ''; } $block_name = $this->SelectParam($params, 'render_as,name'); if ( $block_name ) { $block_params = $this->prepareTagParams($params); $block_params['name'] = $block_name; $block_params['edit_mode'] = $temp_tables ? 1 : 0; $block_params['display'] = $temp_tables && $modified ? 1 : 0; return $this->Application->ParseBlock($block_params); } return $temp_tables && $modified ? 1 : 0; } /** * Allows to detect if this prefix has something in clipboard * * @param Array $params * @return bool */ function HasClipboard($params) { $clipboard = $this->Application->RecallVar('clipboard'); if ($clipboard) { $clipboard = unserialize($clipboard); foreach ($clipboard as $prefix => $clipboard_data) { foreach ($clipboard_data as $mode => $ids) { if (count($ids)) return 1; } } } return 0; } /** * Allows to detect if root category being edited * * @param Array $params */ function IsRootCategory($params) { $object =& $this->getObject($params); /* @var $object CategoriesItem */ return $object->IsRoot(); } /** * Returns home category id * * @param Array $params * @return int */ function HomeCategory($params) { return $this->Application->getBaseCategory(); } /** * Used for disabling "Home" and "Up" buttons in category list * * @param Array $params * @return bool */ function ModuleRootCategory($params) { return $this->Application->GetVar('m_cat_id') == $this->Application->getBaseCategory(); } function CatalogItemCount($params) { $params['skip_quering'] = true; $object =& $this->GetList($params); return $object->GetRecordsCount(false) != $object->GetRecordsCount() ? $object->GetRecordsCount().' / '.$object->GetRecordsCount(false) : $object->GetRecordsCount(); } function InitCatalog($params) { $tab_prefixes = $this->Application->GetVar('tp'); // {all, <prefixes_list>, none} if ($tab_prefixes === false) $tab_prefixes = 'all'; $skip_prefixes = isset($params['skip_prefixes']) && $params['skip_prefixes'] ? explode(',', $params['skip_prefixes']) : Array(); $replace_main = isset($params['replace_m']) && $params['replace_m']; // get all prefixes available $prefixes = Array(); foreach ($this->Application->ModuleInfo as $module_name => $module_data) { $prefix = $module_data['Var']; if ($prefix == 'adm'/* || $prefix == 'm'*/) continue; if ($prefix == 'm' && $replace_main) { $prefix = 'c'; } $prefixes[] = $prefix; } if ($tab_prefixes == 'none') { $skip_prefixes = array_unique(array_merge($skip_prefixes, $prefixes)); unset($skip_prefixes[ array_search($replace_main ? 'c' : 'm', $skip_prefixes) ]); } elseif ($tab_prefixes != 'all') { // prefix list here $tab_prefixes = explode(',', $tab_prefixes); // list of prefixes that should stay $skip_prefixes = array_unique(array_merge($skip_prefixes, array_diff($prefixes, $tab_prefixes))); } $params['name'] = $params['render_as']; $params['skip_prefixes'] = implode(',', $skip_prefixes); return $this->Application->ParseBlock($params); } /** * Determines, that printed category/menu item is currently active (will also match parent category) * * @param Array $params * @return bool */ function IsActive($params) { static $current_path = null; if ( !isset($current_path) ) { $sql = 'SELECT ParentPath FROM ' . TABLE_PREFIX . 'Category WHERE CategoryId = ' . (int)$this->Application->GetVar('m_cat_id'); $current_path = $this->Conn->GetOne($sql); } if ( array_key_exists('parent_path', $params) ) { $test_path = $params['parent_path']; } else { $template = $params['template']; if ( $template ) { // when using from "c:CachedMenu" tag $sql = 'SELECT ParentPath FROM ' . TABLE_PREFIX . 'Category WHERE NamedParentPath = ' . $this->Conn->qstr('Content/' . $template); $test_path = $this->Conn->GetOne($sql); } else { // when using from "c:PrintList" tag $cat_id = array_key_exists('cat_id', $params) && $params['cat_id'] ? $params['cat_id'] : false; if ( $cat_id === false ) { // category not supplied -> get current from PrintList $category =& $this->getObject($params); } else { if ( "$cat_id" == 'Root' ) { $cat_id = $this->Application->findModule('Name', $params['module'], 'RootCat'); } $category =& $this->Application->recallObject($this->Prefix . '.-c' . $cat_id, $this->Prefix, Array ('skip_autoload' => true)); /* @var $category CategoriesItem */ $category->Load($cat_id); } $test_path = $category->GetDBField('ParentPath'); } } return strpos($current_path, $test_path) !== false; } /** * Checks if user have one of required permissions * * @param Array $params * @return bool */ function HasPermission($params) { $perm_helper =& $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $params['raise_warnings'] = 0; $object =& $this->getObject($params); /* @var $object kDBItem */ $params['cat_id'] = $object->isLoaded() ? $object->GetDBField('ParentPath') : $this->Application->GetVar('m_cat_id'); return $perm_helper->TagPermissionCheck($params); } /** * Prepares name for field with event in it (used only on front-end) * * @param Array $params * @return string */ function SubmitName($params) { return 'events[' . $this->Prefix . '][' . $params['event'] . ']'; } /** * Returns last modification date of items in category / system * * @param Array $params * @return string */ function LastUpdated($params) { $category_id = (int)$this->Application->GetVar('m_cat_id'); $local = array_key_exists('local', $params) && ($category_id > 0) ? $params['local'] : false; $serial_name = $this->Application->incrementCacheSerial('c', $local ? $category_id : null, false); $cache_key = 'category_last_updated[%' . $serial_name . '%]'; $row_data = $this->Application->getCache($cache_key); if ( $row_data === false ) { if ( $local && ($category_id > 0) ) { // scan only current category & it's children list ($tree_left, $tree_right) = $this->Application->getTreeIndex($category_id); $sql = 'SELECT MAX(Modified) AS ModDate, MAX(CreatedOn) AS NewDate FROM ' . TABLE_PREFIX . 'Category WHERE TreeLeft BETWEEN ' . $tree_left . ' AND ' . $tree_right; } else { // scan all categories in system $sql = 'SELECT MAX(Modified) AS ModDate, MAX(CreatedOn) AS NewDate FROM ' . TABLE_PREFIX . 'Category'; } $this->Conn->nextQueryCachable = true; $row_data = $this->Conn->GetRow($sql); $this->Application->setCache($cache_key, $row_data); } if ( !$row_data ) { return ''; } $date = $row_data[$row_data['NewDate'] > $row_data['ModDate'] ? 'NewDate' : 'ModDate']; // format date $format = isset($params['format']) ? $params['format'] : '_regional_DateTimeFormat'; if ( preg_match("/_regional_(.*)/", $format, $regs) ) { $lang =& $this->Application->recallObject('lang.current'); /* @var $lang LanguagesItem */ if ( $regs[1] == 'DateTimeFormat' ) { // combined format $format = $lang->GetDBField('DateFormat') . ' ' . $lang->GetDBField('TimeFormat'); } else { // simple format $format = $lang->GetDBField($regs[1]); } } return adodb_date($format, $date); } function CategoryItemCount($params) { $object =& $this->getObject($params); /* @var $object kDBList */ $params['cat_id'] = $object->GetID(); $count_helper =& $this->Application->recallObject('CountHelper'); /* @var $count_helper kCountHelper */ return $count_helper->CategoryItemCount($params['prefix'], $params); } /** * Returns prefix + any word (used for shared between categories per page settings) * * @param Array $params * @return string */ function VarName($params) { return $this->Prefix.'_'.$params['type']; } /** * Checks if current category is valid symbolic link to another category * * @param Array $params * @return string */ function IsCategorySymLink($params) { $object =& $this->getObject($params); /* @var $object kDBList */ $sym_category_id = $object->GetDBField('SymLinkCategoryId'); if (is_null($sym_category_id)) { return false; } $id_field = $this->Application->getUnitOption($this->Prefix, 'IDField'); $table_name = $this->Application->getUnitOption($this->Prefix, 'TableName'); $sql = 'SELECT '.$id_field.' FROM '.$table_name.' WHERE '.$id_field.' = '.$sym_category_id; return $this->Conn->GetOne($sql)? true : false; } /** * Returns module prefix based on root category for given * * @param Array $params * @return string */ function GetModulePrefix($params) { $object =& $this->getObject($params); /* @var $object kDBItem */ $parent_path = explode('|', substr($object->GetDBField('ParentPath'), 1, -1)); $category_helper =& $this->Application->recallObject('CategoryHelper'); /* @var $category_helper CategoryHelper */ $module_info = $category_helper->getCategoryModule($params, $parent_path); return $module_info['Var']; } function ImageSrc($params) { list ($ret, $tag_processed) = $this->processAggregatedTag('ImageSrc', $params, $this->getPrefixSpecial()); return $tag_processed ? $ret : false; } function PageLink($params) { $params['m_cat_page'] = $this->Application->GetVar($this->getPrefixSpecial() . '_Page'); return parent::PageLink($params); } /** * Returns spelling suggestions against search keyword * * @param Array $params * @return string * @access protected */ protected function SpellingSuggestions($params) { $keywords = kUtil::unhtmlentities( trim($this->Application->GetVar('keywords')) ); if ( !$keywords ) { return ''; } // 1. try to get already cached suggestion $cache_key = 'search.suggestion[%SpellingDictionary%]:' . $keywords; $suggestion = $this->Application->getCache($cache_key); if ( $suggestion !== false ) { return $suggestion; } $table_name = $this->Application->getUnitOption('spelling-dictionary', 'TableName'); // 2. search suggestion in database $this->Conn->nextQueryCachable = true; $sql = 'SELECT SuggestedCorrection FROM ' . $table_name . ' WHERE MisspelledWord = ' . $this->Conn->qstr($keywords); $suggestion = $this->Conn->GetOne($sql); if ( $suggestion !== false ) { $this->Application->setCache($cache_key, $suggestion); return $suggestion; } // 3. suggestion not found in database, ask webservice $app_id = $this->Application->ConfigValue('YahooApplicationId'); $url = 'http://search.yahooapis.com/WebSearchService/V1/spellingSuggestion?appid=' . $app_id . '&query='; $curl_helper =& $this->Application->recallObject('CurlHelper'); /* @var $curl_helper kCurlHelper */ $xml_data = $curl_helper->Send( $url . urlencode($keywords) ); $xml_helper =& $this->Application->recallObject('kXMLHelper'); /* @var $xml_helper kXMLHelper */ $root_node =& $xml_helper->Parse($xml_data); /* @var $root_node kXMLNode */ $result = $root_node->FindChild('RESULT'); /* @var $result kXMLNode */ if ( is_object($result) ) { // webservice responded -> save in local database $fields_hash = Array ('MisspelledWord' => $keywords, 'SuggestedCorrection' => $result->Data); $this->Conn->doInsert($fields_hash, $table_name); $this->Application->setCache($cache_key, $result->Data); return $result->Data; } return ''; } /** * Shows link for searching by suggested word * * @param Array $params * @return string */ function SuggestionLink($params) { $params['keywords'] = $this->SpellingSuggestions($params); return $this->Application->ProcessParsedTag('m', 'Link', $params); } function InitCatalogTab($params) { $tab_params['mode'] = $this->Application->GetVar('tm'); // single/multi selection possible $tab_params['special'] = $this->Application->GetVar('ts'); // use special for this tab $tab_params['dependant'] = $this->Application->GetVar('td'); // is grid dependant on categories grid // set default params (same as in catalog) if ($tab_params['mode'] === false) $tab_params['mode'] = 'multi'; if ($tab_params['special'] === false) $tab_params['special'] = ''; if ($tab_params['dependant'] === false) $tab_params['dependant'] = 'yes'; // pass params to block with tab content $params['name'] = $params['render_as']; $special = $tab_params['special'] ? $tab_params['special'] : $this->Special; $params['prefix'] = trim($this->Prefix.'.'.$special, '.'); $prefix_append = $this->Application->GetVar('prefix_append'); if ($prefix_append) { $params['prefix'] .= $prefix_append; } $default_grid = array_key_exists('default_grid', $params) ? $params['default_grid'] : 'Default'; $radio_grid = array_key_exists('radio_grid', $params) ? $params['radio_grid'] : 'Radio'; $params['cat_prefix'] = trim('c.'.($tab_params['special'] ? $tab_params['special'] : $this->Special), '.'); $params['tab_mode'] = $tab_params['mode']; $params['grid_name'] = ($tab_params['mode'] == 'multi') ? $default_grid : $radio_grid; $params['tab_dependant'] = $tab_params['dependant']; $params['show_category'] = $tab_params['special'] == 'showall' ? 1 : 0; // this is advanced view -> show category name if ($special == 'showall' || $special == 'user') { $params['grid_name'] .= 'ShowAll'; } // use $pass_params to be able to pass 'tab_init' parameter from m_ModuleInclude tag return $this->Application->ParseBlock($params, 1); } /** * Show CachedNavbar of current item primary category * * @param Array $params * @return string */ function CategoryName($params) { // show category cachednavbar of $object =& $this->getObject($params); + /* @var $object kDBItem */ + $category_id = isset($params['cat_id']) ? $params['cat_id'] : $object->GetDBField('CategoryId'); $cache_key = 'category_paths[%CIDSerial:' . $category_id . '%][%PhrasesSerial%][Adm:' . (int)$this->Application->isAdmin . ']'; $category_path = $this->Application->getCache($cache_key); if ($category_path === false) { // not chached if ($category_id > 0) { $cached_navbar = $object->GetField('CachedNavbar'); if ($category_id == $object->GetDBField('ParentId')) { // parent category cached navbar is one element smaller, then current ones $cached_navbar = explode('&|&', $cached_navbar); array_pop($cached_navbar); $cached_navbar = implode('&|&', $cached_navbar); } else { // no relation with current category object -> query from db $language_id = (int)$this->Application->GetVar('m_lang'); if (!$language_id) { $language_id = 1; } $sql = 'SELECT l' . $language_id . '_CachedNavbar FROM ' . $object->TableName . ' WHERE ' . $object->IDField . ' = ' . $category_id; $cached_navbar = $this->Conn->GetOne($sql); } $cached_navbar = preg_replace('/^(Content&\|&|Content)/i', '', $cached_navbar); $category_path = trim($this->CategoryName( Array('cat_id' => 0) ).' > '.str_replace('&|&', ' > ', $cached_navbar), ' > '); } else { $category_path = $this->Application->Phrase(($this->Application->isAdmin ? 'la_' : 'lu_') . 'rootcategory_name'); } $this->Application->setCache($cache_key, $category_path); } return $category_path; } // structure related /** * Returns page object based on requested params * * @param Array $params * @return CategoriesItem */ function &_getPage($params) { $page =& $this->Application->recallObject($this->Prefix . '.-virtual', null, $params); /* @var $page kDBItem */ // 1. load by given id $page_id = array_key_exists('page_id', $params) ? $params['page_id'] : false; if ($page_id) { if ($page_id != $page->GetID()) { // load if different $page->Load($page_id); } return $page; } // 2. load by template $template = array_key_exists('page', $params) ? $params['page'] : ''; if (!$template) { $template = $this->Application->GetVar('t'); } // different path in structure AND design template differes from requested template $structure_path_match = strtolower( $page->GetDBField('NamedParentPath') ) == strtolower('Content/' . $template); $design_match = $page->GetDBField('CachedTemplate') == $template; if (!$structure_path_match && !$design_match) { // Same sql like in "c:getPassedID". Load, when current page object doesn't match requested page object $themes_helper =& $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ $page_id = $themes_helper->getPageByTemplate($template); $page->Load($page_id); } return $page; } /** * Returns requested content block content of current or specified page * * @param Array $params * @return string */ function ContentBlock($params) { $num = getArrayValue($params, 'num'); if (!$num) { return 'NO CONTENT NUM SPECIFIED'; } $page =& $this->_getPage($params); /* @var $page kDBItem */ if (!$page->isLoaded()) { // page is not created yet => all blocks are empty return ''; } $page_id = $page->GetID(); $content =& $this->Application->recallObject('content.-block', null, Array ('skip_autoload' => true)); /* @var $content kDBItem */ $data = Array ('PageId' => $page_id, 'ContentNum' => $num); $content->Load($data); if (!$content->isLoaded()) { // bug: missing content blocks are created even if user have no SMS-management rights $content->SetFieldsFromHash($data); $content->Create(); } $edit_code_before = $edit_code_after = ''; if (EDITING_MODE == EDITING_MODE_CONTENT) { $bg_color = isset($params['bgcolor']) ? $params['bgcolor'] : '#ffffff'; $url_params = Array ( 'pass' => 'm,c,content', 'm_opener' => 'd', 'c_id' => $page->GetID(), 'content_id' => $content->GetID(), 'front' => 1, 'admin' => 1, '__URLENCODE__' => 1, '__NO_REWRITE__'=> 1, 'escape' => 1, 'index_file' => 'index.php', // 'bgcolor' => $bg_color, // '__FORCE_SID__' => 1 ); // link from Front-End to admin, don't remove "index.php" $edit_url = $this->Application->HREF('categories/edit_content', ADMIN_DIRECTORY, $url_params, 'index.php'); $edit_code_before = ' <div class="cms-edit-btn-container"> <div class="cms-edit-btn" onclick="$form_name=\'kf_cont_'.$content->GetID().'\'; std_edit_item(\'content\', \'categories/edit_content\');"> <div class="cms-btn-image"> <img src="' . $this->Application->BaseURL() . 'core/admin_templates/img/top_frame/icons/content_mode.png" width="15" height="16" alt=""/> </div> <div class="cms-btn-text">' . $this->Application->Phrase('la_btn_EditContent', false, true) . ' '.(defined('DEBUG_MODE') && DEBUG_MODE ? " - #{$num}" : '').'</div> </div> <div class="cms-btn-content">'; $edit_form = '<form method="POST" style="display: inline; margin: 0px" name="kf_cont_'.$content->GetID().'" id="kf_cont_'.$content->GetID().'" action="'.$edit_url.'">'; $edit_form .= '<input type="hidden" name="c_id" value="'.$page->GetID().'"/>'; $edit_form .= '<input type="hidden" name="content_id" value="'.$content->GetID().'"/>'; $edit_form .= '<input type="hidden" name="front" value="1"/>'; $edit_form .= '<input type="hidden" name="bgcolor" value="'.$bg_color.'"/>'; $edit_form .= '<input type="hidden" name="m_lang" value="'.$this->Application->GetVar('m_lang').'"/>'; $edit_form .= '</form>'; $edit_code_after = '</div></div>'; if (array_key_exists('forms_later', $params) && $params['forms_later']) { $all_forms = $this->Application->GetVar('all_forms'); $this->Application->SetVar('all_forms', $all_forms . $edit_form); } else { $edit_code_after .= $edit_form; } } if ($this->Application->GetVar('_editor_preview_') == 1) { $data = $this->Application->RecallVar('_editor_preview_content_'); } else { $data = $content->GetField('Content'); } $data = $edit_code_before . $this->_transformContentBlockData($data, $params) . $edit_code_after; if ($data != '') { $this->Application->Parser->DataExists = true; } return $data; } /** * Apply all kinds of content block data transformations without rewriting ContentBlock tag * * @param string $data * @return string */ function _transformContentBlockData(&$data, $params) { return $data; } /** * Returns current page name or page based on page/page_id parameters * * @param Array $params * @return string * @todo Used? */ function PageName($params) { $page =& $this->_getPage($params); return $page->GetDBField('Name'); } /** * Returns current/given page information * * @param Array $params * @return string */ function PageInfo($params) { $page =& $this->_getPage($params); switch ($params['type']) { case 'title': $db_field = 'Title'; break; case 'htmlhead_title': $db_field = 'Name'; break; case 'meta_title': $db_field = 'MetaTitle'; break; case 'menu_title': $db_field = 'MenuTitle'; break; case 'meta_keywords': $db_field = 'MetaKeywords'; $cat_field = 'Keywords'; break; case 'meta_description': $db_field = 'MetaDescription'; $cat_field = 'Description'; break; case 'tracking': case 'index_tools': if (!EDITING_MODE) { $tracking = $page->GetDBField('IndexTools'); return $tracking ? $tracking : $this->Application->ConfigValue('cms_DefaultTrackingCode'); } // no break here on purpose default: return ''; } $default = isset($params['default']) ? $params['default'] : ''; $val = $page->GetField($db_field); if (!$default) { if ($this->Application->isModuleEnabled('In-Portal')) { if (!$val && ($params['type'] == 'meta_keywords' || $params['type'] == 'meta_description')) { // take category meta if it's not set for the page return $this->Application->ProcessParsedTag('c', 'Meta', Array('name' => $cat_field)); } } } if (isset($params['force_default']) && $params['force_default']) { return $default; } if (preg_match('/^_Auto:/', $val)) { $val = $default; /*if ($db_field == 'Title') { $page->SetDBField($db_field, $default); $page->Update(); }*/ } elseif ($page->GetID() == false) { return $default; } return $val; } /** * Includes admin css and js, that are required for cms usage on Front-Edn * * @param Array $params * @return string * @access protected */ protected function EditingScripts($params) { if ( $this->Application->GetVar('admin_scripts_included') || !EDITING_MODE ) { return ''; } $this->Application->SetVar('admin_scripts_included', 1); $js_url = $this->Application->BaseURL() . 'core/admin_templates/js'; $minify_helper =& $this->Application->recallObject('MinifyHelper'); /* @var $minify_helper MinifyHelper */ $to_compress = Array ( $js_url . '/jquery/thickbox/thickbox.css', $js_url . '/../incs/cms.css', ); $css_compressed = $minify_helper->CompressScriptTag(Array ('files' => implode('|', $to_compress))); $ret = '<link rel="stylesheet" href="' . $css_compressed . '" type="text/css" media="screen"/>' . "\n"; if ( EDITING_MODE == EDITING_MODE_DESIGN ) { $ret .= ' <style type="text/css" media="all"> div.movable-element .movable-header { cursor: move; } </style>'; } $ret .= '<script type="text/javascript" src="' . $js_url . '/jquery/jquery.pack.js"></script>' . "\n"; $ret .= '<script type="text/javascript" src="' . $js_url . '/jquery/jquery-ui.custom.min.js"></script>' . "\n"; $to_compress = Array ( $js_url . '/is.js', $js_url . '/application.js', $js_url . '/script.js', $js_url . '/jquery/thickbox/thickbox.js', $js_url . '/template_manager.js', ); $js_compressed = $minify_helper->CompressScriptTag( Array ('files' => implode('|', $to_compress)) ); $ret .= '<script type="text/javascript" src="' . $js_compressed . '"></script>' . "\n"; $ret .= '<script language="javascript">' . "\n"; $ret .= "TB.pathToImage = '" . $js_url . "/jquery/thickbox/loadingAnimation.gif';" . "\n"; $template = $this->Application->GetVar('t'); $theme_id = $this->Application->GetVar('m_theme'); $url_params = Array ('block' => '#BLOCK#', 'theme-file_event' => '#EVENT#', 'theme_id' => $theme_id, 'source' => $template, 'pass' => 'all,theme-file', 'front' => 1, 'm_opener' => 'd', '__NO_REWRITE__' => 1, 'no_amp' => 1); $edit_template_url = $this->Application->HREF('themes/template_edit', ADMIN_DIRECTORY, $url_params, 'index.php'); $url_params = Array ('theme-file_event' => 'OnSaveLayout', 'source' => $template, 'pass' => 'all,theme-file', '__NO_REWRITE__' => 1, 'no_amp' => 1); $save_layout_url = $this->Application->HREF('index', '', $url_params); $this_url = $this->Application->HREF('', '', Array ('editing_mode' => '#EDITING_MODE#', '__NO_REWRITE__' => 1, 'no_amp' => 1)); $ret .= "var aTemplateManager = new TemplateManager('" . $edit_template_url . "', '" . $this_url . "', '" . $save_layout_url . "', " . (int)EDITING_MODE . ");\n"; $ret .= "var main_title = '" . addslashes( $this->Application->ConfigValue('Site_Name') ) . "';" . "\n"; $use_popups = (int)$this->Application->ConfigValue('UsePopups'); $ret .= "var \$use_popups = " . ($use_popups > 0 ? 'true' : 'false') . ";\n"; $ret .= "var \$modal_windows = " . ($use_popups == 2 ? 'true' : 'false') . ";\n"; if ( EDITING_MODE != EDITING_MODE_BROWSE ) { $ret .= "var base_url = '" . $this->Application->BaseURL() . "';" . "\n"; $ret .= 'TB.closeHtml = \'<img src="' . $js_url . '/../img/close_window15.gif" width="15" height="15" style="border-width: 0px;" alt="close"/><br/>\';' . "\n"; $url_params = Array ('m_theme' => '', 'pass' => 'm', 'm_opener' => 'r', '__NO_REWRITE__' => 1, 'no_amp' => 1); $browse_url = $this->Application->HREF('catalog/catalog', ADMIN_DIRECTORY, $url_params, 'index.php'); $browse_url = preg_replace('/&(admin|editing_mode)=[\d]/', '', $browse_url); $ret .= ' var topmost = window.top; topmost.document.title = document.title + \' - ' . addslashes($this->Application->Phrase('la_AdministrativeConsole', false)) . '\'; t = \'' . $this->Application->GetVar('t') . '\'; if (window.parent.frames["menu"] != undefined) { if ( $.isFunction(window.parent.frames["menu"].SyncActive) ) { window.parent.frames["menu"].SyncActive("' . $browse_url . '"); } } '; } $ret .= '</script>' . "\n"; if ( EDITING_MODE != EDITING_MODE_BROWSE ) { // add form, so admin scripts could work $ret .= '<form id="kernel_form" name="kernel_form" enctype="multipart/form-data" method="post" action="' . $browse_url . '"> <input type="hidden" name="MAX_FILE_SIZE" id="MAX_FILE_SIZE" value="' . MAX_UPLOAD_SIZE . '" /> <input type="hidden" name="sid" id="sid" value="' . $this->Application->GetSID() . '" /> </form>'; } return $ret; } /** * Prints "Edit Page" button on cms page * * @param Array $params * @return string */ function EditPage($params) { if (!EDITING_MODE) { return ''; } $display_mode = array_key_exists('mode', $params) ? $params['mode'] : false; $edit_code = ''; $page =& $this->_getPage($params); if (!$page->isLoaded() || (($display_mode != 'end') && (EDITING_MODE == EDITING_MODE_BROWSE))) { // when "EditingScripts" tag is not used, make sure, that scripts are also included return $this->EditingScripts($params); } // show "EditPage" button only for pages, that exists in structure if ($display_mode != 'end') { $edit_btn = ''; if (EDITING_MODE == EDITING_MODE_CONTENT) { $url_params = Array( 'pass' => 'm,c', 'm_opener' => 'd', 'c_id' => $page->GetID(), 'c_mode' => 't', 'c_event' => 'OnEdit', 'front' => 1, '__URLENCODE__' => 1, '__NO_REWRITE__'=> 1, 'index_file' => 'index.php', ); $edit_url = $this->Application->HREF('categories/categories_edit', ADMIN_DIRECTORY, $url_params); $edit_btn .= ' <div class="cms-section-properties-btn"' . ($display_mode === false ? ' style="margin: 0px;"' : '') . ' onmouseover="window.status=\'' . addslashes($edit_url) . '\'; return true" onclick="$form_name=\'kf_'.$page->GetID().'\'; std_edit_item(\'c\', \'categories/categories_edit\');"> <div class="cms-btn-image"> <img src="' . $this->Application->BaseURL() . 'core/admin_templates/img/top_frame/icons/section_properties.png" width="15" height="16" alt=""/> </div> <div class="cms-btn-text">' . $this->Application->Phrase('la_btn_SectionProperties', false, true) . '</div> </div>' . "\n"; } elseif (EDITING_MODE == EDITING_MODE_DESIGN) { $url_params = Array( 'pass' => 'm,theme,theme-file', 'm_opener' => 'd', 'theme_id' => $this->Application->GetVar('m_theme'), 'theme_mode' => 't', 'theme_event' => 'OnEdit', 'theme-file_id' => $this->_getThemeFileId(), 'front' => 1, '__URLENCODE__' => 1, '__NO_REWRITE__'=> 1, 'index_file' => 'index.php', ); $edit_url = $this->Application->HREF('themes/file_edit', ADMIN_DIRECTORY, $url_params); $edit_btn .= ' <div class="cms-layout-btn-container"' . ($display_mode === false ? ' style="margin: 0px;"' : '') . '> <div class="cms-save-layout-btn" onclick="aTemplateManager.saveLayout(); return false;"> <div class="cms-btn-image"> <img src="' . $this->Application->BaseURL() . 'core/admin_templates/img/top_frame/icons/save_button.gif" width="16" height="16" alt=""/> </div> <div class="cms-btn-text">' . $this->Application->Phrase('la_btn_SaveChanges', false, true) . '</div> </div> <div class="cms-cancel-layout-btn" onclick="aTemplateManager.cancelLayout(); return false;"> <div class="cms-btn-image"> <img src="' . $this->Application->BaseURL() . 'core/admin_templates/img/top_frame/icons/cancel_button.gif" width="16" height="16" alt=""/> </div> <div class="cms-btn-text">' . $this->Application->Phrase('la_btn_Cancel', false, true) . '</div> </div> </div> <div class="cms-section-properties-btn"' . ($display_mode === false ? ' style="margin: 0px;"' : '') . ' onmouseover="window.status=\'' . addslashes($edit_url) . '\'; return true" onclick="$form_name=\'kf_'.$page->GetID().'\'; std_edit_item(\'theme\', \'themes/file_edit\');"> <div class="cms-btn-image"> <img src="' . $this->Application->BaseURL() . 'core/admin_templates/img/top_frame/icons/section_properties.png" width="15" height="16" alt=""/> </div> <div class="cms-btn-text">' . $this->Application->Phrase('la_btn_SectionTemplate', false, true) . '</div> </div>' . "\n"; } if ($display_mode == 'start') { // button with border around the page $edit_code .= '<div class="cms-section-properties-btn-container">' . $edit_btn . '<div class="cms-btn-content">'; } else { // button without border around the page $edit_code .= $edit_btn; } } if ($display_mode == 'end') { // draw border around the page $edit_code .= '</div></div>'; } if ($display_mode != 'end') { $edit_code .= '<form method="POST" style="display: inline; margin: 0px" name="kf_'.$page->GetID().'" id="kf_'.$page->GetID().'" action="'.$edit_url.'"></form>'; // when "EditingScripts" tag is not used, make sure, that scripts are also included $edit_code .= $this->EditingScripts($params); } return $edit_code; } function _getThemeFileId() { $template = $this->Application->GetVar('t'); if (!$this->Application->TemplatesCache->TemplateExists($template) && !$this->Application->isAdmin) { $cms_handler =& $this->Application->recallObject($this->Prefix . '_EventHandler'); /* @var $cms_handler CategoriesEventHandler */ $template = ltrim($cms_handler->GetDesignTemplate(), '/'); } $file_path = dirname($template) == '.' ? '' : '/' . dirname($template); $file_name = basename($template); $sql = 'SELECT FileId FROM ' . TABLE_PREFIX . 'ThemeFiles WHERE (ThemeId = ' . (int)$this->Application->GetVar('m_theme') . ') AND (FilePath = ' . $this->Conn->qstr($file_path) . ') AND (FileName = ' . $this->Conn->qstr($file_name . '.tpl') . ')'; return $this->Conn->GetOne($sql); } /** * Builds site menu * * @param Array $params * @return string */ function CachedMenu($params) { $menu_helper =& $this->Application->recallObject('MenuHelper'); /* @var $menu_helper MenuHelper */ return $menu_helper->menuTag($this->getPrefixSpecial(), $params); } /** * Trick to allow some kind of output formatting when using CachedMenu tag * * @param Array $params * @return bool */ function SplitColumn($params) { return $this->Application->GetVar($params['i']) > ceil($params['total'] / $params['columns']); } /** * Returns direct children count of given category * * @param Array $params * @return int */ function HasSubCats($params) { $sql = 'SELECT COUNT(*) FROM ' . TABLE_PREFIX . 'Category WHERE ParentId = ' . $params['cat_id']; return $this->Conn->GetOne($sql); } /** * Prints sub-pages of given/current page. * * @param Array $params * @return string * @todo This could be reached by using "parent_cat_id" parameter. Only difference here is new block parameter "path". Need to rewrite. */ function PrintSubPages($params) { $list =& $this->Application->recallObject($this->getPrefixSpecial(), $this->Prefix.'_List', $params); /* @var $list kDBList */ $category_id = array_key_exists('category_id', $params) ? $params['category_id'] : $this->Application->GetVar('m_cat_id'); $list->addFilter('current_pages', TABLE_PREFIX . 'CategoryItems.CategoryId = ' . $category_id); $list->Query(); $list->GoFirst(); $o = ''; $block_params = $this->prepareTagParams($params); $block_params['name'] = $params['render_as']; while (!$list->EOL()) { $block_params['path'] = $list->GetDBField('Path'); $o .= $this->Application->ParseBlock($block_params); $list->GoNext(); } return $o; } /** * Builds link for browsing current page on Front-End * * @param Array $params * @return string */ function PageBrowseLink($params) { $object =& $this->getObject($params); + /* @var $object kDBItem */ $themes_helper =& $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ $site_config_helper =& $this->Application->recallObject('SiteConfigHelper'); /* @var $site_config_helper SiteConfigHelper */ $settings = $site_config_helper->getSettings(); $url_params = Array ( 'm_cat_id' => $object->GetID(), 'm_theme' => $themes_helper->getCurrentThemeId(), 'editing_mode' => $settings['default_editing_mode'], 'pass' => 'm', 'admin' => 1, 'index_file' => 'index.php' ); if ($this->Application->ConfigValue('UseModRewrite')) { $url_params['__MOD_REWRITE__'] = 1; } return $this->Application->HREF($object->GetDBField('NamedParentPath'), '_FRONT_END_', $url_params); } function DirectLink($params) { $object =& $this->getObject($params); + /* @var $object kDBItem */ $themes_helper =& $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ $url_params = Array ( 'm_cat_id' => $object->GetID(), 'm_theme' => $themes_helper->getCurrentThemeId(), 'pass' => 'm', 'index_file' => 'index.php', 'authkey' => $object->GetDBField('DirectLinkAuthKey'), ); if ($this->Application->ConfigValue('UseModRewrite')) { $url_params['__MOD_REWRITE__'] = 1; } return $this->Application->HREF($object->GetDBField('NamedParentPath'), '_FRONT_END_', $url_params); } /** * Builds link to cms page (used?) * * @param Array $params * @return string */ function ContentPageLink($params) { $object =& $this->getObject($params); + /* @var $object kDBItem */ + $params['t'] = $object->GetDBField('NamedParentPath'); $params['m_cat_id'] = 0; return $this->Application->ProcessParsedTag('m', 'Link', $params); } /** * Prepares cms page description for search result page * * @param Array $params * @return string */ function SearchDescription($params) { $object =& $this->getObject($params); $desc = $object->GetField('MetaDescription'); if (!$desc) { $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'PageContent WHERE PageId = ' . $object->GetID() . ' AND ContentNum = 1'; $content = $this->Conn->GetRow($sql); if ($content['l'.$this->Application->GetVar('m_lang').'_Content']) { $desc = $content['l'.$this->Application->GetVar('m_lang').'_Content']; } else { $desc = $content['l'.$this->Application->GetDefaultLanguageId().'_Content']; } } return mb_substr($desc, 0, 300).(mb_strlen($desc) > 300 ? '...' : ''); } /** * Simplified version of "c:CategoryLink" for "c:PrintList" * * @param Array $params * @return string * @todo Used? Needs refactoring. */ function EnterCatLink($params) { $object =& $this->getObject($params); $url_params = Array ('pass' => 'm', 'm_cat_id' => $object->GetID()); return $this->Application->HREF($params['template'], '', $url_params); } /** * Simplified version of "c:CategoryPath", that do not use blocks for rendering * * @param Array $params * @return string * @todo Used? Maybe needs to be removed. */ function PagePath($params) { $object =& $this->getObject($params); $path = $object->GetField('CachedNavbar'); if ($path) { $items = explode('&|&', $path); array_shift($items); return implode(' -> ', $items); } return ''; } /** * Returns configuration variable value * * @param Array $params * @return string * @todo Needs to be replaced with "m:GetConfig" tag; Not used now (were used on structure_edit.tpl). */ function AllowManualFilenames($params) { return $this->Application->ConfigValue('ProjCMSAllowManualFilenames'); } /** * Draws path to current page (each page can be link to it) * * @param Array $params * @return string */ function CurrentPath($params) { $block_params = $this->prepareTagParams($params); $block_params['name'] = $block_params['render_as']; $object =& $this->Application->recallObject($this->Prefix); /* @var $object kDBItem */ $category_ids = explode('|', substr($object->GetDBField('ParentPath'), 1, -1)); $id_field = $this->Application->getUnitOption($this->Prefix, 'IDField'); $table_name = $this->Application->getUnitOption($this->Prefix, 'TableName'); $language = (int)$this->Application->GetVar('m_lang'); if (!$language) { $language = 1; } $sql = 'SELECT l'.$language.'_Name AS Name, NamedParentPath FROM '.$table_name.' WHERE '.$id_field.' IN ('.implode(',', $category_ids).')'; $categories_data = $this->Conn->Query($sql); $ret = ''; foreach ($categories_data as $index => $category_data) { if ($category_data['Name'] == 'Content') { continue; } $block_params['title'] = $category_data['Name']; $block_params['template'] = preg_replace('/^Content\//i', '', $category_data['NamedParentPath']); $block_params['is_first'] = $index == 1; // because Content is 1st element $block_params['is_last'] = $index == count($categories_data) - 1; $ret .= $this->Application->ParseBlock($block_params); } return $ret; } /** * Synonim to PrintList2 for "onlinestore" theme * * @param Array $params * @return string */ function ListPages($params) { return $this->PrintList2($params); } /** * Returns information about parser element locations in template * * @param Array $params * @return mixed */ function BlockInfo($params) { if (!EDITING_MODE) { return ''; } $template_helper =& $this->Application->recallObject('TemplateHelper'); /* @var $template_helper TemplateHelper */ return $template_helper->blockInfo( $params['name'] ); } /** * Hide all editing tabs except permission tab, when editing "Home" (ID = 0) category * * @param Array $params */ function ModifyUnitConfig($params) { $root_category = $this->Application->RecallVar('IsRootCategory_' . $this->Application->GetVar('m_wid')); if (!$root_category) { return ; } $edit_tab_presets = $this->Application->getUnitOption($this->Prefix, 'EditTabPresets'); $edit_tab_presets['Default'] = Array ( 'permissions' => $edit_tab_presets['Default']['permissions'], ); $this->Application->setUnitOption($this->Prefix, 'EditTabPresets', $edit_tab_presets); } /** * Prints catalog export templates * * @param Array $params * @return string */ function PrintCatalogExportTemplates($params) { $prefixes = explode(',', $params['prefixes']); $ret = Array (); foreach ($prefixes as $prefix) { if ($this->Application->prefixRegistred($prefix)) { $module_path = $this->Application->getUnitOption($prefix, 'ModuleFolder') . '/'; $module_name = $this->Application->findModule('Path', $module_path, 'Name'); $ret[$prefix] = mb_strtolower($module_name) . '/export'; } } $json_helper =& $this->Application->recallObject('JSONHelper'); /* @var $json_helper JSONHelper */ return $json_helper->encode($ret); } /** * Checks, that "view in browse mode" functionality available * * @param Array $params * @return bool */ function BrowseModeAvailable($params) { $valid_special = $params['Special'] != 'user'; $not_selector = $this->Application->GetVar('type') != 'item_selector'; return $valid_special && $not_selector; } /** * Returns a link for editing product * * @param Array $params * @return string */ function ItemEditLink($params) { $object =& $this->getObject(); /* @var $object kDBList */ $edit_template = $this->Application->getUnitOption($this->Prefix, 'AdminTemplatePath') . '/' . $this->Application->getUnitOption($this->Prefix, 'AdminTemplatePrefix') . 'edit'; $url_params = Array ( 'm_opener' => 'd', $this->Prefix.'_mode' => 't', $this->Prefix.'_event' => 'OnEdit', $this->Prefix.'_id' => $object->GetID(), 'm_cat_id' => $object->GetDBField('ParentId'), 'pass' => 'all,'.$this->Prefix, 'no_pass_through' => 1, ); return $this->Application->HREF($edit_template,'', $url_params); } function RelevanceIndicator($params) { $object =& $this->getObject($params); + /* @var $object kDBItem */ $search_results_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search'; $sql = 'SELECT Relevance FROM '.$search_results_table.' WHERE ResourceId = '.$object->GetDBField('ResourceId'); $percents_off = (int)(100 - (100 * $this->Conn->GetOne($sql))); $percents_off = ($percents_off < 0) ? 0 : $percents_off; if ($percents_off) { $params['percent_off'] = $percents_off; $params['percent_on'] = 100 - $percents_off; $params['name'] = $this->SelectParam($params, 'relevance_normal_render_as,block_relevance_normal'); } else { $params['name'] = $this->SelectParam($params, 'relevance_full_render_as,block_relevance_full'); } return $this->Application->ParseBlock($params); } /** * Returns list of categories, that have category add/edit permission * * @param Array $params * @return string */ function AllowedCategoriesJSON($params) { if ($this->Application->RecallVar('user_id') == USER_ROOT) { $categories = true; } else { $object =& $this->getObject($params); /* @var $object kDBItem */ $perm_helper =& $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $perm_prefix = $this->Application->getUnitOption($this->Prefix, 'PermItemPrefix'); $categories = $perm_helper->getPermissionCategories($perm_prefix . '.' . ($object->IsNewItem() ? 'ADD' : 'MODIFY')); } $json_helper =& $this->Application->recallObject('JSONHelper'); /* @var $json_helper JSONHelper */ return $json_helper->encode($categories); } function PageEditable($params) { if ($this->Application->isDebugMode()) { return true; } $object =& $this->getObject($params); /* @var $object kDBItem */ return !$object->GetDBField('Protected'); } } \ No newline at end of file Index: branches/5.2.x/core/units/themes/themes_eh.php =================================================================== --- branches/5.2.x/core/units/themes/themes_eh.php (revision 14674) +++ branches/5.2.x/core/units/themes/themes_eh.php (revision 14675) @@ -1,202 +1,202 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class ThemesEventHandler extends kDBEventHandler { /** - * Allows to override standart permission mapping + * Allows to override standard permission mapping * */ function mapPermissions() { parent::mapPermissions(); $permissions = Array( 'OnChangeTheme' => Array('self' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Checks user permission to execute given $event * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(&$event) { if ($event->Name == 'OnItemBuild') { // check permission without using $event->getSection(), // so first cache rebuild won't lead to "ldefault_Name" field being used return true; } return parent::CheckPermission($event); } /** * Allows to set selected theme as primary * * @param kEvent $event */ function OnSetPrimary(&$event) { if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { $event->status = kEvent::erFAIL; return; } $ids = $this->StoreSelectedIDs($event); if ($ids) { $id = array_shift($ids); $this->setPrimary($id); $rebuild_event = new kEvent('adm:OnRebuildThemes'); $this->Application->HandleEvent($rebuild_event); } $this->clearSelectedIDs($event); } function setPrimary($id) { $id_field = $this->Application->getUnitOption($this->Prefix, 'IDField'); $table_name = $this->Application->getUnitOption($this->Prefix, 'TableName'); $sql = 'UPDATE '.$table_name.' SET PrimaryTheme = 0'; $this->Conn->Query($sql); $sql = 'UPDATE '.$table_name.' SET PrimaryTheme = 1, Enabled = 1 WHERE '.$id_field.' = '.$id; $this->Conn->Query($sql); } /** * Set's primary theme (when checkbox used on editing form) * * @param kEvent $event */ function OnAfterCopyToLive(&$event) { $object =& $this->Application->recallObject($event->Prefix.'.-item', null, Array('skip_autoload' => true, 'live_table' => true)); /* @var $object kDBItem */ $object->Load($event->getEventParam('id')); if ($object->GetDBField('PrimaryTheme')) { $this->setPrimary($event->getEventParam('id')); } } /** * Also rebuilds theme files, when enabled theme is saved * * @param kEvent $event * @return void * @access protected */ protected function OnSave(&$event) { parent::OnSave($event); if ( ($event->status != kEvent::erSUCCESS) || !$event->getEventParam('ids') ) { return ; } $ids = $event->getEventParam('ids'); $id_field = $this->Application->getUnitOption($event->Prefix, 'IDField'); $table_name = $this->Application->getUnitOption($event->Prefix, 'TableName'); $sql = 'SELECT COUNT(*) FROM ' . $table_name . ' WHERE ' . $id_field . ' IN (' . $ids . ') AND (Enabled = 1)'; $enabled_themes = $this->Conn->GetOne($sql); if ( $enabled_themes ) { $rebuild_event = new kEvent('adm:OnRebuildThemes'); $this->Application->HandleEvent($rebuild_event); } } /** * Allows to change the theme * * @param kEvent $event */ function OnChangeTheme(&$event) { if ($this->Application->isAdminUser) { // for structure theme dropdown $this->Application->StoreVar('theme_id', $this->Application->GetVar('theme')); $this->Application->StoreVar('RefreshStructureTree', 1); return ; } $this->Application->SetVar('t', 'index'); $this->Application->SetVar('m_cat_id', 0); if (MOD_REWRITE) { $mod_rewrite_helper =& $this->Application->recallObject('ModRewriteHelper'); /* @var $mod_rewrite_helper kModRewriteHelper */ $mod_rewrite_helper->removePages(); } $this->Application->SetVar('m_theme', $this->Application->GetVar('theme')); } /** * Apply system filter to themes list * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(&$event) { parent::SetCustomQuery($event); $object =& $event->getObject(); /* @var $object kDBList */ if (in_array($event->Special, Array ('enabled', 'selected', 'available')) || !$this->Application->isAdminUser) { // "enabled" special or Front-End $object->addFilter('enabled_filter', '%1$s.Enabled = ' . STATUS_ACTIVE); } // site domain theme picker if ($event->Special == 'selected' || $event->Special == 'available') { $edit_picker_helper =& $this->Application->recallObject('EditPickerHelper'); /* @var $edit_picker_helper EditPickerHelper */ $edit_picker_helper->applyFilter($event, 'Themes'); } // apply domain-based theme filtering $themes = $this->Application->siteDomainField('Themes'); if (strlen($themes)) { $themes = explode('|', substr($themes, 1, -1)); $object->addFilter('domain_filter', '%1$s.ThemeId IN (' . implode(',', $themes) . ')'); } } } Index: branches/5.2.x/core/units/translator/translator_event_handler.php =================================================================== --- branches/5.2.x/core/units/translator/translator_event_handler.php (revision 14674) +++ branches/5.2.x/core/units/translator/translator_event_handler.php (revision 14675) @@ -1,174 +1,174 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class TranslatorEventHandler extends kDBEventHandler { /** - * Allows to override standart permission mapping + * Allows to override standard permission mapping * */ function mapPermissions() { parent::mapPermissions(); $permissions = Array( 'OnChangeLanguage' => Array('subitem' => 'add|edit'), 'OnSaveAndClose' => Array('subitem' => 'add|edit'), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Check permission of item, that being translated * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(&$event) { list($prefix, $field) = $this->getPrefixAndField($event); $top_prefix = $this->Application->GetTopmostPrefix($prefix, true); $event->setEventParam('top_prefix', $top_prefix); return parent::CheckPermission($event); } /** * Returns prefix and field being translated * * @param kEvent $event */ function getPrefixAndField(&$event) { $field = $this->Application->GetVar($event->getPrefixSpecial(true).'_field'); if (strpos($field,':') !== false) { list($prefix, $field) = explode(':', $field); } else { $prefix = $this->Application->GetVar($event->getPrefixSpecial(true).'_prefix'); } return Array($prefix, $field); } /** * Loads record to be translated * * @param kEvent $event * @return void * @access protected */ protected function OnLoad(&$event) { list($obj_prefix, $field) = $this->getPrefixAndField($event); $object =& $this->Application->recallObject($obj_prefix); /* @var $object kDBItem */ $translator =& $event->getObject(); /* @var $translator kDBItem */ $def_lang = $this->Application->GetDefaultLanguageId(); $current_lang = $translator->GetDBField('Language'); if (!$current_lang) $current_lang = $this->Application->RecallVar('trans_lang'); if (!$current_lang) $current_lang = $this->Application->GetVar('m_lang'); /*if ($current_lang == $def_lang) { $current_lang = $def_lang + 1; }*/ $this->Application->StoreVar('trans_lang', $current_lang); //remember translation language for user friendlyness $translator->SetID(1); $translator->SetDBField('Original', $object->GetDBField('l'.$this->Application->GetVar('m_lang').'_'.$field)); $translator->SetDBField('Language', $current_lang); $translator->SetDBField('SwitchLanguage', $current_lang); $translator->SetDBField('Translation', $object->GetDBField('l'.$current_lang.'_'.$field)); $cur_lang =& $this->Application->recallObject('lang.current'); /* @var $cur_lang LanguagesItem */ $cur_lang->Load($current_lang); $translator->SetDBField('Charset', $cur_lang->GetDBField('Charset')); $event->redirect = false; } /** * Saves changes into temporary table and closes editing window - * + * * @param kEvent $event * @return void * @access protected */ protected function OnSaveAndClose(&$event) { $event->CallSubEvent('OnPreSave'); $event->SetRedirectParam('opener', 'u'); } /** * Saves edited item into temp table * If there is no id, new item is created in temp table * * @param kEvent $event * @return void * @access protected */ protected function OnPreSave(&$event) { $translator =& $event->getObject(); /* @var $translator kDBItem */ $translator->SetFieldsFromHash( $this->getSubmittedFields($event) ); list($obj_prefix, $field) = $this->getPrefixAndField($event); $object =& $this->Application->recallObject($obj_prefix); /* @var $object kDBItem */ $lang = $translator->GetDBField('Language'); $object->SetFieldOptions('l' . $lang . '_' . $field, Array ()); $object->SetDBField('l' . $lang . '_' . $field, $translator->GetDBField('Translation')); $this->RemoveRequiredFields($object); $object->Update(); } /** * Changes current language in translation popup * * @param kEvent $event * @return void * @access protected */ protected function OnChangeLanguage(&$event) { $event->CallSubEvent('OnPreSave'); $object =& $event->getObject(); /* @var $object kDBItem */ $object->SetDBField('Language', $object->GetDBField('SwitchLanguage')); $event->CallSubEvent('OnLoad'); $event->redirect = false; } } \ No newline at end of file Index: branches/5.2.x/core/units/mailing_lists/mailing_list_eh.php =================================================================== --- branches/5.2.x/core/units/mailing_lists/mailing_list_eh.php (revision 14674) +++ branches/5.2.x/core/units/mailing_lists/mailing_list_eh.php (revision 14675) @@ -1,330 +1,330 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class MailingListEventHandler extends kDBEventHandler { /** - * Allows to override standart permission mapping + * Allows to override standard permission mapping * */ function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnCancelMailing' => Array ('self' => 'edit'), 'OnGenerateEmailQueue' => Array ('self' => true), 'OnProcessEmailQueue' => Array ('self' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Prepare recipient list * * @param kEvent $event */ function OnNew(&$event) { parent::OnNew($event); $recipient_type = $this->Application->GetVar('mailing_recipient_type'); if (!$recipient_type) { return ; } $recipients = $this->Application->GetVar($recipient_type); if ($recipients) { $object =& $event->getObject(); /* @var $object kDBItem */ $to = $recipient_type . '_' . implode(';' . $recipient_type . '_', array_keys($recipients)); $object->SetDBField('To', $to); } } /** * Don't allow to delete mailings in progress * * @param kEvent $event * @param string $type * @return void * @access protected */ protected function customProcessing(&$event, $type) { if ($event->Name == 'OnMassDelete' && $type == 'before') { $ids = $event->getEventParam('ids'); if ($ids) { $id_field = $this->Application->getUnitOption($event->Prefix, 'IDField'); $table_name = $this->Application->getUnitOption($event->Prefix, 'TableName'); $sql = 'SELECT ' . $id_field . ' FROM ' . $table_name . ' WHERE ' . $id_field . ' IN (' . implode(',', $ids) . ') AND Status <> ' . MailingList::PARTIALLY_PROCESSED; $allowed_ids = $this->Conn->GetCol($sql); $event->setEventParam('ids', $allowed_ids); } } } /** * Delete all realted mails in email queue * * @param kEvent $event */ function OnAfterItemDelete(&$event) { parent::OnAfterItemDelete($event); $this->_deleteQueue($event); $object =& $event->getObject(); /* @var $object kDBItem */ // delete mailing attachments after mailing is deleted $attachments = $object->GetField('Attachments', 'file_paths'); if ($attachments) { $attachments = explode('|', $attachments); foreach ($attachments as $attachment_file) { if (file_exists($attachment_file)) { unlink($attachment_file); } } } } /** * Cancels given mailing and deletes all it's email queue * * @param kEvent $event */ function OnCancelMailing(&$event) { $object =& $event->getObject( Array('skip_autoload' => true) ); /* @var $object kDBItem */ $ids = $this->StoreSelectedIDs($event); if ($ids) { foreach ($ids as $id) { $object->Load($id); $object->SetDBField('Status', MailingList::CANCELLED); $object->Update(); } } $this->clearSelectedIDs($event); } /** * Checks, that at least one message text field is filled * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(&$event) { parent::OnBeforeItemCreate($event); $object =& $event->getObject(); /* @var $object kDBItem */ if ( !$this->Application->GetVar('mailing_recipient_type') ) { // user manually typed email addresses -> normalize $recipients = str_replace(',', ';', $object->GetDBField('To')); $recipients = array_map('trim', explode(';', $recipients)); $object->SetDBField('To', implode(';', $recipients)); } if ( !$object->GetDBField('MessageText') ) { $object->setRequired('MessageHtml'); } // remember user, who created mailing, because of his name // is needed for "From" field, but mailing occurs from cron $user_id = $this->Application->RecallVar('user_id'); $object->SetDBField('PortalUserId', $user_id); } /** * Deletes mailing list email queue, when it becomes cancelled * * @param kEvent $event */ function OnAfterItemUpdate(&$event) { parent::OnAfterItemUpdate($event); $object =& $event->getObject(); /* @var $object kDBItem */ $status = $object->GetDBField('Status'); if (($status != $object->GetOriginalField('Status')) && ($status == MailingList::CANCELLED)) { $this->_deleteQueue($event); } } /** * Deletes email queue records related with given mailing list * * @param kEvent $event */ function _deleteQueue(&$event) { $object =& $event->getObject(); /* @var $object kDBItem */ $sql = 'DELETE FROM ' . $this->Application->getUnitOption('email-queue', 'TableName') . ' WHERE MailingId = ' . $object->GetID(); $this->Conn->Query($sql); } /** * Allows to safely get mailing configuration variables * * @param string $variable_name * @return int */ function _ensureDefault($variable_name) { $value = $this->Application->ConfigValue($variable_name); if ($value === false) { // ensure default value, when configuration variable is missing return 10; } if (!$value) { // configuration variable found, but it's value is empty or zero return false; } return $value; } /** * Generates email queue for active mailing lists * * @param kEvent $event */ function OnGenerateEmailQueue(&$event) { $id_field = $this->Application->getUnitOption($event->Prefix, 'IDField'); $table_name = $this->Application->getUnitOption($event->Prefix, 'TableName'); $where_clause = Array ( 'Status NOT IN (' . MailingList::CANCELLED . ',' . MailingList::PROCESSED . ')', '(EmailsQueued < EmailsTotal) OR (EmailsTotal = 0)', '`To` <> ""', ); $sql = 'SELECT * FROM ' . $table_name . ' WHERE (' . implode(') AND (', $where_clause) . ') ORDER BY ' . $id_field . ' ASC'; $mailing_lists = $this->Conn->Query($sql, $id_field); if (!$mailing_lists) { return ; } // queue 10 emails per step summary from all mailing lists (FIFO logic) $to_queue = $this->_ensureDefault('MailingListQueuePerStep'); if ($to_queue === false) { return ; } $mailing_list_helper =& $this->Application->recallObject('MailingListHelper'); /* @var $mailing_list_helper MailingListHelper */ foreach ($mailing_lists as $mailing_id => $mailing_data) { if ($mailing_data['EmailsTotal'] == 0) { // no work performed on this mailing list -> calculate totals $updated_fields = $mailing_list_helper->generateRecipients($mailing_id, $mailing_data); $updated_fields['Status'] = MailingList::PARTIALLY_PROCESSED; $mailing_data = array_merge($mailing_data, $updated_fields); $this->Conn->doUpdate($updated_fields, $table_name, $id_field . ' = ' . $mailing_id); } $emails = unserialize( $mailing_data['ToParsed'] ); if (!$emails) { continue; } // queue allowed count of emails $i = 0; $process_count = count($emails) >= $to_queue ? $to_queue : count($emails); while ($i < $process_count) { $mailing_list_helper->queueEmail($emails[$i], $mailing_id, $mailing_data); $i++; } // remove processed emails from array $to_queue -= $process_count; // decrement available for processing email count array_splice($emails, 0, $process_count); $updated_fields = Array ( 'ToParsed' => serialize($emails), 'EmailsQueued' => $mailing_data['EmailsQueued'] + $process_count, ); $mailing_data = array_merge($mailing_data, $updated_fields); $this->Conn->doUpdate($updated_fields, $table_name, $id_field . ' = ' . $mailing_id); if (!$to_queue) { // emails to be queued per step reached -> leave break; } } } /** * Process email queue from cron * * @param kEvent $event */ function OnProcessEmailQueue(&$event) { $deliver_count = $this->_ensureDefault('MailingListSendPerStep'); if ($deliver_count === false) { return ; } $queue_table = $this->Application->getUnitOption('email-queue', 'TableName'); // get queue part to send $sql = 'SELECT * FROM ' . $queue_table . ' WHERE (SendRetries < 5) AND (LastSendRetry < ' . strtotime('-2 hours') . ') LIMIT 0,' . $deliver_count; $messages = $this->Conn->Query($sql); if (!$messages) { // no more messages left in queue return ; } $mailing_list_helper =& $this->Application->recallObject('MailingListHelper'); /* @var $mailing_list_helper MailingListHelper */ $mailing_list_helper->processQueue($messages); } } \ No newline at end of file Index: branches/5.2.x/core/units/favorites/favorites_eh.php =================================================================== --- branches/5.2.x/core/units/favorites/favorites_eh.php (revision 14674) +++ branches/5.2.x/core/units/favorites/favorites_eh.php (revision 14675) @@ -1,104 +1,104 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class FavoritesEventHandler extends kDBEventHandler { /** - * Allows to override standart permission mapping + * Allows to override standard permission mapping * */ function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnFavoriteToggle' => Array('subitem' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Adds/removes item from favorites * * @param kEvent $event */ function OnFavoriteToggle(&$event) { $parent_prefix = $this->Application->getUnitOption($event->Prefix, 'ParentPrefix'); $parent_object =& $this->Application->recallObject($parent_prefix); /* @var $parent_object kDBItem */ if (!$parent_object->isLoaded() || !$this->Application->CheckPermission('FAVORITES', 0, $parent_object->GetDBField('ParentPath'))) { $event->status = kEvent::erPERM_FAIL; return ; } $user_id = $this->Application->RecallVar('user_id'); $sql = 'SELECT FavoriteId FROM '.$this->Application->getUnitOption($event->Prefix, 'TableName').' WHERE (PortalUserId = '.$user_id.') AND (ResourceId = '.$parent_object->GetDBField('ResourceId').')'; $favorite_id = $this->Conn->GetOne($sql); $object =& $event->getObject(Array('skip_autoload' => true)); /* @var $object kDBItem */ if ($favorite_id) { $object->Delete($favorite_id); } else { $object->Create(); } $event->SetRedirectParam('pass', 'm,'.$parent_prefix); } /** * Prepares Favorite record fields * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(&$event) { $object =& $event->getObject(); /* @var $object kDBItem */ $user_id = $this->Application->RecallVar('user_id'); $object->SetDBField('PortalUserId', $user_id); $parent_prefix = $this->Application->getUnitOption($event->Prefix, 'ParentPrefix'); $parent_object =& $this->Application->recallObject($parent_prefix); /* @var $parent_object kDBItem */ $object->SetDBField('ResourceId', $parent_object->GetDBField('ResourceId')); $object->SetDBField('ItemTypeId', $this->Application->getUnitOption($parent_prefix, 'ItemType')); } /** * [HOOK] Deletes favorite record to item, that is beeing deleted * * @param kEvent $event */ function OnDeleteFavoriteItem(&$event) { $main_object =& $event->MasterEvent->getObject(); $sql = 'DELETE FROM '.$this->Application->getUnitOption($event->Prefix, 'TableName').' WHERE ResourceId = '.$main_object->GetDBField('ResourceId'); $this->Conn->Query($sql); } } \ No newline at end of file Index: branches/5.2.x/core/units/files/file_eh.php =================================================================== --- branches/5.2.x/core/units/files/file_eh.php (revision 14674) +++ branches/5.2.x/core/units/files/file_eh.php (revision 14675) @@ -1,107 +1,107 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class FileEventHandler extends kDBEventHandler { /** - * Allows to override standart permission mapping + * Allows to override standard permission mapping * */ function mapPermissions() { parent::mapPermissions(); $permissions = Array( 'OnDownloadFile' => Array('subitem' => 'view'), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Remembers user, who is created file record. Makes file primary if no other files are uploaded. * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(&$event) { parent::OnBeforeItemCreate($event); $object =& $event->getObject(); /* @var $object kDBItem */ $object->SetDBField('CreatedById', $this->Application->RecallVar('user_id')); } /** * Resets primary file mark when more then one file is marked as primary * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(&$event) { parent::OnBeforeItemUpdate($event); $object =& $event->getObject(); /* @var $object kDBItem */ if ( !$object->GetDBField('FileName') ) { $object->SetDBField('FileName', basename($object->GetDBField('FilePath'))); } } /** * Apply any custom changes to list's sql query * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(&$event) { parent::SetCustomQuery($event); $object =& $event->getObject(); /* @var $object kDBList */ - + if (!$this->Application->isAdminUser) { $object->addFilter('active_filter', '%1$s.Status = '.STATUS_ACTIVE); } } /** * Returns file contents associated with item * * @param kEvent $event */ function OnDownloadFile(&$event) { $object =& $event->getObject(); /* @var $object kDBItem */ $file_helper =& $this->Application->recallObject('FileHelper'); /* @var $file_helper FileHelper */ $filename = $object->GetField('FilePath', 'full_path'); $file_helper->DownloadFile($filename); $event->status = kEvent::erSTOP; } } \ No newline at end of file Index: branches/5.2.x/core/units/selectors/selectors_event_handler.php =================================================================== --- branches/5.2.x/core/units/selectors/selectors_event_handler.php (revision 14674) +++ branches/5.2.x/core/units/selectors/selectors_event_handler.php (revision 14675) @@ -1,436 +1,436 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class SelectorsEventHandler extends kDBEventHandler { /** - * Allows to override standart permission mapping + * Allows to override standard permission mapping * */ function mapPermissions() { parent::mapPermissions(); $permissions = Array( 'OnResetToBase' => Array('subitem' => 'add|edit'), 'OnMassResetToBase' => Array('subitem' => 'add|edit'), 'OnOpenStyleEditor' => Array('subitem' => 'add|edit'), 'OnSaveStyle' => Array('subitem' => 'add|edit'), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Occurs before an item has been cloned * Id of newly created item is passed as event' 'id' param * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeClone(&$event) { parent::OnBeforeClone($event); $event->Init($event->Prefix, '-item'); $object =& $event->getObject(); /* @var $object kDBItem */ $title_field = 'SelectorName'; $new_name = $object->GetDBField($title_field); $original_checked = false; $foreign_key = $event->getEventParam('foreign_key'); // in case if whole stylesheet is cloned if ( $foreign_key === false ) { $foreign_key = $object->GetDBField('StylesheetId'); } // in case if selector is copied ifself do { if ( preg_match('/(.*)-([\d]+)/', $new_name, $regs) ) { $new_name = $regs[1] . '-' . ($regs[2] + 1); } elseif ( $original_checked ) { $new_name = $new_name . '-1'; } // 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 ' . $object->TableName . ' WHERE ' . $title_field . ' = ' . $this->Conn->qstr($new_name) . ' AND StylesheetId = ' . $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 ); $object->SetDBField($title_field, $new_name); } /** * Show base styles or block styles * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(&$event) { $object =& $event->getObject(); /* @var $object kDBList */ switch ( $event->Special ) { case 'base': $object->addFilter('type_filter', '%1$s.Type = 1'); break; case 'block': $object->addFilter('type_filter', '%1$s.Type = 2'); break; } } /** * Occurs before updating item * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(&$event) { parent::OnBeforeItemUpdate($event); $this->SerializeSelectorData($event); } /** * Occurs before creating item * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(&$event) { parent::OnBeforeItemCreate($event); - + $this->SerializeSelectorData($event); } /** * Enter description here... * * @param kEvent $event */ function OnAfterItemUpdate(&$event) { $this->UnserializeSelectorData($event); } /** * Occurs after creating item * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemCreate(&$event) { parent::OnAfterItemCreate($event); $this->UnserializeSelectorData($event); } /** * Gets special of main item for linking with sub-item * * @param kEvent $event * @return string */ function getMainSpecial(&$event) { return ''; } /** * Save css-style name & description before opening css editor * * @param kEvent $event */ function OnOpenStyleEditor(&$event) { $this->SaveChanges($event); $event->redirect = false; } /** * Saves Changes to Item * * @param kEvent $event */ function SaveChanges(&$event) { $object =& $event->getObject( Array('skip_autoload' => true) ); /* @var $object kDBItem */ $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); if ( $items_info ) { list($id, $field_values) = each($items_info); - + if ( $id == 0 ) { $parent_id = getArrayValue($field_values, 'ParentId'); if ( $parent_id ) { $object->Load($parent_id); } $object->SetFieldsFromHash($field_values); $object->Create(); $this->Application->SetVar($event->getPrefixSpecial() . '_id', $object->GetID()); } else { $object->Load($id); $object->SetFieldsFromHash($field_values); $object->Update(); } } } /** * Save style changes from style editor * * @param kEvent $event */ function OnSaveStyle(&$event) { $this->SaveChanges($event); $object =& $event->getObject(); $this->Application->SetVar($event->getPrefixSpecial().'_id', $object->GetId() ); $this->finalizePopup($event); } /** * Extract styles * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemLoad(&$event) { parent::OnAfterItemLoad($event); $object =& $event->getObject(); /* @var $object kDBItem */ $selector_data = $object->GetDBField('SelectorData'); if ( $selector_data ) { $selector_data = unserialize($selector_data); $object->SetDBField('SelectorData', $selector_data); } else { $selector_data = Array (); } $this->AddParentProperties($event, $selector_data); } /** * Serialize item before saving to db * * @param kEvent $event */ function SerializeSelectorData(&$event) { $object =& $event->getObject(); /* @var $object kDBItem */ $selector_data = $object->GetDBField('SelectorData'); if ( !$selector_data ) { $selector_data = Array (); } $selector_data = $this->RemoveParentProperties($event, $selector_data); if ( !kUtil::IsSerialized($selector_data) ) { $selector_data = serialize($selector_data); } $object->SetDBField('SelectorData', $selector_data); } /** * Unserialize data back when update was made * * @param kEvent $event */ function UnserializeSelectorData(&$event) { $object =& $event->getObject(); /* @var $object kDBItem */ $selector_data = $object->GetDBField('SelectorData'); if ( !$selector_data ) { $selector_data = Array (); } if ( kUtil::IsSerialized($selector_data) ) { $selector_data = unserialize($selector_data); } $selector_data = $this->AddParentProperties($event, $selector_data); $object->SetDBField('SelectorData', $selector_data); } /** * Populate options based on temporary table :) * * @param kEvent $event */ function OnPrepareBaseStyles(&$event) { $object =& $event->getObject(); $parent_info = $object->getLinkedInfo(); $title_field = $this->Application->getUnitOption($event->Prefix,'TitleField'); $sql = 'SELECT '.$title_field.', '.$object->IDField.' FROM '.$object->TableName.' WHERE Type = 1 AND StylesheetId = '.$parent_info['ParentId'].' ORDER BY '.$title_field; $options = $this->Conn->GetCol($sql,$object->IDField); $object->SetFieldOption('ParentId', 'options', $options); } /** * Remove properties of parent style that match by value from style * * @param kEvent $event */ function RemoveParentProperties(&$event, $selector_data) { $object =& $event->getObject(); /* @var $object kDBItem */ $parent_id = $object->GetDBField('ParentId'); if ( $parent_id ) { $sql = 'SELECT SelectorData FROM ' . $object->TableName . ' WHERE ' . $object->IDField . ' = ' . $parent_id; $base_selector_data = $this->Conn->GetOne($sql); if ( kUtil::IsSerialized($base_selector_data) ) { $base_selector_data = unserialize($base_selector_data); } foreach ($selector_data as $prop_name => $prop_value) { if ( !$prop_value || getArrayValue($base_selector_data, $prop_name) == $prop_value ) { unset($selector_data[$prop_name]); } } } else { foreach ($selector_data as $prop_name => $prop_value) { if ( !$prop_value ) { unset($selector_data[$prop_name]); } } } $object->SetDBField('SelectorData', $selector_data); return $selector_data; } /** * Add back properties from parent style, that match this style property values * * @param kEvent $event */ function AddParentProperties(&$event, $selector_data) { $object =& $event->getObject(); /* @var $object kDBItem */ $parent_id = $object->GetDBField('ParentId'); if ( $parent_id ) { $sql = 'SELECT SelectorData FROM ' . $object->TableName . ' WHERE ' . $object->IDField . ' = ' . $parent_id; $base_selector_data = $this->Conn->GetOne($sql); if ( kUtil::IsSerialized($base_selector_data) ) { $base_selector_data = unserialize($base_selector_data); } $selector_data = kUtil::array_merge_recursive($base_selector_data, $selector_data); $object->SetDBField('SelectorData', $selector_data); return $selector_data; } return Array (); } /** * Reset Style definition to base style -> no customizations * * @param kEvent $event */ function OnResetToBase(&$event) { $object =& $event->getObject(); /* @var $object SelectorsItem */ $object->SetFieldsFromHash( $this->getSubmittedFields($event) ); $object->ResetStyle(); $event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial()); } /** * Resets selected styles properties to values of their base classes * * @param kEvent $event */ function OnMassResetToBase(&$event) { $object =& $event->getObject( Array('skip_autoload' => true) ); /* @var $object SelectorsItem */ $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); if ( $items_info ) { foreach ($items_info as $id => $field_values) { $object->Load($id); $object->ResetStyle(); } } } } \ No newline at end of file Index: branches/5.2.x/core/units/forms/forms/forms_eh.php =================================================================== --- branches/5.2.x/core/units/forms/forms/forms_eh.php (revision 14674) +++ branches/5.2.x/core/units/forms/forms/forms_eh.php (revision 14675) @@ -1,622 +1,622 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class FormsEventHandler extends kDBEventHandler { /** - * Allows to override standart permission mapping + * Allows to override standard permission mapping * */ function mapPermissions() { parent::mapPermissions(); $permissions = Array( // user can view any form on front-end 'OnItemBuild' => Array('self' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); } function OnCreateSubmissionNodes(&$event) { if (defined('IS_INSTALL') && IS_INSTALL) { // skip any processing, because Forms table doesn't exists until install is finished return ; } $forms = $this->getForms(); if (!$forms) { return ; } $form_subsection = Array( 'parent' => 'in-portal:forms', 'icon' => 'form_submission', 'label' => '', 'url' => Array('t' => 'submissions/submissions_list', 'pass' => 'm,form'), 'permissions' => Array('view', 'add', 'edit', 'delete'), 'priority' => 1, 'type' => stTREE, ); $priority = 1; $sections = $this->Application->getUnitOption($event->Prefix, 'Sections'); foreach ($forms as $form_id => $form_name) { $this->Application->Phrases->AddCachedPhrase('form_sub_label_'.$form_id, $form_name); $this->Application->Phrases->AddCachedPhrase('la_description_in-portal:submissions:'.$form_id, $form_name.' Submissions'); $form_subsection['label'] = 'form_sub_label_'.$form_id; $form_subsection['url']['form_id'] = $form_id; $form_subsection['priority'] = $priority++; $sections['in-portal:submissions:'.$form_id] = $form_subsection; } $this->Application->setUnitOption($event->Prefix, 'Sections', $sections); } function getForms() { $cache_key = 'forms[%FormSerial%]'; $forms = $this->Application->getCache($cache_key); if ($forms === false) { $this->Conn->nextQueryCachable = true; $sql = 'SELECT Title, FormId FROM ' . TABLE_PREFIX . 'Forms ORDER BY Title ASC'; $forms = $this->Conn->GetCol($sql, 'FormId'); $this->Application->setCache($cache_key, $forms); } return $forms; } /** * Saves content of temp table into live and * redirects to event' default redirect (normally grid template) * * @param kEvent $event * @return void * @access protected */ protected function OnSave(&$event) { parent::OnSave($event); if ( $event->status == kEvent::erSUCCESS ) { $this->OnCreateFormFields($event); $this->_deleteSectionCache(); } } /** * 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 * @return void * @access protected */ protected function OnMassDelete(&$event) { parent::OnMassDelete($event); if ( $event->status == kEvent::erSUCCESS ) { $this->_deleteSectionCache(); } } function _deleteSectionCache() { $reset_event = new kEvent('adm:OnResetSections'); $this->Application->HandleEvent($reset_event); $this->Application->StoreVar('RefreshStructureTree', 1); } /** * Dynamically fills custom data config * * @param kEvent $event */ function OnCreateFormFields(&$event) { $cur_fields = $this->Conn->Query('DESCRIBE '.TABLE_PREFIX.'FormSubmissions', 'Field'); $cur_fields = array_keys($cur_fields); // keep all fields, that are not created on the fly (includes ones, that are added during customizations) foreach ($cur_fields as $field_index => $field_name) { if (!preg_match('/^fld_[\d]+/', $field_name)) { unset($cur_fields[$field_index]); } } $desired_fields = $this->Conn->GetCol('SELECT CONCAT(\'fld_\', FormFieldId) FROM '.TABLE_PREFIX.'FormFields ORDER BY FormFieldId'); $sql = array(); $fields_to_add = array_diff($desired_fields, $cur_fields); foreach ($fields_to_add as $field) { $field_expression = $field.' Text NULL'; $sql[] = 'ADD COLUMN '.$field_expression; } $fields_to_drop = array_diff($cur_fields, $desired_fields); foreach ($fields_to_drop as $field) { $sql[] = 'DROP COLUMN '.$field; } if ($sql) { $query = 'ALTER TABLE '.TABLE_PREFIX.'FormSubmissions '.implode(', ', $sql); $this->Conn->Query($query); } } /** * Enter description here... * * @param kEvent $event * @return void * @access protected */ protected function OnFormSubmit(&$event) { $object =& $event->getObject(); /* @var $object kDBItem */ $fields = explode(',',$this->Application->GetVar('fields')); $required_fields = explode(',', $this->Application->GetVar('required_fields')); $fields_params = $this->Application->GetVar('fields_params'); $virtual_fields = $this->Application->getUnitOption($event->Prefix, 'VirtualFields'); foreach ($fields as $field) { $virtual_fields[$field] = Array (); if ( in_array($field, $required_fields) ) { $virtual_fields[$field]['required'] = 1; } $params = getArrayValue($fields_params, $field); if ( $params !== false ) { if ( getArrayValue($params, 'Type') == 'email' ) { $virtual_fields[$field]['formatter'] = 'kFormatter'; $virtual_fields[$field]['regexp'] = '/^(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')$/i'; $virtual_fields[$field]['error_msgs'] = Array ('invalid_format' => '!la_invalid_email!'); } if ( getArrayValue($params, 'Type') == 'file' ) { $virtual_fields[$field]['formatter'] = 'kUploadFormatter'; $virtual_fields[$field]['upload_dir'] = '/uploads/sketches/'; } } } $object->SetVirtualFields($virtual_fields); $field_values = $this->getSubmittedFields($event); $checkboxes = explode(',', $this->Application->GetVar('checkbox_fields')); // MailingList,In-Link,In-Newz,In-Bulletin foreach ($checkboxes as $checkbox) { if (isset($field_values[$checkbox])) { $field_values[$checkbox] = 1; } else { $field_values[$checkbox] = '0'; } } $object->SetFieldsFromHash($field_values); if ( $object->Validate() ) { $event->redirect = $this->Application->GetVar('success_template'); $this->Application->EmailEventAdmin($this->Application->GetVar('email_event')); $send_params = Array ( 'to_email' => $field_values[$this->Application->GetVar('email_field')], 'to_name' => $field_values[$this->Application->GetVar('name_field')] ); $this->Application->EmailEventUser($this->Application->GetVar('email_event'), null, $send_params); if ( $field_values['MailingList'] ) { $this->Application->StoreVar('SubscriberEmail', $field_values['Email']); $this->Application->HandleEvent($sub_event, 'u:OnSubscribeUser', Array ('no_unsubscribe' => 1)); } } else { $event->status = kEvent::erFAIL; } } /** * Don't use security image, when form requires login * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(&$event) { parent::OnBeforeItemCreate($event); $this->itemChanged($event); } /** * Don't use security image, when form requires login * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(&$event) { parent::OnBeforeItemUpdate($event); $this->itemChanged($event); } /** * Occurs before item is changed * * @param kEvent $event */ function itemChanged(&$event) { $this->_validatePopSettings($event); $this->_disableSecurityImage($event); $this->_setRequired($event); } /** * Validates POP3 settings (performs test connect) * * @param kEvent $event */ function _validatePopSettings(&$event) { $object =& $event->getObject(); /* @var $object kDBItem */ $modes = Array ('Reply', 'Bounce'); $fields = Array ('Server', 'Port', 'Username', 'Password'); $changed_fields = array_keys( $object->GetChangedFields() ); foreach ($modes as $mode) { $set = true; $changed = false; foreach ($fields as $field) { $value = $object->GetDBField($mode . $field); if (strlen( trim($value) ) == 0) { $set = false; break; } if (!$changed && in_array($mode . $field, $changed_fields)) { $changed = true; } } if ($set && $changed) { // fields are set and at least on of them is changed $connection_info = Array (); foreach ($fields as $field) { $connection_info[ strtolower($field) ] = $object->GetDBField($mode . $field); } $pop3_helper =& $this->Application->makeClass('POP3Helper', Array ($connection_info, 10)); /* @var $pop3_helper POP3Helper */ switch ( $pop3_helper->initMailbox(true) ) { case 'socket': $object->SetError($mode . 'Server', 'connection_failed'); break; case 'login': $object->SetError($mode . 'Username', 'login_failed'); break; case 'list': $object->SetError($mode . 'Server', 'message_listing_failed'); break; } } } } /** * Makes email communication fields required, when form uses email communication * * @param kEvent $event */ function _setRequired(&$event) { $object =& $event->getObject(); /* @var $object kDBItem */ $required = $object->GetDBField('EnableEmailCommunication'); $fields = Array ( 'ReplyFromName', 'ReplyFromEmail', 'ReplyServer', 'ReplyPort', 'ReplyUsername', 'ReplyPassword', ); if ($required && $object->GetDBField('BounceEmail')) { $bounce_fields = Array ('BounceEmail', 'BounceServer', 'BouncePort', 'BounceUsername', 'BouncePassword'); $fields = array_merge($fields, $bounce_fields); } $object->setRequired($fields, $required); } /** * Don't use security image, when form requires login * * @param kEvent $event */ function _disableSecurityImage(&$event) { $object =& $event->getObject(); /* @var $object kDBItem */ if ($object->GetDBField('RequireLogin')) { $object->SetDBField('UseSecurityImage', 0); } } /** * Queries pop3 server about new incoming mail * * @param kEvent $event */ function OnProcessReplies(&$event) { $this->_processMailbox($event, false); } /** * Queries pop3 server about new incoming mail * * @param kEvent $event */ function OnProcessBouncedReplies(&$event) { $this->_processMailbox($event, true); } /** * Queries pop3 server about new incoming mail * * @param kEvent $event */ function _processMailbox(&$event, $bounce_mode = false) { $this->Application->SetVar('client_mode', 1); $id_field = $this->Application->getUnitOption($event->Prefix, 'IDField'); $table_name = $this->Application->getUnitOption($event->Prefix, 'TableName'); $sql = 'SELECT * FROM ' . $table_name . ' WHERE EnableEmailCommunication = 1'; $forms = $this->Conn->Query($sql, $id_field); $mailbox_helper =& $this->Application->recallObject('MailboxHelper'); /* @var $mailbox_helper MailboxHelper */ $field_prefix = $bounce_mode ? 'Bounce' : 'Reply'; foreach ($forms as $form_id => $form_info) { $recipient_email = $bounce_mode ? $form_info['BounceEmail'] : $form_info['ReplyFromEmail']; if (!$recipient_email) { continue; } $mailbox_helper->process( Array ( 'server' => $form_info[$field_prefix . 'Server'], 'port' => $form_info[$field_prefix . 'Port'], 'username' => $form_info[$field_prefix . 'Username'], 'password' => $form_info[$field_prefix . 'Password'] ), Array (&$this, 'isValidRecipient'), Array (&$this, 'processEmail'), Array ( 'recipient_email' => $recipient_email, 'bounce_mode' => $bounce_mode, 'form_info' => $form_info, ) ); } } function isValidRecipient($params) { $mailbox_helper =& $this->Application->recallObject('MailboxHelper'); /* @var $mailbox_helper MailboxHelper */ $recipients = $mailbox_helper->getRecipients(); $recipient_email = $params['recipient_email']; $emails_found = preg_match_all('/((' . REGEX_EMAIL_USER . ')(@' . REGEX_EMAIL_DOMAIN . '))/i', $recipients, $all_emails); if (is_array($all_emails)) { for ($i = 0; $i < $emails_found; $i++) { if ($all_emails[1][$i] == $recipient_email) { // only read messages, that are addresses to submission reply email return true; } } } // If this is a forwarded message - we drop all the other aliases and deliver only to the x-forward to address; if (preg_match('/((' . REGEX_EMAIL_USER . ')(@' . REGEX_EMAIL_DOMAIN . '))/i', $mailbox_helper->headers['x-forward-to'], $get_to_email)) { if ($get_to_email[1] == $recipient_email) { // only read messages, that are addresses to submission reply email return true; } } return false; } function processEmail($params, &$fields_hash) { if ($params['bounce_mode']) { // mark original message as bounced $mailbox_helper =& $this->Application->recallObject('MailboxHelper'); /* @var $mailbox_helper MailboxHelper */ if (!array_key_exists('attachments', $mailbox_helper->parsedMessage)) { // for now only parse bounces based on attachments, skip other bounce types return false; } for ($i = 0; $i < count($mailbox_helper->parsedMessage['attachments']); $i++) { $attachment =& $mailbox_helper->parsedMessage['attachments'][$i]; switch ($attachment['headers']['content-type']) { case 'message/delivery-status': // save as BounceInfo $mime_decode_helper =& $this->Application->recallObject('MimeDecodeHelper'); /* @var $mime_decode_helper MimeDecodeHelper */ $charset = $mailbox_helper->parsedMessage[ $fields_hash['MessageType'] ][0]['charset']; $fields_hash['Message'] = $mime_decode_helper->convertEncoding($charset, $attachment['data']); break; case 'message/rfc822': // undelivered message $fields_hash['Subject'] = $attachment['filename2'] ? $attachment['filename2'] : $attachment['filename']; break; } } } if (!preg_match('/^(.*) #verify(.*)$/', $fields_hash['Subject'], $regs)) { // incorrect subject, no verification code $form_info = $params['form_info']; if ($form_info['ProcessUnmatchedEmails'] && ($fields_hash['FromEmail'] != $params['recipient_email'])) { // it's requested to convert unmatched emails to new submissions $form_id = $form_info['FormId']; $this->Application->SetVar('form_id', $form_id); $sql = 'SELECT ' . $this->Application->getUnitOption('formsubs', 'IDField') . ' FROM ' . $this->Application->getUnitOption('formsubs', 'TableName') . ' WHERE MessageId = ' . $this->Conn->qstr($fields_hash['MessageId']); $found = $this->Conn->GetOne($sql); if ($found) { // don't process same message twice return false; } $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'FormFields WHERE (FormId = ' . $form_info['FormId'] . ') AND (EmailCommunicationRole > 0)'; $form_fields = $this->Conn->Query($sql, 'EmailCommunicationRole'); // what roles are filled from what fields $role_mapping = Array ( SubmissionFormField::COMMUNICATION_ROLE_EMAIL => 'FromEmail', SubmissionFormField::COMMUNICATION_ROLE_NAME => 'FromName', SubmissionFormField::COMMUNICATION_ROLE_SUBJECT => 'Subject', SubmissionFormField::COMMUNICATION_ROLE_BODY => 'Message', ); $submission_fields = Array (); foreach ($role_mapping as $role => $email_field) { if (array_key_exists($role, $form_fields)) { $submission_fields[ 'fld_' . $form_fields[$role]['FormFieldId'] ] = $fields_hash[$email_field]; } } if ($submission_fields) { // remove object, because it's linked to single form upon creation forever $this->Application->removeObject('formsubs.-item'); $form_submission =& $this->Application->recallObject('formsubs.-item', null, Array ('skip_autoload' => true)); /* @var $form_submission kDBItem */ // in case that other non-role mapped fields are required $form_submission->IgnoreValidation = true; $form_submission->SetDBFieldsFromHash($submission_fields); $form_submission->SetDBField('FormId', $form_id); $form_submission->SetDBField('MessageId', $fields_hash['MessageId']); $form_submission->SetDBField('SubmissionTime_date', adodb_mktime()); $form_submission->SetDBField('SubmissionTime_time', adodb_mktime()); $form_submission->SetDBField('ReferrerURL', $this->Application->Phrase('la_Text_Email')); return $form_submission->Create(); } } return false; } $sql = 'SELECT ' . $this->Application->getUnitOption('submission-log', 'IDField') . ' FROM ' . $this->Application->getUnitOption('submission-log', 'TableName') . ' WHERE MessageId = ' . $this->Conn->qstr($fields_hash['MessageId']); $found = $this->Conn->GetOne($sql); if ($found) { // don't process same message twice return false; } $reply_to =& $this->Application->recallObject('submission-log.-reply-to', null, Array ('skip_autoload' => true)); /* @var $reply_to kDBItem */ $reply_to->Load($regs[2], 'VerifyCode'); if (!$reply_to->isLoaded()) { // fake verification code OR feedback, containing submission log was deleted return false; } if ($params['bounce_mode']) { // mark original message as bounced $reply_to->SetDBField('BounceInfo', $fields_hash['Message']); $reply_to->SetDBField('BounceDate_date', TIMENOW); $reply_to->SetDBField('BounceDate_time', TIMENOW); $reply_to->SetDBField('SentStatus', SUBMISSION_LOG_BOUNCE); $reply_to->Update(); return true; } $reply =& $this->Application->recallObject('submission-log.-reply', null, Array ('skip_autoload' => true)); /* @var $reply kDBItem */ $reply->SetDBFieldsFromHash($fields_hash); $reply->SetDBField('ReplyTo', $reply_to->GetID()); $reply->SetDBField('FormSubmissionId', $reply_to->GetDBField('FormSubmissionId')); $reply->SetDBField('ToEmail', $params['recipient_email']); $reply->SetDBField('Subject', $regs[1]); // save subject without verification code $reply->SetDBField('SentStatus', SUBMISSION_LOG_SENT); return $reply->Create(); } } \ No newline at end of file Index: branches/5.2.x/core/units/user_profile/user_profile_eh.php =================================================================== --- branches/5.2.x/core/units/user_profile/user_profile_eh.php (revision 14674) +++ branches/5.2.x/core/units/user_profile/user_profile_eh.php (revision 14675) @@ -1,87 +1,87 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class UserProfileEventHandler extends kDBEventHandler { /** - * Allows to override standart permission mapping + * Allows to override standard permission mapping * */ function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnItemBuild' => Array('subitem' => true), 'OnUpdate' => Array('subitem' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Saves user profile to database * * @param kEvent $event */ function OnUpdate(&$event) { $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); list ($user_id, $field_values) = each($items_info); if ($user_id != $this->Application->RecallVar('user_id')) { // we are not updating own profile return ; } $public_profile_add = Array (); $public_profile_remove = Array (); $profile_mapping = $this->Application->getUnitOption('u', 'UserProfileMapping'); foreach ($field_values as $variable_name => $variable_value) { if (array_key_exists($variable_name, $profile_mapping)) { // old style variable for displaying fields in public profile (named "pp_*") if ($variable_value) { $public_profile_add[] = $profile_mapping[$variable_name]; } else { $public_profile_remove[] = $profile_mapping[$variable_name]; } } else { $this->Application->StorePersistentVar($variable_name, kUtil::unhtmlentities($variable_value)); } } if ($public_profile_add || $public_profile_remove) { $user =& $this->Application->recallObject('u.current'); /* @var $user kDBItem */ // get current value $display_to_public_old = $user->GetDBField('DisplayToPublic'); $display_to_public_new = $display_to_public_old ? explode('|', substr($display_to_public_old, 1, -1)) : Array (); // update value $display_to_public_new = array_diff(array_merge($display_to_public_new, $public_profile_add), $public_profile_remove); $display_to_public_new = array_unique($display_to_public_new); $display_to_public_new = $display_to_public_new ? '|' . implode('|', $display_to_public_new) . '|' : ''; if ($display_to_public_new != $display_to_public_old) { $user->SetDBField('DisplayToPublic', $display_to_public_new); $user->Update(); } } } } \ No newline at end of file Index: branches/5.2.x/core/units/languages/languages_event_handler.php =================================================================== --- branches/5.2.x/core/units/languages/languages_event_handler.php (revision 14674) +++ branches/5.2.x/core/units/languages/languages_event_handler.php (revision 14675) @@ -1,667 +1,667 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class LanguagesEventHandler extends kDBEventHandler { /** - * Allows to override standart permission mapping + * Allows to override standard permission mapping * */ function mapPermissions() { parent::mapPermissions(); $permissions = Array( 'OnChangeLanguage' => Array('self' => true), 'OnSetPrimary' => Array('self' => 'advanced:set_primary|add|edit'), 'OnImportLanguage' => Array('self' => 'advanced:import'), 'OnExportLanguage' => Array('self' => 'advanced:export'), 'OnExportProgress' => Array('self' => 'advanced:export'), 'OnReflectMultiLingualFields' => Array ('self' => 'view'), 'OnSynchronizeLanguages' => Array ('self' => 'edit'), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Checks user permission to execute given $event * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(&$event) { if ($event->Name == 'OnItemBuild') { // check permission without using $event->getSection(), // so first cache rebuild won't lead to "ldefault_Name" field being used return true; } return parent::CheckPermission($event); } /** * Allows to get primary language object * * @param kEvent $event */ function getPassedID(&$event) { if ($event->Special == 'primary') { return $this->Application->GetDefaultLanguageId(); } return parent::getPassedID($event); } /** * [HOOK] Updates table structure on new language adding/removing language * * @param kEvent $event */ function OnReflectMultiLingualFields(&$event) { if ($this->Application->GetVar('ajax') == 'yes') { $event->status = kEvent::erSTOP; } if (is_object($event->MasterEvent)) { if ($event->MasterEvent->status != kEvent::erSUCCESS) { // only rebuild when all fields are validated return ; } if (($event->MasterEvent->Name == 'OnSave') && !$this->Application->GetVar('new_language')) { // only rebuild during new language adding return ; } } $ml_helper =& $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $this->Application->UnitConfigReader->ReReadConfigs(); foreach ($this->Application->UnitConfigReader->configData as $prefix => $config_data) { $ml_helper->createFields($prefix); } $event->SetRedirectParam('action_completed', 1); } /** * Allows to set selected language as primary * * @param kEvent $event */ function OnSetPrimary(&$event) { if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { $event->status = kEvent::erFAIL; return; } $this->StoreSelectedIDs($event); $ids = $this->getSelectedIDs($event); if ($ids) { $id = array_shift($ids); $object =& $event->getObject( Array('skip_autoload' => true) ); /* @var $object LanguagesItem */ $object->Load($id); $object->copyMissingData( $object->setPrimary() ); } } /** * [HOOK] Reset primary status of other languages if we are saving primary language * * @param kEvent $event */ function OnUpdatePrimary(&$event) { if ($event->MasterEvent->status != kEvent::erSUCCESS) { return ; } $object =& $event->getObject( Array('skip_autoload' => true) ); /* @var $object LanguagesItem */ $object->SwitchToLive(); // set primary for each languages, that have this checkbox checked $ids = explode(',', $event->MasterEvent->getEventParam('ids')); foreach ($ids as $id) { $object->Load($id); if ($object->GetDBField('PrimaryLang')) { $object->copyMissingData( $object->setPrimary(true, false) ); } if ($object->GetDBField('AdminInterfaceLang')) { $object->setPrimary(true, true); } } // if no primary language left, then set primary last language (not to load again) from edited list $sql = 'SELECT '.$object->IDField.' FROM '.$object->TableName.' WHERE PrimaryLang = 1'; $primary_language = $this->Conn->GetOne($sql); if (!$primary_language) { $object->setPrimary(false, false); // set primary language } $sql = 'SELECT '.$object->IDField.' FROM '.$object->TableName.' WHERE AdminInterfaceLang = 1'; $primary_language = $this->Conn->GetOne($sql); if (!$primary_language) { $object->setPrimary(false, true); // set admin interface language } } /** * Prefills options with dynamic values * * @param kEvent $event */ function OnAfterConfigRead(&$event) { parent::OnAfterConfigRead($event); $fields = $this->Application->getUnitOption($event->Prefix, 'Fields'); // set dynamic hints for options in date format fields $options = $fields['InputDateFormat']['options']; if ($options) { foreach ($options as $i => $v) { $options[$i] = $v . ' (' . adodb_date($i) . ')'; } $fields['InputDateFormat']['options'] = $options; } $options = $fields['InputTimeFormat']['options']; if ($options) { foreach ($options as $i => $v) { $options[$i] = $v . ' (' . adodb_date($i) . ')'; } $fields['InputTimeFormat']['options'] = $options; } $this->Application->setUnitOption($event->Prefix, 'Fields', $fields); } /** * Occurs before updating item * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(&$event) { $object =& $event->getObject(); /* @var $object kDBItem */ $status_fields = $this->Application->getUnitOption($event->Prefix, 'StatusField'); $status_field = array_shift($status_fields); if ( $object->GetDBField('PrimaryLang') == 1 && $object->GetDBField($status_field) == 0 ) { $object->SetDBField($status_field, 1); } } /** * Shows only enabled languages on front * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(&$event) { $object =& $event->getObject(); /* @var $object kDBList */ if (in_array($event->Special, Array ('enabled', 'selected', 'available'))) { $object->addFilter('enabled_filter', '%1$s.Enabled = ' . STATUS_ACTIVE); } // site domain language picker if ($event->Special == 'selected' || $event->Special == 'available') { $edit_picker_helper =& $this->Application->recallObject('EditPickerHelper'); /* @var $edit_picker_helper EditPickerHelper */ $edit_picker_helper->applyFilter($event, 'Languages'); } // apply domain-based language filtering $languages = $this->Application->siteDomainField('Languages'); if (strlen($languages)) { $languages = explode('|', substr($languages, 1, -1)); $object->addFilter('domain_filter', '%1$s.LanguageId IN (' . implode(',', $languages) . ')'); } } /** * Copy labels from another language * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemCreate(&$event) { parent::OnAfterItemCreate($event); $object =& $event->getObject(); /* @var $object kDBItem */ $src_language = $object->GetDBField('CopyFromLanguage'); if ( $object->GetDBField('CopyLabels') && $src_language ) { $dst_language = $object->GetID(); // 1. schedule data copy after OnSave event is executed $var_name = $event->getPrefixSpecial() . '_copy_data' . $this->Application->GetVar('m_wid'); $pending_actions = $this->Application->RecallVar($var_name, Array ()); if ( $pending_actions ) { $pending_actions = unserialize($pending_actions); } $pending_actions[$src_language] = $dst_language; $this->Application->StoreVar($var_name, serialize($pending_actions)); $object->SetDBField('CopyLabels', 0); } } /** * Saves language from temp table to live * * @param kEvent $event * @return void * @access protected */ protected function OnSave(&$event) { parent::OnSave($event); if ( $event->status != kEvent::erSUCCESS ) { return; } $var_name = $event->getPrefixSpecial() . '_copy_data' . $this->Application->GetVar('m_wid'); $pending_actions = $this->Application->RecallVar($var_name, Array ()); if ( $pending_actions ) { $pending_actions = unserialize($pending_actions); } // create multilingual columns for phrases & email events table first (actual for 6+ language) $ml_helper =& $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $ml_helper->createFields('phrases'); $ml_helper->createFields('emailevents'); foreach ($pending_actions as $src_language => $dst_language) { // phrases import $sql = 'UPDATE ' . $this->Application->getUnitOption('phrases', 'TableName') . ' SET l' . $dst_language . '_Translation = l' . $src_language . '_Translation'; $this->Conn->Query($sql); // events import $sql = 'UPDATE ' . $this->Application->getUnitOption('emailevents', 'TableName') . ' SET l' . $dst_language . '_Subject = l' . $src_language . '_Subject, l' . $dst_language . '_Body = l' . $src_language . '_Body'; $this->Conn->Query($sql); } $this->Application->RemoveVar($var_name); $event->CallSubEvent('OnReflectMultiLingualFields'); $event->CallSubEvent('OnUpdatePrimary'); } /** * Prepare temp tables for creating new item * but does not create it. Actual create is * done in OnPreSaveCreated * * @param kEvent $event * @return void * @access protected */ protected function OnPreCreate(&$event) { parent::OnPreCreate($event); $object =& $event->getObject(); /* @var $object kDBItem */ $object->SetDBField('CopyLabels', 1); $sql = 'SELECT ' . $object->IDField . ' FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . ' WHERE PrimaryLang = 1'; $primary_lang_id = $this->Conn->GetOne($sql); $object->SetDBField('CopyFromLanguage', $primary_lang_id); $object->SetDBField('SynchronizationModes', Language::SYNCHRONIZE_DEFAULT); } /** * Sets new language mark * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeDeleteFromLive(&$event) { parent::OnBeforeDeleteFromLive($event); $id_field = $this->Application->getUnitOption($event->Prefix, 'IDField'); $sql = 'SELECT ' . $id_field . ' FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . ' WHERE ' . $id_field . ' = ' . $event->getEventParam('id'); $id = $this->Conn->GetOne($sql); if ( !$id ) { $this->Application->SetVar('new_language', 1); } } function OnChangeLanguage(&$event) { $language_id = $this->Application->GetVar('language'); if ($this->Application->isAdmin) { // admin data only $this->Application->SetVar('m_lang', $language_id); // set new language for this session (admin interface only) $this->Application->Session->SetField('Language', $language_id); // remember last user language in administrative console if ($this->Application->RecallVar('user_id') == USER_ROOT) { $this->Application->StorePersistentVar('AdminLanguage', $language_id); } else { $object =& $this->Application->recallObject('u.current'); /* @var $object kDBItem */ $object->SetDBField('AdminLanguage', $language_id); $object->Update(); } // without this language change in admin will cause erase of last remembered tree section $this->Application->SetVar('skip_last_template', 1); } else { // changing language on Front-End $this->Application->SetVar('m_lang', $language_id); if (MOD_REWRITE) { $mod_rewrite_helper =& $this->Application->recallObject('ModRewriteHelper'); /* @var $mod_rewrite_helper kModRewriteHelper */ $mod_rewrite_helper->removePages(); } } } /** * Parse language XML file into temp tables and redirect to progress bar screen * * @param kEvent $event */ function OnImportLanguage(&$event) { if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { $event->status = kEvent::erFAIL; return; } $items_info = $this->Application->GetVar('phrases_import'); if ($items_info) { list ($id, $field_values) = each($items_info); $object =& $this->Application->recallObject('phrases.import', 'phrases', Array('skip_autoload' => true)); /* @var $object kDBItem */ $object->setID($id); $object->SetFieldsFromHash($field_values); if (!$object->Validate()) { $event->status = kEvent::erFAIL; return ; } $filename = $object->GetField('LangFile', 'full_path'); if (!filesize($filename)) { $object->SetError('LangFile', 'la_empty_file', 'la_EmptyFile'); $event->status = kEvent::erFAIL; } $language_import_helper =& $this->Application->recallObject('LanguageImportHelper'); /* @var $language_import_helper LanguageImportHelper */ $language_import_helper->performImport( $filename, $object->GetDBField('PhraseType'), $object->GetDBField('Module'), $object->GetDBField('ImportOverwrite') ? LANG_OVERWRITE_EXISTING : LANG_SKIP_EXISTING ); // delete uploaded language pack after import is finished unlink($filename); $event->SetRedirectParam('opener', 'u'); } } /** * Stores ids of selected languages and redirects to export language step 1 * * @param kEvent $event */ function OnExportLanguage(&$event) { if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { $event->status = kEvent::erFAIL; return; } $this->Application->setUnitOption('phrases','AutoLoad',false); $this->StoreSelectedIDs($event); $this->Application->StoreVar('export_language_ids', implode(',', $this->getSelectedIDs($event)) ); $event->setRedirectParams( Array('phrases.export_event' => 'OnNew', 'pass' => 'all,phrases.export') ); } /** * Saves selected languages to xml file passed * * @param kEvent $event */ function OnExportProgress(&$event) { $items_info = $this->Application->GetVar('phrases_export'); if ($items_info) { list($id, $field_values) = each($items_info); $object =& $this->Application->recallObject('phrases.export', null, Array('skip_autoload' => true)); /* @var $object kDBItem */ $object->setID($id); $object->SetFieldsFromHash($field_values); if (!$object->Validate()) { $event->status = kEvent::erFAIL; return ; } $file_helper =& $this->Application->recallObject('FileHelper'); /* @var $file_helper FileHelper */ $file_helper->CheckFolder(EXPORT_PATH); if (!is_writable(EXPORT_PATH)) { $event->status = kEvent::erFAIL; $object->SetError('LangFile', 'write_error', 'la_ExportFolderNotWritable'); return ; } if ( substr($field_values['LangFile'], -5) != '.lang') { $field_values['LangFile'] .= '.lang'; } $filename = EXPORT_PATH . '/' . $field_values['LangFile']; $language_import_helper =& $this->Application->recallObject('LanguageImportHelper'); /* @var $language_import_helper LanguageImportHelper */ if ($object->GetDBField('DoNotEncode')) { $language_import_helper->setExportEncoding('plain'); } $language_import_helper->setExportLimits($field_values['ExportPhrases'], $field_values['ExportEmailEvents']); $lang_ids = explode(',', $this->Application->RecallVar('export_language_ids') ); $language_import_helper->performExport($filename, $field_values['PhraseType'], $lang_ids, $field_values['Module']); } $event->redirect = 'regional/languages_export_step2'; $event->SetRedirectParam('export_file', $field_values['LangFile']); } /** * Returns to previous template in opener stack * * @param kEvent $event */ function OnGoBack(&$event) { $event->SetRedirectParam('opener', 'u'); } function OnScheduleTopFrameReload(&$event) { $this->Application->StoreVar('RefreshTopFrame',1); } /** * Do now allow deleting current language * * @param kEvent $event */ function OnBeforeItemDelete(&$event) { $object =& $event->getObject(); /* @var $object kDBItem */ if ( $object->GetDBField('PrimaryLang') || $object->GetDBField('AdminInterfaceLang') || $object->GetID() == $this->Application->GetVar('m_lang') ) { $event->status = kEvent::erFAIL; } } /** * Deletes phrases and email events on given language * * @param kEvent $event */ function OnAfterItemDelete(&$event) { parent::OnAfterItemDelete($event); $object =& $event->getObject(); /* @var $object kDBItem */ // clean Events table $fields_hash = Array ( 'l' . $object->GetID() . '_Subject' => NULL, 'l' . $object->GetID() . '_Body' => NULL, ); $this->Conn->doUpdate($fields_hash, $this->Application->getUnitOption('emailevents', 'TableName'), 1); // clean Phrases table $fields_hash = Array ( 'l' . $object->GetID() . '_Translation' => NULL, ); $this->Conn->doUpdate($fields_hash, $this->Application->getUnitOption('phrases', 'TableName'), 1); } /** * Copy missing phrases across all system languages (starting from primary) * * @param kEvent $event * @return void * @access protected */ protected function OnSynchronizeLanguages(&$event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return; } $source_languages = $target_languages = Array (); // get language list with primary language first $sql = 'SELECT SynchronizationModes, LanguageId FROM ' . TABLE_PREFIX . 'Language WHERE SynchronizationModes <> "" ORDER BY PrimaryLang DESC'; $languages = $this->Conn->GetCol($sql, 'LanguageId'); foreach ($languages as $language_id => $synchronization_modes) { $synchronization_modes = explode('|', substr($synchronization_modes, 1, -1)); if ( in_array(Language::SYNCHRONIZE_TO_OTHERS, $synchronization_modes) ) { $source_languages[] = $language_id; } if ( in_array(Language::SYNCHRONIZE_FROM_OTHERS, $synchronization_modes) ) { $target_languages[] = $language_id; } } foreach ($source_languages as $source_id) { foreach ($target_languages as $target_id) { if ( $source_id == $target_id ) { continue; } $sql = 'UPDATE ' . TABLE_PREFIX . 'Phrase SET l' . $target_id . '_Translation = l' . $source_id . '_Translation WHERE COALESCE(l' . $target_id . '_Translation, "") = "" AND COALESCE(l' . $source_id . '_Translation, "") <> ""'; $this->Conn->Query($sql); } } } } \ No newline at end of file Index: branches/5.2.x/core/units/permissions/permissions_event_handler.php =================================================================== --- branches/5.2.x/core/units/permissions/permissions_event_handler.php (revision 14674) +++ branches/5.2.x/core/units/permissions/permissions_event_handler.php (revision 14675) @@ -1,257 +1,257 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class PermissionsEventHandler extends kDBEventHandler { /** - * Allows to override standart permission mapping + * Allows to override standard permission mapping * */ function mapPermissions() { parent::mapPermissions(); $permissions = Array( 'OnGroupSavePermissions' => Array('subitem' => 'advanced:manage_permissions'), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Save category permissions * * @param kEvent $event */ function OnCategorySavePermissions(&$event) { $group_id = $this->Application->GetVar('current_group_id'); $category_id = $this->Application->GetVar('c_id'); $permissions = $this->Application->GetVar($event->getPrefixSpecial(true)); if (isset($permissions[$group_id])) { $permissions = $permissions[$group_id]; $object =& $event->getObject( Array('skip_autoload' => true) ); $permissions_helper =& $this->Application->recallObject('PermissionsHelper'); /* @var $permissions_helper kPermissionsHelper */ $permissions_helper->LoadPermissions($group_id, $category_id, 0, 'c'); // format: <perm_name>['inherited'] || <perm_name>['value'] $delete_ids = Array(); $create_sql = Array(); $update_sql = Array(); $create_mask = '(%s,%s,'.$group_id.',%s,0,'.$category_id.')'; $new_id = (int)$this->Conn->GetOne('SELECT MIN('.$object->IDField.') FROM '.$object->TableName); if($new_id > 0) $new_id = 0; --$new_id; foreach ($permissions as $perm_name => $perm_data) { $inherited = $perm_data['inherited']; $perm_value = isset($perm_data['value']) ? $perm_data['value'] : false; $perm_id = $permissions_helper->getPermissionID($perm_name); if ($inherited && ($perm_id != 0)) { // permission become inherited (+ direct value was set before) => DELETE $delete_ids[] = $permissions_helper->getPermissionID($perm_name); } if (!$inherited) { // not inherited if (($perm_id != 0) && ($perm_value != $permissions_helper->getPermissionValue($perm_name))) { // record was found in db & new value differs from old one => UPDATE $update_sql[$perm_id] = ' UPDATE '.$object->TableName.' SET PermissionValue = '.$perm_value.' WHERE (PermissionId = '.$perm_id.')'; } if ($perm_id == 0) { // not found in db, but set directly => INSERT $create_sql[] = sprintf($create_mask, $new_id--, $this->Conn->qstr($perm_name), $this->Conn->qstr($perm_value)); } } // permission state was not changed in all other cases } $this->UpdatePermissions($event, $create_sql, $update_sql, $delete_ids); } $event->MasterEvent->SetRedirectParam('item_prefix', $this->Application->GetVar('item_prefix')); $event->MasterEvent->SetRedirectParam('group_id', $this->Application->GetVar('group_id')); } /** * Saves permissions while editing group * * @param kEvent $event * * @return void * @access protected */ protected function OnGroupSavePermissions(&$event) { if ( !$this->Application->CheckPermission('in-portal:user_groups.advanced:manage_permissions', 1) ) { // no permission to save permissions return ; } $permissions = $this->Application->GetVar($event->getPrefixSpecial(true)); if ( !$permissions ) { return ; } $object =& $event->getObject( Array ('skip_autoload' => true) ); /* @var $object kDBItem */ $group_id = $this->Application->GetVar('g_id'); $permissions_helper =& $this->Application->recallObject('PermissionsHelper'); /* @var $permissions_helper kPermissionsHelper */ $permissions_helper->LoadPermissions($group_id, 0, 1, 'g'); $delete_ids = $create_sql = Array (); $create_mask = '(%s,%s,' . $group_id . ',%s,1,0)'; $new_id = (int)$this->Conn->GetOne('SELECT MIN(' . $object->IDField . ') FROM ' . $object->TableName); if ( $new_id > 0 ) { $new_id = 0; } --$new_id; $sections_helper =& $this->Application->recallObject('SectionsHelper'); /* @var $sections_helper kSectionsHelper */ foreach ($permissions as $section_name => $section_permissions) { $section_data =& $sections_helper->getSectionData($section_name); if ( $section_data && isset($section_data['perm_prefix']) ) { // using permission from other prefix $section_name = $this->Application->getUnitOption($section_data['perm_prefix'] . '.main', 'PermSection'); } foreach ($section_permissions as $perm_name => $perm_value) { if ( !$permissions_helper->isOldPermission($section_name, $perm_name) ) { $perm_name = $section_name . '.' . $perm_name; } $db_perm_value = $permissions_helper->getPermissionValue($perm_name); if ( $db_perm_value == 1 && $perm_value == 0 ) { // permission was disabled => delete it's record $delete_ids[] = $permissions_helper->getPermissionID($perm_name); } elseif ( $db_perm_value == 0 && $perm_value == 1 ) { // permission was enabled => created it's record $create_sql[$perm_name] = sprintf($create_mask, $new_id--, $this->Conn->qstr($perm_name), $this->Conn->qstr($perm_value)); } // permission state was not changed in all other cases } } $this->UpdatePermissions($event, $create_sql, Array (), $delete_ids); if ( $this->Application->GetVar('advanced_save') == 1 ) { // advanced permission popup [save button] $this->finalizePopup($event); // $event->redirect = 'incs/just_close'; } elseif ( $this->Application->GetVar('section_name') != '' ) { // save simple permissions before opening advanced permission popup $event->redirect = false; } } /** * Apply modification sqls to permissions table * * @param kEvent $event * @param Array $create_sql * @param Array $update_sql * @param Array $delete_ids */ function UpdatePermissions(&$event, $create_sql, $update_sql, $delete_ids) { $object =& $event->getObject(); /* @var $object kDBItem */ if ($delete_ids) { $action = ChangeLog::DELETE; $object->Load($delete_ids[count($delete_ids) - 1]); $delete_sql = ' DELETE FROM '.$object->TableName.' WHERE '.$object->IDField.' IN ('.implode(',', $delete_ids).')'; $this->Conn->Query($delete_sql); } if ($create_sql) { $create_sql = ' INSERT INTO '.$object->TableName.' VALUES '.implode(',', $create_sql); $this->Conn->Query($create_sql); $sql = 'SELECT MIN(' . $object->IDField . ') FROM ' . $object->TableName; $id = $this->Conn->GetOne($sql); $action = ChangeLog::CREATE; $object->Load($id); } if ($update_sql) { foreach ($update_sql as $id => $sql) { $this->Conn->Query($sql); } $action = ChangeLog::UPDATE; $object->Load($id); $object->SetDBField('PermissionValue', $object->GetDBField('PermissionValue') ? 0 : 1); } if ($delete_ids || $create_sql || $update_sql) { $object->setModifiedFlag($action); if ($event->Name == 'OnCategorySavePermissions') { $this->Application->StoreVar('PermCache_UpdateRequired', 1); } } } /** * Don't delete permissions from live table in case of new category creation. * Called as much times as permission count for categories set, so don't * perform any sql queries here! * * @param kEvent $event */ function OnBeforeDeleteFromLive(&$event) { if ( $event->Prefix == 'c-perm' ) { // only when saving category permissions, not group permissions $foreign_keys = $event->getEventParam('foreign_key'); if ( (count($foreign_keys) == 1) && ($foreign_keys[0] == 0) ) { // parent item has zero id $temp_object =& $this->Application->recallObject('c'); /* @var $temp_object CategoriesItem */ - + if ( $temp_object->isLoaded() ) { // category with id = 0 found in temp table $event->status = kEvent::erFAIL; } } } } } \ No newline at end of file Index: branches/5.2.x/core/units/email_events/email_events_event_handler.php =================================================================== --- branches/5.2.x/core/units/email_events/email_events_event_handler.php (revision 14674) +++ branches/5.2.x/core/units/email_events/email_events_event_handler.php (revision 14675) @@ -1,1136 +1,1136 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class EmailEventsEventsHandler extends kDBEventHandler { /** - * Allows to override standart permission mapping + * Allows to override standard permission mapping * */ function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnFrontOnly' => Array ('self' => 'edit'), 'OnSaveSelected' => Array ('self' => 'view'), 'OnProcessEmailQueue' => Array ('self' => 'add|edit'), 'OnSuggestAddress' => Array ('self' => 'add|edit'), // events only for developers 'OnPreCreate' => Array ('self' => 'debug'), 'OnDelete' => Array ('self' => 'debug'), 'OnDeleteAll' => Array ('self' => 'debug'), 'OnMassDelete' => Array ('self' => 'debug'), 'OnMassApprove' => Array ('self' => 'debug'), 'OnMassDecline' => Array ('self' => 'debug'), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Changes permission section to one from REQUEST, not from config * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(&$event) { $module = $this->Application->GetVar('module'); if (strlen($module) > 0) { // checking permission when lising module email events in separate section $module = explode(':', $module, 2); if (count($module) == 1) { $main_prefix = $this->Application->findModule('Name', $module[0], 'Var'); } else { $exceptions = Array('Category' => 'c', 'Users' => 'u'); $main_prefix = $exceptions[ $module[1] ]; } $section = $this->Application->getUnitOption($main_prefix.'.email', 'PermSection'); $event->setEventParam('PermSection', $section); } // checking permission when listing all email events when editing language return parent::CheckPermission($event); } /** * Apply any custom changes to list's sql query * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(&$event) { $object =& $event->getObject(); /* @var $object kDBList */ if ($event->Special == 'module') { $module = $this->Application->GetVar('module'); $object->addFilter('module_filter', '%1$s.Module = '.$this->Conn->qstr($module)); } if (!$event->Special && !$this->Application->isDebugMode()) { // no special $object->addFilter('enabled_filter', '%1$s.Enabled <> ' . STATUS_DISABLED); } } /** * Set default headers * * @param kEvent $event * @return void * @access protected */ protected function OnPreCreate(&$event) { parent::OnPreCreate($event); $object =& $event->getObject(); /* @var $object kDBItem */ $object->SetDBField('Headers', $this->Application->ConfigValue('Smtp_DefaultHeaders')); } /** * Sets status Front-End Only to selected email events * * @param kEvent $event */ function OnFrontOnly(&$event) { if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { $event->status = kEvent::erFAIL; return ; } $ids = implode(',', $this->StoreSelectedIDs($event)); $table_name = $this->Application->getUnitOption($event->Prefix, 'TableName'); $sql = 'UPDATE '.$table_name.' SET FrontEndOnly = 1 WHERE EventId IN ('.$ids.')'; $this->Conn->Query($sql); $this->clearSelectedIDs($event); } /** * Sets selected user to email events selected * * @param kEvent $event */ function OnSelectUser(&$event) { if ($event->Special != 'module') { parent::OnSelectUser($event); return ; } if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { $event->status = kEvent::erFAIL; return ; } $items_info = $this->Application->GetVar('u'); if ($items_info) { $user_id = array_shift( array_keys($items_info) ); $selected_ids = $this->getSelectedIDs($event, true); $ids = $this->Application->RecallVar($event->getPrefixSpecial().'_selected_ids'); $id_field = $this->Application->getUnitOption($event->Prefix, 'IDField'); $table_name = $this->Application->getUnitOption($event->Prefix, 'TableName'); $sql = 'UPDATE '.$table_name.' SET '.$this->Application->RecallVar('dst_field').' = '.$user_id.' WHERE '.$id_field.' IN ('.$ids.')'; $this->Conn->Query($sql); } $this->finalizePopup($event); } /** * Saves selected ids to session * * @param kEvent $event */ function OnSaveSelected(&$event) { $this->StoreSelectedIDs($event); } /** * Returns email event object based on given kEvent object * * @param kEvent $event * @return kDBItem */ function &_getEmailEvent(&$event) { $false = false; $name = $event->getEventParam('EmailEventName'); $type = $event->getEventParam('EmailEventType'); $object =& $event->getObject( Array('skip_autoload' => true) ); /* @var $object kDBItem */ if (!$object->isLoaded() || ($object->GetDBField('Event') != $name || $object->GetDBField('Type') != $type)) { // get event parameters by name & type $load_keys = Array ('Event' => $name, 'Type' => $type); $object->Load($load_keys); if (!$object->isLoaded() || ($object->GetDBField('Enabled') == STATUS_DISABLED)) { // event record not found OR is disabled return $false; } if ($object->GetDBField('FrontEndOnly') && $this->Application->isAdmin) { return $false; } } return $object; } /** * Processes email sender * * @param kEvent $event * @param Array $direct_params */ function _processSender(&$event, $direct_params = Array ()) { $this->Application->removeObject('u.email-from'); $object =& $this->_getEmailEvent($event); /* @var $object kDBItem */ $email = $name = ''; // set defaults from event if ($object->GetDBField('CustomSender')) { $address = $object->GetDBField('SenderAddress'); $address_type = $object->GetDBField('SenderAddressType'); switch ($address_type) { case EmailEvent::ADDRESS_TYPE_EMAIL: $email = $address; break; case EmailEvent::ADDRESS_TYPE_USER: $sql = 'SELECT FirstName, LastName, Email, PortalUserId FROM ' . TABLE_PREFIX . 'PortalUser WHERE Login = ' . $this->Conn->qstr($address); $user_info = $this->Conn->GetRow($sql); if ($user_info) { // user still exists $email = $user_info['Email']; $name = trim($user_info['FirstName'] . ' ' . $user_info['LastName']); $user =& $this->Application->recallObject('u.email-from', null, Array('skip_autoload' => true)); /* @var $user UsersItem */ $user->Load($user_info['PortalUserId']); } break; } if ($object->GetDBField('SenderName')) { $name = $object->GetDBField('SenderName'); } } // update with custom data given during event execution if (array_key_exists('from_email', $direct_params)) { $email = $direct_params['from_email']; } if (array_key_exists('from_name', $direct_params)) { $name = $direct_params['from_name']; } // still nothing, set defaults if (!$email) { $email = $this->Application->ConfigValue('Smtp_AdminMailFrom'); } if (!$name) { $name = strip_tags( $this->Application->ConfigValue('Site_Name') ); } $esender =& $this->Application->recallObject('EmailSender'); /* @var $esender kEmailSendingHelper */ $esender->SetFrom($email, $name); return Array ($email, $name); } /** * Processes email recipients * * @param kEvent $event * @param Array $direct_params */ function _processRecipients(&$event, $direct_params = Array ()) { $this->Application->removeObject('u.email-to'); $object =& $this->_getEmailEvent($event); /* @var $object kDBItem */ $to_email = $to_name = ''; $all_recipients = Array (); $recipients_xml = $object->GetDBField('Recipients'); if ($recipients_xml) { $minput_helper =& $this->Application->recallObject('MInputHelper'); /* @var $minput_helper MInputHelper */ // group recipients by type $records = $minput_helper->parseMInputXML($recipients_xml); foreach ($records as $record) { $recipient_type = $record['RecipientType']; if (!array_key_exists($recipient_type, $all_recipients)) { $all_recipients[$recipient_type] = Array (); } $all_recipients[$recipient_type][] = $record; } } if (!array_key_exists(EmailEvent::RECIPIENT_TYPE_TO, $all_recipients)) { $all_recipients[EmailEvent::RECIPIENT_TYPE_TO] = Array (); } // remove all "To" recipients, when not allowed $overwrite_to_email = array_key_exists('overwrite_to_email', $direct_params) ? $direct_params['overwrite_to_email'] : false; if (!$object->GetDBField('CustomRecipient') || $overwrite_to_email) { $all_recipients[EmailEvent::RECIPIENT_TYPE_TO] = Array (); } // update with custom data given during event execution (user_id) $to_user_id = $event->getEventParam('EmailEventToUserId'); if ($to_user_id > 0) { $sql = 'SELECT FirstName, LastName, Email FROM ' . TABLE_PREFIX . 'PortalUser WHERE PortalUserId = ' . $to_user_id; $user_info = $this->Conn->GetRow($sql); if ($user_info) { $add_recipient = Array ( 'RecipientAddressType' => EmailEvent::ADDRESS_TYPE_EMAIL, 'RecipientAddress' => $user_info['Email'], 'RecipientName' => trim($user_info['FirstName'] . ' ' . $user_info['LastName']), ); array_unshift($all_recipients[EmailEvent::RECIPIENT_TYPE_TO], $add_recipient); $user =& $this->Application->recallObject('u.email-to', null, Array('skip_autoload' => true)); /* @var $user UsersItem */ $user->Load($to_user_id); } } elseif (is_numeric($to_user_id)) { // recipient is system user with negative ID (root, guest, etc.) -> send to admin array_unshift($all_recipients[EmailEvent::RECIPIENT_TYPE_TO], $this->_getDefaultRepipient()); } // update with custom data given during event execution (email + name) $add_recipient = Array (); if (array_key_exists('to_email', $direct_params)) { $add_recipient['RecipientName'] = ''; $add_recipient['RecipientAddressType'] = EmailEvent::ADDRESS_TYPE_EMAIL; $add_recipient['RecipientAddress'] = $direct_params['to_email']; } if (array_key_exists('to_name', $direct_params)) { $add_recipient['RecipientName'] = $direct_params['to_name']; } if ($add_recipient) { array_unshift($all_recipients[EmailEvent::RECIPIENT_TYPE_TO], $add_recipient); } if (($object->GetDBField('Type') == EmailEvent::EVENT_TYPE_ADMIN) && !$all_recipients[EmailEvent::RECIPIENT_TYPE_TO]) { // admin email event without direct recipient -> send to admin array_unshift($all_recipients[EmailEvent::RECIPIENT_TYPE_TO], $this->_getDefaultRepipient()); } $esender =& $this->Application->recallObject('EmailSender'); /* @var $esender kEmailSendingHelper */ $header_mapping = Array ( EmailEvent::RECIPIENT_TYPE_TO => 'To', EmailEvent::RECIPIENT_TYPE_CC => 'Cc', EmailEvent::RECIPIENT_TYPE_BCC => 'Bcc', ); $default_email = $this->Application->ConfigValue('Smtp_AdminMailFrom'); foreach ($all_recipients as $recipient_type => $recipients) { // add recipients to email $pairs = Array (); foreach ($recipients as $recipient) { $address = $recipient['RecipientAddress']; $address_type = $recipient['RecipientAddressType']; $repipient_name = $recipient['RecipientName']; switch ($address_type) { case EmailEvent::ADDRESS_TYPE_EMAIL: $pairs[] = Array ('email' => $address, 'name' => $repipient_name); break; case EmailEvent::ADDRESS_TYPE_USER: $sql = 'SELECT FirstName, LastName, Email FROM ' . TABLE_PREFIX . 'PortalUser WHERE Login = ' . $this->Conn->qstr($address); $user_info = $this->Conn->GetRow($sql); if ($user_info) { // user still exists $name = trim($user_info['FirstName'] . ' ' . $user_info['LastName']); $pairs[] = Array ( 'email' => $user_info['Email'], 'name' => $name ? $name : $repipient_name, ); } break; case EmailEvent::ADDRESS_TYPE_GROUP: $sql = 'SELECT u.FirstName, u.LastName, u.Email FROM ' . TABLE_PREFIX . 'PortalGroup g JOIN ' . TABLE_PREFIX . 'UserGroup ug ON ug.GroupId = g.GroupId JOIN ' . TABLE_PREFIX . 'PortalUser u ON u.PortalUserId = ug.PortalUserId WHERE g.Name = ' . $this->Conn->qstr($address); $users = $this->Conn->Query($sql); foreach ($users as $user) { $name = trim($user_info['FirstName'] . ' ' . $user_info['LastName']); $pairs[] = Array ( 'email' => $user_info['Email'], 'name' => $name ? $name : $repipient_name, ); } break; } } if (!$pairs) { continue; } if ($recipient_type == EmailEvent::RECIPIENT_TYPE_TO) { $to_email = $pairs[0]['email'] ? $pairs[0]['email'] : $default_email; $to_name = $pairs[0]['name'] ? $pairs[0]['name'] : $to_email; } $header_name = $header_mapping[$recipient_type]; foreach ($pairs as $pair) { $email = $pair['email'] ? $pair['email'] : $default_email; $name = $pair['name'] ? $pair['name'] : $email; $esender->AddRecipient($header_name, $email, $name); } } return Array ($to_email, $to_name); } /** * This is default recipient, when we can't determine actual one * * @return Array */ function _getDefaultRepipient() { return Array ( 'RecipientName' => $this->Application->ConfigValue('Smtp_AdminMailFrom'), 'RecipientAddressType' => EmailEvent::ADDRESS_TYPE_EMAIL, 'RecipientAddress' => $this->Application->ConfigValue('Smtp_AdminMailFrom'), ); } /** * Returns email event message by ID (headers & body in one piece) * * @param kEvent $event * @param int $language_id */ function _getMessageBody(&$event, $language_id = null) { if (!isset($language_id)) { $language_id = $this->Application->GetVar('m_lang'); } $object =& $this->_getEmailEvent($event); // 1. get message body $message_body = $this->_formMessageBody($object, $language_id, $object->GetDBField('MessageType')); // 2. replace tags if needed $default_replacement_tags = Array ( '<inp:touser _Field="password"' => '<inp2:u_Field name="Password_plain"', '<inp:touser _Field="UserName"' => '<inp2:u_Field name="Login"', '<inp:touser _Field' => '<inp2:u_Field name', ); $replacement_tags = $object->GetDBField('ReplacementTags'); $replacement_tags = $replacement_tags ? unserialize($replacement_tags) : Array (); $replacement_tags = array_merge($default_replacement_tags, $replacement_tags); foreach ($replacement_tags as $replace_from => $replace_to) { $message_body = str_replace($replace_from, $replace_to, $message_body); } return $message_body; } /** * Prepare email message body * * @param kDBItem $object * @param int $language_id * @return string */ function _formMessageBody(&$object, $language_id) { $default_language_id = $this->Application->GetDefaultLanguageId(); $fields_hash = Array ( 'Headers' => $object->GetDBField('Headers'), ); // prepare subject $subject = $object->GetDBField('l' . $language_id . '_Subject'); if (!$subject) { $subject = $object->GetDBField('l' . $default_language_id . '_Subject'); } $fields_hash['Subject'] = $subject; // prepare body $body = $object->GetDBField('l' . $language_id . '_Body'); if (!$body) { $body = $object->GetDBField('l' . $default_language_id . '_Body'); } $fields_hash['Body'] = $body; $email_message_helper =& $this->Application->recallObject('EmailMessageHelper'); /* @var $email_message_helper EmailMessageHelper */ $ret = $email_message_helper->buildTemplate($fields_hash); // add footer $footer = $this->_getFooter($language_id, $object->GetDBField('MessageType')); if ($ret && $footer) { $ret .= "\r\n" . $footer; } return $ret; } /** * Returns email footer * * @param int $language_id * @param string $message_type * @return string */ function _getFooter($language_id, $message_type) { static $footer = null; if (!isset($footer)) { $default_language_id = $this->Application->GetDefaultLanguageId(); $sql = 'SELECT l' . $language_id . '_Body, l' . $default_language_id . '_Body FROM ' . $this->Application->getUnitOption('emailevents', 'TableName') . ' em WHERE Event = "COMMON.FOOTER"'; $footer_data = $this->Conn->GetRow($sql); $footer = $footer_data['l' . $language_id . '_Body']; if (!$footer) { $footer = $footer_data['l' . $default_language_id . '_Body']; } if ($message_type == 'text') { $esender =& $this->Application->recallObject('EmailSender'); /* @var $esender kEmailSendingHelper */ $footer = $esender->ConvertToText($footer); } } return $footer; } /** * Parse message template and return headers (as array) and message body part * * @param string $message * @param Array $direct_params * @return Array */ function ParseMessageBody($message, $direct_params = Array ()) { $message_language = $this->_getSendLanguage($direct_params); $this->_changeLanguage($message_language); $direct_params['message_text'] = isset($direct_params['message']) ? $direct_params['message'] : ''; // parameter alias // 1. parse template $this->Application->InitParser(); $parser_params = $this->Application->Parser->Params; // backup parser params $this->Application->Parser->SetParams( array_merge($parser_params, $direct_params) ); $message = implode('&|&', explode("\n\n", $message, 2)); // preserves double \n in case when tag is located in subject field $message = $this->Application->Parser->Parse($message, 'email_template', 0); $this->Application->Parser->SetParams($parser_params); // restore parser params // 2. replace line endings, that are send with data submitted via request $message = str_replace("\r\n", "\n", $message); // possible case $message = str_replace("\r", "\n", $message); // impossible case, but just in case replace this too // 3. separate headers from body $message_headers = Array (); list($headers, $message_body) = explode('&|&', $message, 2); $category_helper =& $this->Application->recallObject('CategoryHelper'); /* @var $category_helper CategoryHelper */ $message_body = $category_helper->replacePageIds($message_body); $headers = explode("\n", $headers); foreach ($headers as $header) { $header = explode(':', $header, 2); $message_headers[ trim($header[0]) ] = trim($header[1]); } $this->_changeLanguage(); return Array ($message_headers, $message_body); } /** * Raised when email message should be sent * * @param kEvent $event * @return void * @access protected */ protected function OnEmailEvent(&$event) { $email_event_name = $event->getEventParam('EmailEventName'); if ( strpos($email_event_name, '_') !== false ) { throw new Exception('<span class="debug_error">Invalid email event name</span> <strong>' . $email_event_name . '</strong>. Use only <strong>UPPERCASE characters</strong> and <strong>dots</strong> as email event names'); } $object =& $this->_getEmailEvent($event); if ( !is_object($object) ) { // email event not found OR it's won't be send under given circumstances return ; } // additional parameters from kApplication->EmailEvent $send_params = $event->getEventParam('DirectSendParams'); // 1. get information about message sender and recipient list ($from_email, $from_name) = $this->_processSender($event, $send_params); list ($to_email, $to_name) = $this->_processRecipients($event, $send_params); // 2. prepare message to be sent $message_language = $this->_getSendLanguage($send_params); $message_template = $this->_getMessageBody($event, $message_language); if ( !trim($message_template) ) { trigger_error('Message template is empty', E_USER_WARNING); return ; } list ($message_headers, $message_body) = $this->ParseMessageBody($message_template, $send_params); if ( !trim($message_body) ) { trigger_error('Message template is empty after parsing', E_USER_WARNING); return ; } // 3. set headers & send message $esender =& $this->Application->recallObject('EmailSender'); /* @var $esender kEmailSendingHelper */ $message_subject = isset($message_headers['Subject']) ? $message_headers['Subject'] : 'Mail message'; $esender->SetSubject($message_subject); if ( $this->Application->isDebugMode() ) { // set special header with event name, so it will be easier to determine what's actually was received $message_headers['X-Event-Name'] = $email_event_name . ' - ' . ($object->GetDBField('Type') == EmailEvent::EVENT_TYPE_ADMIN ? 'ADMIN' : 'USER'); } foreach ($message_headers as $header_name => $header_value) { $esender->SetEncodedHeader($header_name, $header_value); } $esender->CreateTextHtmlPart($message_body, $object->GetDBField('MessageType') == 'html'); $event->status = $esender->Deliver() ? kEvent::erSUCCESS : kEvent::erFAIL; if ( $event->status == kEvent::erSUCCESS ) { // all keys, that are not used in email sending are written to log record $send_keys = Array ('from_email', 'from_name', 'to_email', 'to_name', 'message'); foreach ($send_keys as $send_key) { unset($send_params[$send_key]); } $fields_hash = Array ( 'fromuser' => $from_name . ' (' . $from_email . ')', 'addressto' => $to_name . ' (' . $to_email . ')', 'subject' => $message_subject, 'timestamp' => adodb_mktime(), 'event' => $email_event_name, 'EventParams' => serialize($send_params), ); $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'EmailLog'); } } function _getSendLanguage($send_params) { if (array_key_exists('language_id', $send_params)) { return $send_params['language_id']; } return $this->Application->GetVar('m_lang'); } function _changeLanguage($language_id = null) { static $prev_language_id = null; if ( !isset($language_id) ) { // restore language $language_id = $prev_language_id; } $this->Application->SetVar('m_lang', $language_id); $language =& $this->Application->recallObject('lang.current'); /* @var $language LanguagesItem */ $language->Load($language_id); $this->Application->Phrases->LanguageId = $language_id; $this->Application->Phrases->Phrases = Array (); $prev_language_id = $language_id; // for restoring it later } /** * Process emails from queue * * @param kEvent $event * @todo Move to MailingList */ function OnProcessEmailQueue(&$event) { $deliver_count = $event->getEventParam('deliver_count'); if ($deliver_count === false) { $deliver_count = $this->Application->ConfigValue('MailingListSendPerStep'); if ($deliver_count === false) { $deliver_count = 10; // 10 emails per script run (if not specified directly) } } $processing_type = $this->Application->GetVar('type'); if ($processing_type = 'return_progress') { $email_queue_progress = $this->Application->RecallVar('email_queue_progress'); if ($email_queue_progress === false) { $emails_sent = 0; $sql = 'SELECT COUNT(*) FROM ' . TABLE_PREFIX . 'EmailQueue WHERE (SendRetries < 5) AND (LastSendRetry < ' . strtotime('-2 hours') . ')'; $total_emails = $this->Conn->GetOne($sql); $this->Application->StoreVar('email_queue_progress', $emails_sent.':'.$total_emails); } else { list ($emails_sent, $total_emails) = explode(':', $email_queue_progress); } } $sql = 'SELECT * FROM '.TABLE_PREFIX.'EmailQueue WHERE (SendRetries < 5) AND (LastSendRetry < ' . strtotime('-2 hours') . ') LIMIT 0,' . $deliver_count; $messages = $this->Conn->Query($sql); $message_count = count($messages); if (!$message_count) { // no messages left to send in queue if ($processing_type = 'return_progress') { $this->Application->RemoveVar('email_queue_progress'); $this->Application->Redirect($this->Application->GetVar('finish_template')); } return ; } $mailing_list_helper =& $this->Application->recallObject('MailingListHelper'); /* @var $mailing_list_helper MailingListHelper */ $mailing_list_helper->processQueue($messages); if ($processing_type = 'return_progress') { $emails_sent += $message_count; if ($emails_sent >= $total_emails) { $this->Application->RemoveVar('email_queue_progress'); $this->Application->Redirect($this->Application->GetVar('finish_template')); } $this->Application->StoreVar('email_queue_progress', $emails_sent.':'.$total_emails); $event->status = kEvent::erSTOP; echo ($emails_sent / $total_emails) * 100; } } /** * Prefills module dropdown * * @param kEvent $event */ function OnAfterConfigRead(&$event) { parent::OnAfterConfigRead($event); $options = Array (); foreach ($this->Application->ModuleInfo as $module_name => $module_info) { if ($module_name == 'In-Portal') { continue; } $options[$module_name] = $module_name; } $fields = $this->Application->getUnitOption($event->Prefix, 'Fields'); $fields['Module']['options'] = $options; $this->Application->setUnitOption($event->Prefix, 'Fields', $fields); if ($this->Application->GetVar('regional')) { $this->Application->setUnitOption($event->Prefix, 'PopulateMlFields', true); } } /** * Prepare temp tables and populate it * with items selected in the grid * * @param kEvent $event */ function OnEdit(&$event) { parent::OnEdit($event); // use language from grid, instead of primary language used by default $event->SetRedirectParam('m_lang', $this->Application->GetVar('m_lang')); } /** * Fixes default recipient type * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemLoad(&$event) { parent::OnAfterItemLoad($event); $object =& $event->getObject(); /* @var $object kDBItem */ if (!$this->Application->isDebugMode(false)) { if ($object->GetDBField('AllowChangingRecipient')) { $object->SetDBField('RecipientType', EmailEvent::RECIPIENT_TYPE_TO); } else { $object->SetDBField('RecipientType', EmailEvent::RECIPIENT_TYPE_CC); } } // process replacement tags $records = Array (); $replacement_tags = $object->GetDBField('ReplacementTags'); $replacement_tags = $replacement_tags ? unserialize($replacement_tags) : Array (); foreach ($replacement_tags as $tag => $replacement) { $records[] = Array ('Tag' => $tag, 'Replacement' => $replacement); } $minput_helper =& $this->Application->recallObject('MInputHelper'); /* @var $minput_helper MInputHelper */ $xml = $minput_helper->prepareMInputXML($records, Array ('Tag', 'Replacement')); $object->SetDBField('ReplacementTagsXML', $xml); } /** * Performs custom validation + keep read-only fields * * @param kEvent $event */ function _itemChanged(&$event) { $object =& $event->getObject(); /* @var $object kDBItem */ // validate email subject and body for parsing errors $this->_validateEmailTemplate($object); // validate sender and recipient addresses if ($object->GetDBField('CustomSender')) { $this->_validateAddress($event, 'Sender'); } $this->_validateAddress($event, 'Recipient'); if (!$this->Application->isDebugMode(false)) { // only allow to enable/disable event while in debug mode $to_restore = Array ('Enabled', 'AllowChangingSender', 'AllowChangingRecipient'); if (!$object->GetOriginalField('AllowChangingSender')) { $to_restore = array_merge($to_restore, Array ('CustomSender', 'SenderName', 'SenderAddressType', 'SenderAddress')); } if (!$object->GetOriginalField('AllowChangingRecipient')) { $to_restore = array_merge($to_restore, Array ('CustomRecipient'/*, 'Recipients'*/)); } // prevent specific fields from editing foreach ($to_restore as $restore_field) { $original_value = $object->GetOriginalField($restore_field); if ($object->GetDBField($restore_field) != $original_value) { $object->SetDBField($restore_field, $original_value); } } } // process replacement tags if ( $object->GetDBField('ReplacementTagsXML') ) { $minput_helper =& $this->Application->recallObject('MInputHelper'); /* @var $minput_helper MInputHelper */ $replacement_tags = Array (); $records = $minput_helper->parseMInputXML( $object->GetDBField('ReplacementTagsXML') ); foreach ($records as $record) { $replacement_tags[ trim($record['Tag']) ] = trim($record['Replacement']); } $object->SetDBField('ReplacementTags', $replacement_tags ? serialize($replacement_tags) : NULL); } } /** * Validates address using given field prefix * * @param kEvent $event * @param string $field_prefix */ function _validateAddress(&$event, $field_prefix) { $object =& $event->getObject(); /* @var $object kDBItem */ $address_type = $object->GetDBField($field_prefix . 'AddressType'); $object->setRequired($field_prefix . 'Address', $address_type > 0); $address = $object->GetDBField($field_prefix . 'Address'); if (!$address) { // don't validate against empty address return ; } switch ($address_type) { case EmailEvent::ADDRESS_TYPE_EMAIL: if (!preg_match('/^(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')$/i', $address)) { $object->SetError($field_prefix . 'Address', 'invalid_email'); } break; case EmailEvent::ADDRESS_TYPE_USER: $sql = 'SELECT PortalUserId FROM ' . TABLE_PREFIX . 'PortalUser WHERE Login = ' . $this->Conn->qstr($address); if (!$this->Conn->GetOne($sql)) { $object->SetError($field_prefix . 'Address', 'invalid_user'); } break; case EmailEvent::ADDRESS_TYPE_GROUP: $sql = 'SELECT GroupId FROM ' . TABLE_PREFIX . 'PortalGroup WHERE Name = ' . $this->Conn->qstr($address); if (!$this->Conn->GetOne($sql)) { $object->SetError($field_prefix . 'Address', 'invalid_group'); } break; } } /** * Don't allow to enable/disable events in non-debug mode * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(&$event) { parent::OnBeforeItemCreate($event); $this->_itemChanged($event); } /** * Don't allow to enable/disable events in non-debug mode * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(&$event) { parent::OnBeforeItemUpdate($event); $this->_itemChanged($event); } /** * Suggest address based on typed address and selected address type * * @param kEvent $event */ function OnSuggestAddress(&$event) { $event->status = kEvent::erSTOP; $address_type = $this->Application->GetVar('type'); $address = $this->Application->GetVar('value'); $limit = $this->Application->GetVar('limit'); if (!$limit) { $limit = 20; } switch ($address_type) { case EmailEvent::ADDRESS_TYPE_EMAIL: $field = 'Email'; $table_name = TABLE_PREFIX . 'PortalUser'; break; case EmailEvent::ADDRESS_TYPE_USER: $field = 'Login'; $table_name = TABLE_PREFIX . 'PortalUser'; break; case EmailEvent::ADDRESS_TYPE_GROUP: $field = 'Name'; $table_name = TABLE_PREFIX . 'PortalGroup'; break; } if (isset($field)) { $sql = 'SELECT DISTINCT ' . $field . ' FROM ' . $table_name . ' WHERE ' . $field . ' LIKE ' . $this->Conn->qstr($address . '%') . ' ORDER BY ' . $field . ' ASC LIMIT 0,' . $limit; $data = $this->Conn->GetCol($sql); } else { $data = Array (); } $this->Application->XMLHeader(); echo '<suggestions>'; foreach ($data as $item) { echo '<item>' . htmlspecialchars($item) . '</item>'; } echo '</suggestions>'; } /** * Validates subject and body fields of Email template * @param kDBItem $object */ function _validateEmailTemplate(&$object) { $this->parseField($object, 'Subject'); $this->parseField($object, 'Body'); } /** * Parses contents of given object field and sets error, when invalid in-portal tags found * @param kDBItem $object * @param string $field * @return void */ function parseField(&$object, $field) { $this->Application->InitParser(); - + try { $this->Application->Parser->CompileRaw($object->GetField($field), 'email_template'); } catch (ParserException $e) { if ( $this->Application->isDebugMode() ) { $this->Application->Debugger->appendHTML('<b style="color: red;">Error in Email Template:</b> ' . $e->getMessage() . ' (line: ' . $e->getLine() . ')'); } $object->SetError($field, 'parsing_error'); } } } \ No newline at end of file