Page Menu
Home
In-Portal Phabricator
Search
Configure Global Search
Log In
Files
F800054
in-portal
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Fri, Feb 21, 11:59 PM
Size
16 KB
Mime Type
text/x-diff
Expires
Sun, Feb 23, 11:59 PM (10 h, 29 m)
Engine
blob
Format
Raw Data
Handle
573320
Attached To
rINP In-Portal
in-portal
View Options
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
Log In to Comment