Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Fri, Feb 21, 11:59 PM

in-portal

Index: branches/5.2.x/core/units/categories/cache_updater.php
===================================================================
--- branches/5.2.x/core/units/categories/cache_updater.php (revision 16813)
+++ branches/5.2.x/core/units/categories/cache_updater.php (revision 16814)
@@ -1,579 +1,579 @@
<?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 clsRecursionStack {
var $Stack;
/**
* Creates instance of clsRecursionStack class.
*/
public function __construct()
{
$this->Stack = Array();
}
function Push($values)
{
array_push($this->Stack, $values);
}
function Pop()
{
if ($this->Count() > 0) {
return array_pop($this->Stack);
}
else {
return false;
}
}
function Get()
{
if ($this->Count() > 0) {
// return end($this->Stack);
return $this->Stack[count($this->Stack)-1];
}
else {
return false;
}
}
function Update($values)
{
$this->Stack[count($this->Stack)-1] = $values;
}
function Count()
{
return count($this->Stack);
}
}
class clsCachedPermissions {
var $Allow = Array();
var $Deny = Array();
var $CatId;
/**
* Table name used for inserting permissions
*
* @var string
*/
var $table = '';
/**
* Creates class instance.
*
* @param integer $cat_id Category ID.
* @param string $table_name Table name.
*/
public function __construct($cat_id, $table_name)
{
$this->CatId = $cat_id;
$this->table = $table_name;
}
function SetCatId($CatId)
{
$this->CatId = $CatId;
}
function CheckPermArray($Perm)
{
if (!isset($this->Allow[$Perm])) {
$this->Allow[$Perm] = array();
$this->Deny[$Perm] = array();
}
}
function AddAllow($Perm, $GroupId)
{
$this->CheckPermArray($Perm);
if (!in_array($GroupId, $this->Allow[$Perm])) {
array_push($this->Allow[$Perm], $GroupId);
$this->RemoveDeny($Perm, $GroupId);
}
}
function AddDeny($Perm, $GroupId)
{
$this->CheckPermArray($Perm);
if (!in_array($GroupId, $this->Deny[$Perm])) {
array_push($this->Deny[$Perm], $GroupId);
$this->RemoveAllow($Perm, $GroupId);
}
}
function RemoveDeny($Perm, $GroupId)
{
if (in_array($GroupId, $this->Deny[$Perm])) {
array_splice($this->Deny[$Perm], array_search($GroupId, $this->Deny[$Perm]), 1);
}
}
function RemoveAllow($Perm, $GroupId)
{
if (in_array($GroupId, $this->Allow[$Perm])) {
array_splice($this->Allow[$Perm], array_search($GroupId, $this->Allow[$Perm]), 1);
}
}
function GetInsertSQL()
{
$values = array();
foreach ($this->Allow as $perm => $groups) {
if (count($groups) > 0) {
$values[] = '(' .$this->CatId. ', ' .$perm. ', "' .join(',', $groups). '")';
}
}
if ( !$values ) {
return '';
}
return 'INSERT INTO `' . $this->table . '` (CategoryId, PermId, ACL) VALUES ' . join(',', $values);
}
}
class kPermCacheUpdater extends kHelper {
/**
* Holds Stack
*
* @var clsRecursionStack
*/
var $Stack;
/**
* Rebuild process iteration
*
* @var int
*/
var $iteration;
/**
* Categories count to process
*
* @var unknown_type
*/
var $totalCats = 0;
/**
* Processed categories count
*
* @var int
*/
var $doneCats = 0;
/**
* Temporary table name used for storing cache building progress
*
* @var string
*/
var $progressTable = '';
/**
* Temporary table name used for storing not fully built permissions cache
* 1. preserves previous cache while new cache is building
* 2. when rebuild process fails allows previous cache (in live table) is used
*
* @var string
*/
var $permCacheTable = '';
var $primaryLanguageId = 0;
var $languages = Array ();
var $root_prefixes = Array();
/**
* Update cache only for requested categories and it's parent categories
*
* @var bool
*/
var $StrictPath = false;
/**
* Returns instance of perm cache updater
*
* @param int $continuing
* @param mixed $strict_path
*/
public function __construct($continuing = null, $strict_path = null)
{
parent::__construct();
if ( isset($strict_path) ) {
if ( $strict_path && !is_array($strict_path) ) {
$strict_path = explode('|', trim($strict_path, '|'));
}
$this->StrictPath = $strict_path;
}
// cache widely used values to speed up process
/** @var kMultiLanguageHelper $ml_helper */
$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');
$this->languages = $ml_helper->getLanguages();
$this->primaryLanguageId = $this->Application->GetDefaultLanguageId();
foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
$this->root_prefixes[ $module_info['RootCat'] ] = $module_info['Var'];
}
$this->iteration = 0;
if ( PHP_SAPI === 'cli' ) {
// The progress bar isn't used.
$this->progressTable = sprintf('permCacheUpdate_%s_%s', time(), SecurityGenerator::generateBytes(8));
$this->permCacheTable = sprintf(
TABLE_PREFIX . 'CategoryPermissionsCache_%s_%s',
time(),
SecurityGenerator::generateBytes(8)
);
}
else {
// Temporary table name must be the same between redirects for progress bar to work.
$this->progressTable = $this->Application->GetTempName('permCacheUpdate');
$this->permCacheTable = $this->Application->GetTempName(TABLE_PREFIX . 'CategoryPermissionsCache');
}
if (!isset($continuing) || $continuing == 1) {
$this->InitUpdater();
}
elseif ($continuing == 2) {
$this->getData();
}
}
function InitUpdater()
{
$this->Stack = new clsRecursionStack();
$this->initData();
}
function getDonePercent()
{
if (!$this->totalCats) {
return 0;
}
return min(100, intval( floor( $this->doneCats / $this->totalCats * 100 ) ));
}
function getData()
{
$tmp = $this->Conn->GetOne('SELECT data FROM `' . $this->progressTable . '`');
if ( $tmp ) {
$tmp = unserialize($tmp);
}
$this->totalCats = isset($tmp['totalCats']) ? $tmp['totalCats'] : 0;
$this->doneCats = isset($tmp['doneCats']) ? $tmp['doneCats'] : 0;
if (isset($tmp['stack'])) {
$this->Stack = $tmp['stack'];
}
else {
$this->Stack = new clsRecursionStack();
}
}
function setData()
{
$tmp = Array (
'totalCats' => $this->totalCats,
'doneCats' => $this->doneCats,
'stack' => $this->Stack,
);
$this->Conn->Query('DELETE FROM `' . $this->progressTable . '`');
$fields_hash = Array('data' => serialize($tmp));
$this->Conn->doInsert($fields_hash, $this->progressTable);
}
function initData()
{
$this->clearData(); // drop table before starting anyway
// 1. create table for rebuilding permissions cache
$this->Conn->Query(
'CREATE TABLE `' . $this->permCacheTable . '` LIKE `' . TABLE_PREFIX . 'CategoryPermissionsCache`'
);
if ( $this->StrictPath ) {
// When using strict path leave all other cache intact.
$sql = 'INSERT INTO `' . $this->permCacheTable . '`
SELECT *
FROM `' . TABLE_PREFIX . 'CategoryPermissionsCache`';
$this->Conn->Query($sql);
// Delete only cache related to categories in path.
$sql = 'DELETE FROM `' . $this->permCacheTable . '`
WHERE CategoryId IN (' . implode(',', $this->StrictPath) . ')';
$this->Conn->Query($sql);
}
- $add_charset = defined(SQL_CHARSET)? ' CHARACTER SET '.SQL_CHARSET.' ' : '';
- $add_collation = defined(SQL_COLLATION)? ' COLLATE '.SQL_COLLATION.' ' : '';
+ $add_charset = defined('SQL_CHARSET') ? ' CHARACTER SET ' . SQL_CHARSET . ' ' : '';
+ $add_collation = defined('SQL_COLLATION') ? ' COLLATE ' . SQL_COLLATION . ' ' : '';
$this->Conn->Query(sprintf(
'CREATE TABLE `%1$s` (data LONGTEXT%2$s%3$s) %2$s%3$s',
$this->progressTable,
$add_charset,
$add_collation
));
$this->totalCats = (int)$this->Conn->GetOne('SELECT COUNT(*) FROM `' . TABLE_PREFIX . 'Categories`');
$this->doneCats = 0;
}
function clearData()
{
// some templates use this
$sql = 'UPDATE ' . TABLE_PREFIX . 'SystemSettings
SET VariableValue = VariableValue + 1
WHERE VariableName = "CategoriesRebuildSerial"';
$this->Conn->Query($sql);
// Always drop temporary tables.
$this->Conn->Query('DROP TABLE IF EXISTS `' . $this->progressTable . '`');
$this->Conn->Query('DROP TABLE IF EXISTS `' . $this->permCacheTable . '`');
$this->Application->deleteDBCache('ForcePermCacheUpdate');
}
function SaveData()
{
// copy data from temp permission cache table back to live
$this->Conn->Query('TRUNCATE '.TABLE_PREFIX.'CategoryPermissionsCache');
$sql = 'INSERT INTO `' . TABLE_PREFIX . 'CategoryPermissionsCache`
SELECT *
FROM `' . $this->permCacheTable . '`';
$this->Conn->Query($sql);
$this->clearData();
$this->Application->incrementCacheSerial('c');
}
function DoTheJob()
{
$data = $this->Stack->Get();
if ($data === false) { //If Stack is empty
$data['current_id'] = 0;
$data['titles'] = Array();
$data['parent_path'] = Array();
$data['named_path'] = Array();
$data['file_name'] = '';
$data['template'] = ''; // design
$data['item_template'] = '';
$data['children_count'] = 0;
$data['left'] = 0;
$data['right'] = 2;
$data['debug_title'] = 'ROOT';
$this->Stack->Push($data);
}
if (!isset($data['queried'])) {
$this->QueryTitle($data);
$this->QueryChildren($data);
$data['children_count'] = count($data['children']);
$this->QueryPermissions($data);
$data['queried'] = 1;
$data['right'] = $data['left']+1;
$sql = $data['perms']->GetInsertSQL();
if ( $sql ) {
$this->Conn->Query($sql);
// $this->doneCats++; // moved to the place where it pops out of the stack by Kostja
}
$this->iteration++;
}
// start with first child if we haven't started yet
if (!isset($data['current_child'])) $data['current_child'] = 0;
// if we have more children on CURRENT LEVEL
if (isset($data['children'][$data['current_child']])) {
if ($this->StrictPath) {
while ( isset($data['children'][ $data['current_child'] ]) && !in_array($data['children'][ $data['current_child'] ], $this->StrictPath) ) {
$data['current_child']++;
continue;
}
if (!isset($data['children'][ $data['current_child'] ])) return false; //error
}
$next_data = Array();
$next_data['titles'] = $data['titles'];
$next_data['parent_path'] = $data['parent_path'];
$next_data['named_path'] = $data['named_path'];
$next_data['template'] = $data['template'];
$next_data['item_template'] = $data['item_template'];
$next_data['current_id'] = $data['children'][ $data['current_child'] ]; //next iteration should process child
$next_data['perms'] = clone $data['perms']; // copy permissions to child - inheritance
$next_data['perms']->SetCatId($next_data['current_id']);
$next_data['left'] = $data['right'];
$data['current_child']++;
$this->Stack->Update($data); //we need to update ourself for the iteration after the next (or further) return to next child
$this->Stack->Push($next_data); //next iteration should process this child
return true;
}
else {
$this->Stack->Update($data);
$prev_data = $this->Stack->Pop(); //remove ourself from stack if we have finished all the childs (or there are none)
$data['right'] = $prev_data['right'];
$this->UpdateCachedPath($data);
// we are getting here if we finished with current level, so check if it's first level - then bail out.
$this->doneCats++; // moved by Kostja from above, seems to fix the prob
$has_more = $this->Stack->Count() > 0;
if ($has_more) {
$next_data = $this->Stack->Get();
$next_data['right'] = $data['right']+1;
$next_data['children_count'] += $data['children_count'];
$this->Stack->Update($next_data);
}
return $has_more;
}
}
function UpdateCachedPath(&$data)
{
if ( $data['current_id'] == 0 ) {
// don't update non-existing "Home" category
return;
}
// allow old fashion system templates to work (maybe this is no longer needed, since no such urls after upgrade from 4.3.1)
$named_parent_path = strpos($data['file_name'], '/') !== false ? $data['file_name'] : implode('/', $data['named_path']);
$fields_hash = Array (
'ParentPath' => '|' . implode('|', $data['parent_path']) . '|',
'NamedParentPath' => $named_parent_path, // url component for a category page
'NamedParentPathHash' => kUtil::crc32(mb_strtolower(preg_replace('/^Content\//i', '', $named_parent_path))),
'CachedTemplate' => $data['template'], // actual template to use when category is visited
'CachedTemplateHash' => kUtil::crc32(mb_strtolower($data['template'])),
'CachedDescendantCatsQty' => $data['children_count'],
'TreeLeft' => $data['left'],
'TreeRight' => $data['right'],
);
foreach ($this->languages as $language_id) {
$fields_hash['l' . $language_id . '_CachedNavbar'] = implode('&|&', $data['titles'][$language_id]);
}
$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Categories', 'CategoryId = ' . $data['current_id']);
if ( $this->Conn->getAffectedRows() > 0 ) {
$this->Application->incrementCacheSerial('c', $data['current_id']);
}
}
function QueryTitle(&$data)
{
$category_id = $data['current_id'];
$sql = 'SELECT *
FROM '.TABLE_PREFIX.'Categories
WHERE CategoryId = '.$category_id;
$record = $this->Conn->GetRow($sql);
if ($record) {
foreach ($this->languages as $language_id) {
$data['titles'][$language_id][] = $record['l'.$language_id.'_Name'] ? $record['l'.$language_id.'_Name'] : $record['l'.$this->primaryLanguageId.'_Name'];
}
$data['debug_title'] = $record['l1_Name'];
$data['parent_path'][] = $category_id;
$data['named_path'][] = preg_replace('/^Content\\//', '', $record['Filename']);
$data['file_name'] = $record['Filename'];
// it is one of the modules root category
/*$root_prefix = isset($this->root_prefixes[$category_id]) ? $this->root_prefixes[$category_id] : false;
if ( $root_prefix ) {
$fields_hash = Array ();
if ( !$record['Template'] ) {
$record['Template'] = $this->Application->ConfigValue($root_prefix . '_CategoryTemplate');
$fields_hash['Template'] = $record['Template'];
}
$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Categories', 'CategoryId = ' . $category_id);
}*/
// if explicitly set, then use it; use parent template otherwise
if ($record['Template'] && ($record['Template'] != CATEGORY_TEMPLATE_INHERIT)) {
$data['template'] = $record['Template'];
}
}
}
function QueryChildren(&$data)
{
$sql = 'SELECT CategoryId
FROM '.TABLE_PREFIX.'Categories
WHERE ParentId = '.$data['current_id'];
$data['children'] = $this->Conn->GetCol($sql);
}
function QueryPermissions(&$data)
{
// don't search for section "view" permissions here :)
$sql = 'SELECT ipc.PermissionConfigId, ip.GroupId, ip.PermissionValue
FROM '.TABLE_PREFIX.'Permissions AS ip
LEFT JOIN '.TABLE_PREFIX.'CategoryPermissionsConfig AS ipc ON ipc.PermissionName = ip.Permission
WHERE (CatId = '.$data['current_id'].') AND (Permission LIKE "%.VIEW") AND (ip.Type = 0)';
$records = $this->Conn->Query($sql);
//create permissions array only if we don't have it yet (set by parent)
if (!isset($data['perms'])) {
$data['perms'] = new clsCachedPermissions($data['current_id'], $this->permCacheTable);
}
foreach ($records as $record) {
if ($record['PermissionValue'] == 1) {
$data['perms']->AddAllow($record['PermissionConfigId'], $record['GroupId']);
}
else {
$data['perms']->AddDeny($record['PermissionConfigId'], $record['GroupId']);
}
}
}
/**
* Rebuild all cache in one step
*
* @param string $path
* @return void
*/
function OneStepRun($path = '')
{
$this->InitUpdater();
$needs_more = true;
while ($needs_more) {
// until proceeded in this step category count exceeds category per step limit
$needs_more = $this->DoTheJob();
}
$this->SaveData();
}
}

Event Timeline