Index: branches/5.2.x/core/units/helpers/page_helper.php =================================================================== --- branches/5.2.x/core/units/helpers/page_helper.php (revision 15327) +++ branches/5.2.x/core/units/helpers/page_helper.php (revision 15328) @@ -1,315 +1,275 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2011 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 PageHelper extends kHelper { /** * Returns page info * * @param int $page_id * @return Array */ function getPageInfo($page_id) { list ($user_id, $history_permission) = $this->getHistoryPermissionAndUser($page_id); $where_clause = Array ( 'pr.PageId = ' . $page_id, 'pr.CreatedById <> ' . $user_id, 'pr.IsDraft = 1', ); $sql = 'SELECT CASE pr.CreatedById WHEN ' . USER_ROOT . ' THEN "root" WHEN ' . USER_GUEST . ' THEN "Guest" ELSE u.Username END FROM ' . $this->Application->getUnitOption('page-revision', 'TableName') . ' pr LEFT JOIN ' . TABLE_PREFIX . 'Users u ON u.PortalUserId = pr.CreatedById WHERE (' . implode(') AND (', $where_clause) . ')'; $users = $this->Conn->GetCol($sql); $page_revisions = Array (); if ( $history_permission ) { $tag_params = Array ('per_page' => -1, 'skip_parent_filter' => 1, 'requery' => 1, 'page_id' => $page_id); $revisions = $this->Application->recallObject('page-revision.list', 'page-revision_List', $tag_params); /* @var $revisions kDBList */ $revisions->Query(); $revisions->GoFirst(); $status_options = $revisions->GetFieldOptions('Status'); $draft_label = $this->Application->Phrase('la_Draft', false, true); $title_label = $this->Application->Phrase('la_RevisionNumber', false, true); $by_label = $this->Application->Phrase('la_By', false, true); while ( !$revisions->EOL() ) { $status = $revisions->GetDBField('Status'); $status_label = $this->Application->Phrase($status_options['options'][$status], false, true); $page_revisions[ 'r' . $revisions->GetDBField('RevisionNumber') ] = Array ( 'title' => $revisions->GetDBField('IsDraft') ? $draft_label : sprintf($title_label, $revisions->GetDBField('RevisionNumber')), 'status' => $status, 'status_label' => mb_strtolower($status_label), 'datetime' => $revisions->GetField('CreatedOn'), 'author' => $by_label . ': ' . $revisions->GetField('CreatedById'), 'draft' => (int)$revisions->GetDBField('IsDraft'), ); $revisions->GoNext(); } } $current_revision = $this->Application->recallObject('page-revision.current'); /* @var $current_revision kDBItem */ $revision_status = $current_revision->GetDBField('Status'); $status_options = $current_revision->GetFieldOptions('Status'); $status_label = $this->Application->Phrase($status_options['options'][$revision_status], false, true); $revision_phase = $current_revision->GetDBField('IsDraft') ? 'la_title_EditingDraft' : 'la_title_ViewingRevision'; $revision_title = sprintf($this->Application->Phrase($revision_phase, false, true), $current_revision->GetDBField('RevisionNumber'), mb_strtolower($status_label)); $current_revision_info = Array ('title' => $revision_title, 'status' => $revision_status, 'saved' => ''); $autosave_time = $current_revision->GetDBField('AutoSavedOn'); if ( $autosave_time ) { $phrase = $this->Application->Phrase($current_revision->GetDBField('IsDraft') ? 'la_DraftSavedAt' : 'la_SavedAt', false, true); $current_revision_info['saved'] = sprintf($phrase, $current_revision->GetField('AutoSavedOn_time') . ' (' . $this->getAgoTime($autosave_time) . ')'); } - $currently_editing = $this->getPluralPhrase( + $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); + /* @var $ml_helper kMultiLanguageHelper */ + + $currently_editing = $ml_helper->getPluralPhrase( count($users), Array ( 'phrase1' => 'la_PageCurrentlyEditing1', 'phrase2' => 'la_PageCurrentlyEditing2', 'phrase5' => 'la_PageCurrentlyEditing5', ), false, true ); $currently_editing = sprintf($currently_editing, implode(', ', $users)); return Array ('current_revision' => $current_revision_info, 'editors' => $users, 'editors_warning' => $currently_editing, 'revisions' => $page_revisions); } /** * Returns time passed between 2 given dates in "X minutes Y seconds ago" format * * @param int $from_date * @param int $to_date * @return string */ function getAgoTime($from_date, $to_date = null, $max_levels = 1) { $blocks = Array ( Array ('name' => 'year', 'amount' => 60*60*24*365), Array ('name' => 'month' ,'amount' => 60*60*24*31), Array ('name' => 'week', 'amount' => 60*60*24*7), Array ('name' => 'day', 'amount' => 60*60*24), Array ('name' => 'hour', 'amount' => 60*60), Array ('name' => 'minute', 'amount' => 60), Array ('name' => 'second', 'amount' => 1), ); if ( !isset($to_date) ) { $to_date = adodb_mktime(); } $diff = abs($to_date - $from_date); if ( $diff == 0 ) { return 'now'; } $current_level = 1; $result = Array (); foreach ($blocks as $block) { if ($current_level > $max_levels) { break; } if ( $diff / $block['amount'] >= 1 ) { $amount = floor($diff / $block['amount']); $plural = $amount > 1 ? 's' : ''; $result[] = $amount . ' ' . $block['name'] . $plural; $diff -= $amount * $block['amount']; $current_level++; } } return implode(' ', $result) . ' ago'; } /** * Returns where clause for loading correct revision for a given page * * @param int $page_id * @param int $live_revision_number * @param string $table_name * @return string */ function getRevsionWhereClause($page_id, $live_revision_number, $table_name = '') { $revision = (int)$this->Application->GetVar('revision'); list ($user_id, $has_permission) = $this->getHistoryPermissionAndUser($page_id); if ( $has_permission && $revision ) { $revision_clause = $table_name . 'RevisionNumber = ' . $revision . ' AND ' . $table_name . 'IsDraft = 0'; } else { $editing_mode = $this->Application->GetVar('editing_mode'); // not in a EDITING_MODE constant, while in admin console $revision_clause = $table_name . 'RevisionNumber = ' . $live_revision_number . ' AND ' . $table_name . 'IsDraft = 0'; if ( $this->Application->GetVar('preview') || $editing_mode == EDITING_MODE_CONTENT ) { $revision_clause = '(' . $table_name . 'CreatedById = ' . $user_id . ' AND ' . $table_name . 'IsDraft = 1) OR (' . $revision_clause . ')'; } } return $revision_clause; } /** * Returns current admin user id (even, when called from front-end) and it's revision history view permission * * @param int $page_id * @return Array */ function getHistoryPermissionAndUser($page_id) { $user_id = (int)$this->Application->RecallVar($this->Application->isAdmin ? 'user_id' : 'admin_user_id'); $history_permission = $this->Application->CheckAdminPermission('CATEGORY.REVISION.HISTORY.VIEW', 0, $page_id); return Array ($user_id, $history_permission); } /** * Creates new content block in every revision that misses it. Plus creates first page revision * * @param int $page_id * @param int $num */ function createNewContentBlock($page_id, $num) { $sql = 'SELECT pc.PageContentId, pr.RevisionId FROM ' . TABLE_PREFIX . 'PageRevisions pr LEFT JOIN ' . TABLE_PREFIX . 'PageContent pc ON pc.RevisionId = pr.RevisionId AND pc.ContentNum = ' . $num . ' WHERE pr.PageId = ' . $page_id; $revisions = $this->Conn->GetCol($sql, 'RevisionId'); if ( !$revisions ) { // no revisions for a page -> create a live revision $revision = $this->Application->recallObject('page-revision.live', null, Array ('skip_autoload' => true)); /* @var $revision kDBItem */ $revision->SetDBField('PageId', $page_id); $revision->SetDBField('RevisionNumber', 1); $revision->SetDBField('Status', STATUS_ACTIVE); $revision->Create(); $revisions[ $revision->GetID() ] = NULL; } $content_block = $this->Application->recallObject('content.new', null, Array ('skip_autoload' => true)); /* @var $content_block kDBItem */ $content_block->SetDBField('PageId', $page_id); $content_block->SetDBField('ContentNum', $num); foreach ($revisions as $revision_id => $content_block_id) { if ( is_numeric($content_block_id) ) { continue; } $content_block->SetDBField('RevisionId', $revision_id); $content_block->Create(); } } /** * Loads content block by it's number * * @param kDBItem $content_block * @param CategoriesItem $page * @param int $num * * @return bool */ function loadContentBlock(&$content_block, &$page, $num) { $page_id = $page->GetID(); if ( !EDITING_MODE && !$this->Application->GetVar('preview') ) { $revision_clause = 'pr.RevisionNumber = ' . $page->GetDBField('LiveRevisionNumber') . ' AND pr.IsDraft = 0'; } else { $revision_clause = $this->getRevsionWhereClause($page_id, $page->GetDBField('LiveRevisionNumber'), 'pr.'); } $sql = $content_block->GetSelectSQL() . ' WHERE (' . $content_block->TableName . '.PageId = ' . $page_id . ') AND (' . $content_block->TableName . '.ContentNum = ' . $num . ') AND (' . $revision_clause . ') ORDER BY pr.IsDraft DESC, pr.RevisionNumber DESC'; $content_data = $this->Conn->GetRow($sql); $content_block->LoadFromHash($content_data); return $content_block->isLoaded(); } - - /** - * Returns phrase based on given number - * - * @param int $number - * @param Array $forms - * @return string - */ - function getPluralPhrase($number, $forms, $allow_editing = true, $use_admin = false) - { - // normalize given forms - if ( !array_key_exists('phrase5', $forms) ) { - $forms['phrase5'] = $forms['phrase2']; - } - - $phrase_type = $this->getPluralPhraseType($number); - - return $this->Application->Phrase( $forms['phrase' . $phrase_type], $allow_editing, $use_admin ); - } - - /** - * Returns phrase type based on given number - * - * @param int $number - * @return int - */ - function getPluralPhraseType($number) - { - $last_digit = substr($number, -1); - $last_but_one_digit = strlen($number) > 1 ? substr($number, -2, 1) : false; - $phrase_type = '5'; - - if ($last_but_one_digit != 1) { - if ($last_digit == 1) { - $phrase_type = '1'; - } - elseif ($last_digit >= 2 && $last_digit <= 4) { - $phrase_type = '2'; - } - } - - return $phrase_type; - } } Index: branches/5.2.x/core/units/helpers/multilanguage_helper.php =================================================================== --- branches/5.2.x/core/units/helpers/multilanguage_helper.php (revision 15327) +++ branches/5.2.x/core/units/helpers/multilanguage_helper.php (revision 15328) @@ -1,409 +1,456 @@ <?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!'); /** * Performs action on multilingual fields * */ class kMultiLanguageHelper extends kHelper { /** * Determines, that language info should be requeried * * @var bool * @access protected */ protected $initMade = false; /** * Maximal language id * * @var int */ protected $languageCount = 0; /** * Languages created in system * * @var Array */ protected $languagesIDs = Array (); /** * Structure of table, that is currently processed * * @var Array */ var $curStructure = Array(); /** * Field, to get structure information from * * @var string */ var $curSourceField = false; /** * Indexes used in table of 32 * * @var int */ var $curIndexCount = 0; /** * Fields from config, that are currently used * * @var Array */ var $curFields = Array(); public function resetState() { $this->initMade = false; } /** * Updates language count in system (always is divisible by 5) * */ protected function _queryLanguages() { if ( !$this->initMade ) { $this->languagesIDs = $this->getActualLanguages(); $this->languageCount = max(max($this->languagesIDs), 5); $this->initMade = true; } } /** * Returns language ids, that can be used * * @return Array */ protected function getActualLanguages() { $cache_key = 'actual_language_ids[%LangSerial%]'; $ret = $this->Application->getCache($cache_key); if ( $ret === false ) { $this->Conn->nextQueryCachable = true; $sql = 'SELECT ' . $this->Application->getUnitOption('lang', 'IDField') . ' FROM ' . $this->Application->getUnitOption('lang', 'TableName'); $ret = $this->Conn->GetCol($sql); $this->Application->setCache($cache_key, $ret); } return $ret; } /** * Checks if language with specified id is created * * @param int $language_id * @return bool */ protected function LanguageFound($language_id) { return in_array($language_id, $this->languagesIDs) || $language_id <= 5; } /** * Returns list of processable languages * * @return Array */ public function getLanguages() { $cache_key = 'processable_language_ids[%LangSerial%]'; $ret = $this->Application->getCache($cache_key); if ( $ret === false ) { $ret = Array (); $this->_queryLanguages(); for ($language_id = 1; $language_id <= $this->languageCount; $language_id++) { if ( $this->LanguageFound($language_id) ) { $ret[] = $language_id; } } $this->Application->setCache($cache_key, $ret); } return $ret; } function scanTable($mask) { $i = 0; $fields_found = 0; $fields = array_keys($this->curStructure); foreach ($fields as $field_name) { if (preg_match($mask, $field_name)) { $fields_found++; } } return $fields_found; } function readTableStructure($table_name, $refresh = false) { // if ($refresh || !getArrayValue($structure_status, $prefix.'.'.$table_name)) { $this->curStructure = $this->Conn->Query('DESCRIBE '.$table_name, 'Field'); $this->curIndexCount = count($this->Conn->Query('SHOW INDEXES FROM '.$table_name)); // } } /** * Creates missing multilingual fields for all unit configs, registered in system * * @param bool $reread_configs * @return void * @access public */ public function massCreateFields($reread_configs = true) { if ( $reread_configs ) { $this->Application->UnitConfigReader->ReReadConfigs(); } foreach ($this->Application->UnitConfigReader->configData as $prefix => $config_data) { $this->createFields($prefix); } } /** * Creates missing multilanguage fields in table by specified prefix * * @param string $prefix * @param bool $refresh Forces config field structure to be re-read from database * @return void */ function createFields($prefix, $refresh = false) { if ( $refresh && preg_match('/(.*)-cdata$/', $prefix, $regs) ) { // call main item config to clone cdata table $this->Application->UnitConfigReader->loadConfig($regs[1]); $this->Application->UnitConfigReader->runAfterConfigRead($prefix); } $table_name = $this->Application->getUnitOption($prefix, 'TableName'); $this->curFields = $this->Application->getUnitOption($prefix, 'Fields'); if ( !($table_name && $this->curFields) || ($table_name && !$this->Conn->TableFound($table_name, kUtil::constOn('IS_INSTALL'))) ) { // invalid config found or prefix not found return ; } $this->_queryLanguages(); $sqls = Array (); $this->readTableStructure($table_name, $refresh); foreach ($this->curFields as $field_name => $field_options) { if ( getArrayValue($field_options, 'formatter') == 'kMultiLanguage' ) { if ( isset($field_options['master_field']) ) { unset($this->curFields[$field_name]); continue; } $this->setSourceField($field_name); if ( $this->languageCount > 0 ) { // `l77_Name` VARCHAR( 255 ) NULL DEFAULT '0'; $field_mask = Array (); $field_mask['name'] = 'l%s_' . $field_name; $field_mask['null'] = getArrayValue($field_options, 'not_null') ? 'NOT NULL' : 'NULL'; if ( $this->curSourceField ) { $default_value = $this->getFieldParam('Default') != 'NULL' ? $this->Conn->qstr($this->getFieldParam('Default')) : $this->getFieldParam('Default'); $field_mask['type'] = $this->getFieldParam('Type'); } else { $default_value = is_null($field_options['default']) ? 'NULL' : $this->Conn->qstr($field_options['default']); $field_mask['type'] = $field_options['db_type']; } $field_mask['default'] = ($field_mask['null'] == 'NOT NULL' && $default_value == 'NULL') ? '' : 'DEFAULT ' . $default_value; if ( strtoupper($field_mask['type']) == 'TEXT' ) { // text fields in mysql doesn't have default value $field_mask = $field_mask['name'] . ' ' . $field_mask['type'] . ' ' . $field_mask['null']; } else { $field_mask = $field_mask['name'] . ' ' . $field_mask['type'] . ' ' . $field_mask['null'] . ' ' . $field_mask['default']; } $alter_sqls = $this->generateAlterSQL($field_mask, 1, $this->languageCount); if ( $alter_sqls ) { $sqls[] = 'ALTER TABLE ' . $table_name . ' ' . $alter_sqls; } } } } foreach ($sqls as $sql_query) { $this->Conn->Query($sql_query); } } /** * Creates missing multilanguage fields in table by specified prefix * * @param string $prefix * @param int $src_language * @param int $dst_language * @return void * @access public */ public function copyMissingData($prefix, $src_language, $dst_language) { $table_name = $this->Application->getUnitOption($prefix, 'TableName'); $this->curFields = $this->Application->getUnitOption($prefix, 'Fields'); if ( !($table_name && $this->curFields) || ($table_name && !$this->Conn->TableFound($table_name, kUtil::constOn('IS_INSTALL'))) ) { // invalid config found or prefix not found return ; } foreach ($this->curFields as $field_name => $field_options) { $formatter = isset($field_options['formatter']) ? $field_options['formatter'] : ''; if ( ($formatter == 'kMultiLanguage') && !isset($field_options['master_field']) ) { $sql = 'UPDATE ' . $table_name . ' SET l' . $dst_language . '_' . $field_name . ' = l' . $src_language . '_' . $field_name . ' WHERE l' . $dst_language . '_' . $field_name . ' = "" OR l' . $dst_language . '_' . $field_name . ' IS NULL'; $this->Conn->Query($sql); } } } function deleteField($prefix, $custom_id) { $table_name = $this->Application->getUnitOption($prefix, 'TableName'); $sql = 'DESCRIBE '.$table_name.' "l%_cust_'.$custom_id.'"'; $fields = $this->Conn->GetCol($sql); $sql = 'ALTER TABLE '.$table_name.' '; $sql_template = 'DROP COLUMN %s, '; foreach ($fields as $field_name) { $sql .= sprintf($sql_template, $field_name); } $this->Conn->Query( substr($sql, 0, -2) ); } /** * Returns parameter requested of current source field * * @param string $param_name * @return string */ function getFieldParam($param_name) { return $this->curStructure[$this->curSourceField][$param_name]; } /** * Detects field name to create other fields from * * @param string $field_name */ function setSourceField($field_name) { $ret = $this->scanTable('/^l[\d]+_'.preg_quote($field_name, '/').'$/'); if (!$ret) { // no multilingual fields at all (but we have such field without language prefix) $original_found = $this->scanTable('/^'.preg_quote($field_name, '$/').'/'); $this->curSourceField = $original_found ? $field_name : false; } else { $this->curSourceField = 'l1_'.$field_name; } } /** * Returns ALTER statement part for adding required fields to table * * @param string $field_mask sql mask for creating field with correct definition (type & size) * @param int $start_index add new fields starting from this index * @param int $create_count create this much new multilingual field translations * @return string */ function generateAlterSQL($field_mask, $start_index, $create_count) { static $single_lang = null; if (!isset($single_lang)) { // if single language mode, then create indexes only on primary columns $table_name = $this->Application->getUnitOption('lang', 'TableName'); $sql = 'SELECT COUNT(*) FROM '.$table_name.' WHERE Enabled = 1'; // if language count = 0, then assume it's multi language mode $single_lang = $this->Conn->GetOne($sql) == 1; } $ret = ''; $ml_field = preg_replace('/l(.*?)_(.*?) (.*)/', '\\2', $field_mask); $i_count = $start_index + $create_count; while ($start_index < $i_count) { if (isset($this->curStructure['l'.$start_index.'_'.$ml_field]) || (!$this->LanguageFound($start_index)) ) { $start_index++; continue; } $prev_index = $start_index - 1; do { list($prev_field,$type) = explode(' ', sprintf($field_mask, $prev_index) ); } while ($prev_index > 0 && !$this->LanguageFound($prev_index--)); if (substr($prev_field, 0, 3) == 'l0_') { $prev_field = substr($prev_field, 3, strlen($prev_field)); if (!$this->curSourceField) { // get field name before this one $fields = array_keys($this->curFields); // $prev_field = key(end($this->curStructure)); $prev_field = $fields[array_search($prev_field, $fields) - 1]; if (getArrayValue($this->curFields[$prev_field], 'formatter') == 'kMultiLanguage') { $prev_field = 'l'.$this->languageCount.'_'.$prev_field; } } } $field_expression = sprintf($field_mask, $start_index); $ret .= 'ADD COLUMN '.$field_expression.' AFTER `'.$prev_field.'`, '; if ($this->curIndexCount < 32 && ($start_index == $this->Application->GetDefaultLanguageId() || !$single_lang)) { // create index for primary language column + for all others (if multiple languages installed) list($field_name, $field_params) = explode(' ', $field_expression, 2); $index_type = isset($this->curFields[$ml_field]['index_type']) ? $this->curFields[$prev_field]['index_type'] : 'string'; $ret .= $index_type == 'string' ? 'ADD INDEX (`'.$field_name.'` (5) ), ' : 'ADD INDEX (`'.$field_name.'`), '; $this->curIndexCount++; } $start_index++; } return preg_replace('/, $/', ';', $ret); } + + /** + * Returns phrase based on given number + * + * @param int $number + * @param Array $forms + * @param bool $allow_editing + * @param bool $use_admin + * @return string + * @access public + */ + public function getPluralPhrase($number, $forms, $allow_editing = true, $use_admin = false) + { + // normalize given forms + if ( !array_key_exists('phrase5', $forms) ) { + $forms['phrase5'] = $forms['phrase2']; + } + + $phrase_type = $this->getPluralPhraseType($number); + + return $this->Application->Phrase($forms['phrase' . $phrase_type], $allow_editing, $use_admin); + } + + /** + * Returns phrase type based on given number + * + * @param int $number + * @return int + * @access protected + */ + protected function getPluralPhraseType($number) + { + $last_digit = substr($number, -1); + $last_but_one_digit = strlen($number) > 1 ? substr($number, -2, 1) : false; + $phrase_type = '5'; + + if ( $last_but_one_digit != 1 ) { + if ( $last_digit == 1 ) { + $phrase_type = '1'; + } + elseif ( $last_digit >= 2 && $last_digit <= 4 ) { + $phrase_type = '2'; + } + } + + return $phrase_type; + } }