Page Menu
Home
In-Portal Phabricator
Search
Configure Global Search
Log In
Files
F1174514
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
Thu, Oct 2, 2:16 PM
Size
13 KB
Mime Type
text/x-diff
Expires
Sat, Oct 4, 2:16 PM (1 d, 21 h)
Engine
blob
Format
Raw Data
Handle
761469
Attached To
rINP In-Portal
in-portal
View Options
Index: branches/5.0.x/core/units/helpers/template_helper.php
===================================================================
--- branches/5.0.x/core/units/helpers/template_helper.php (revision 12892)
+++ branches/5.0.x/core/units/helpers/template_helper.php (revision 12893)
@@ -1,448 +1,459 @@
<?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 TemplateHelper extends kHelper {
/**
* parser element location information
*
* @var Array
*/
var $_blockLocation = Array ();
/**
* Block name, that will be used
*
* @var string
*/
var $_blockName = '';
/**
* Function name, that represents compiled block
*
* @var sting
*/
var $_functionName = '';
/**
* Errors found during template parsing
*
* @var Array
*/
var $_parseErrors = Array ();
/**
* Source template, that is being edited
*
* @var string
*/
var $_sourceTemplate = '';
var $_initMade = false;
/**
* Performs init ot helper
*
* @param kDBItem $object
*/
function InitHelper(&$object)
{
if ($this->_initMade) {
return ;
}
define('DBG_IGNORE_FATAL_ERRORS', 1);
// 1. get block information
$block_info = $this->Application->GetVar('block');
list ($this->_blockName, $this->_functionName) = explode(':', $block_info);
$this->_parseTemplate($object);
if (array_key_exists($this->_functionName, $this->Application->Parser->ElementLocations)) {
$this->_blockLocation = $this->Application->Parser->ElementLocations[$this->_functionName];
}
$this->_initMade = true;
}
function _getSourceTemplate()
{
// get source template
$t = $this->Application->GetVar('source');
if (!$this->Application->TemplatesCache->TemplateExists($t)) {
$cms_handler =& $this->Application->recallObject('st_EventHandler');
/* @var $cms_handler StructureEventHandler */
$t = ltrim($cms_handler->GetDesignTemplate($t), '/');
}
$this->_sourceTemplate = $t;
}
function _getThemeName()
{
$theme_id = (int)$this->Application->GetVar('theme_id');
$sql = 'SELECT Name
FROM ' . $this->Application->getUnitOption('theme', 'TableName') . '
WHERE ' . $this->Application->getUnitOption('theme', 'IDField') . ' = ' . $theme_id;
return $this->Conn->GetOne($sql);
}
/**
* Render source template to get parse errors OR it's element locations
*
* @param kDBItem $object
* @param string $append
* @return bool
*/
function _parseTemplate(&$object, $append = '')
{
// 1. set internal error handler to catch all parsing errors
$error_handlers = $this->Application->errorHandlers;
$this->Application->errorHandlers = Array (
Array (&$this, '_saveError'),
);
// 2. parse template
$this->Application->InitParser( $this->_getThemeName() ); // we have no parser when saving block content
$this->_getSourceTemplate();
// design templates have leading "/" in the beginning
$this->Application->Parser->Run($this->_sourceTemplate . $append);
// 3. restore original error handler
$this->Application->errorHandlers = $error_handlers;
if ($this->_parseErrors) {
if ($this->_isMainTemplate()) {
// 3.1. delete temporary file, that was parsed
$filename = $this->_getTemplateFile(false, $append . '.tpl');
if (!unlink($filename)) {
$error_file = $this->_getTemplateFile(true, $append . '.tpl');
$object->SetError('FileContents', 'template_delete_failed', '+Failed to delete temporary template "<strong>' . $error_file . '</strong>"');
return false;
}
}
else {
// 3.2. restore backup
if (!rename($this->_getTemplateFile(false, '.tpl.bak'), $this->_getTemplateFile(false))) {
$error_file = $this->_getTemplateFile(true);
$object->SetError('FileContents', 'template_restore_failed', '+Failed to restore template "<strong>' . $error_file . '</strong>" from backup.');
return false;
}
}
return false;
}
return true;
}
/**
* Move elements in template and save changes, when possible
*
* @param Array $target_order
* @return bool
*/
function moveTemplateElements($target_order)
{
// 2. parse template
$this->Application->InitParser(); // we have no parser when saving block content
$this->_getSourceTemplate();
$filename = $this->Application->TemplatesCache->GetRealFilename($this->_sourceTemplate) . '.tpl';
if (!is_writable($filename)) {
// we can't save changes, don't bother calculating new template contents
return false;
}
$data = file_get_contents($filename);
$line_ending = strpos($data, "\r") !== false ? "\r\n" : "\n";
// 1. get location of movable areas
$mask = '';
$start_pos = 0;
$elements = Array ();
$areas = $this->_getDivPairs($data, 'movable-area');
foreach ($areas as $area_index => $area) {
// 1.1. get locations of all movable elements inside given area
$area_content = substr($area['data'], $area['open_len'], -$area['close_len']);
$elements = array_merge($elements, $this->_getDivPairs($area_content, 'movable-element', $area_index, $area['open_pos'] + $area['open_len']));
// 1.2. prepare mask to place movable elements into (don't include movable area div ifself)
$mask .= "\t" . substr($data, $start_pos, $area['open_pos'] + $area['open_len'] - $start_pos) . $line_ending . "\t\t" . '#AREA' . $area_index . '#' . $line_ending;
$start_pos = $area['close_pos'] - $area['close_len'];
}
$mask = trim($mask . "\t" . substr($data, $area['close_pos'] - $area['close_len']));
if (!$elements) {
// no elements found
return false;
}
foreach ($areas as $area_index => $area) {
$area_content = '';
$target_elements = $target_order[$area_index];
foreach ($target_order[$area_index] as $old_location) {
$area_content .= $elements[$old_location]['data'] . $line_ending . "\t\t";
}
$mask = str_replace('#AREA' . $area_index . '#', trim($area_content), $mask);
}
$fp = fopen($filename, 'w');
fwrite($fp, $mask);
fclose($fp);
return true;
}
/**
* Extracts div pairs with given class from given text
*
* @param string $data
* @param string $class
* @param int $area
* @param int $offset
* @return Array
*/
function _getDivPairs(&$data, $class, $area = null, $offset = 0)
{
preg_match_all('/(<div[^>]*>)|(<\/div>)/s', $data, $divs, PREG_SET_ORDER + PREG_OFFSET_CAPTURE);
- $skip_count = 0;
+ $deep_level = 0;
+
$pairs = Array ();
+ $skip_count = Array (); // by deep level!
foreach ($divs as $div) {
if (strpos($div[0][0], '/') === false) {
// opening div
+ $skip_count[$deep_level] = 0;
+
if (strpos($div[0][0], $class) !== false) {
+ // ours opening (this deep level) -> save
$pair = Array ('open_pos' => $div[0][1], 'open_len' => strlen($div[0][0]));
- $skip_count = 0;
}
else {
- $skip_count++;
+ // not ours opening -> skip next closing (this deep level)
+ $skip_count[$deep_level]++;
}
+
+ $deep_level++;
}
else {
// closing div
- if ($skip_count == 0) {
+ $deep_level--;
+
+ if ($skip_count[$deep_level] == 0) {
+ // nothing to skip (this deep level) -> save
$pair['close_len'] = strlen($div[0][0]);
$pair['close_pos'] = $div[0][1] + $pair['close_len'];
$pair['data'] = substr($data, $pair['open_pos'], $pair['close_pos'] - $pair['open_pos']);
if (isset($area)) {
$pair['open_pos'] += $offset;
$pair['close_pos'] += $offset;
// index indicates area
$pairs['a' . $area . 'e' . count($pairs)] = $pair;
}
else {
$pairs[] = $pair;
}
}
else {
- $skip_count--;
+ // skip closing div as requested
+ $skip_count[$deep_level]--;
}
}
}
return $pairs;
}
/**
* Returns information about parser element locations in template
*
* @param Array $params
* @return mixed
*/
function blockInfo($info_type)
{
switch ($info_type) {
case 'block_name':
return $this->_blockName;
break;
case 'function_name':
return $this->_functionName;
break;
case 'start_pos':
case 'end_pos':
case 'template':
if (!array_key_exists($info_type, $this->_blockLocation)) {
// invalid block name
return 'invalid block name';
}
return $this->_blockLocation[$info_type];
break;
case 'template_file':
return $this->_getTemplateFile(true);
break;
case 'content':
$template_body = file_get_contents( $this->_getTemplateFile() );
$length = $this->_blockLocation['end_pos'] - $this->_blockLocation['start_pos'];
return substr($template_body, $this->_blockLocation['start_pos'], $length);
break;
}
return 'undefined';
}
/**
* Main template being edited (parse copy, instead of original)
*
* @return bool
*/
function _isMainTemplate()
{
return $this->_blockLocation['template'] == $this->_sourceTemplate;
}
/**
* Returns filename, that contains template, where block is located
*
* @return string
*/
function _getTemplateFile($relative = false, $extension = '.tpl')
{
$filename = $this->Application->TemplatesCache->GetRealFilename( $this->_blockLocation['template'] ) . $extension;
if ($relative) {
$filename = preg_replace('/^' . preg_quote(FULL_PATH, '/') . '/', '', $filename, 1);
}
return $filename;
}
/**
* Saves new version of block to template, where it's located
*
* @param kDBItem $object
*/
function saveBlock(&$object)
{
$main_template = $this->_isMainTemplate();
$filename = $this->_getTemplateFile(false);
// 1. get new template content
$new_template_body = $this->_getNewTemplateContent($object, $filename, $lines_before);
if (is_bool($new_template_body) && ($new_template_body === true)) {
// when nothing changed -> stop processing
return true;
}
// 2. backup original template
if (!$main_template && !copy($filename, $filename . '.bak')) {
// backup failed
$error_file = $this->_getTemplateFile(true, '.tpl.bak');
$object->SetError('FileContents', 'template_backup_failed', '+Failed to create backup template "<strong>' . $error_file . '</strong>" backup.');
return false;
}
// 3. save changed template
$save_filename = $this->_getTemplateFile(false, $main_template ? '.tmp.tpl' : '.tpl');
$fp = fopen($save_filename, 'w');
if (!$fp) {
// backup template create failed OR existing template save
$error_file = $this->_getTemplateFile(true, $main_template ? '.tmp.tpl' : '.tpl');
$object->SetError('FileContents', 'template_changes_save_failed', '+Failed to save template "<strong>' . $error_file . '</strong>" changes.');
return false;
}
fwrite($fp, $new_template_body);
fclose($fp);
// 3. parse template to check for errors
$this->_parseTemplate($object, $main_template ? '.tmp' : '');
if ($this->_parseErrors) {
$error_msg = Array ();
foreach ($this->_parseErrors as $error_data) {
if (preg_match('/line ([\d]+)/', $error_data['msg'], $regs)) {
// another line number inside message -> patch it
$error_data['msg'] = str_replace('line ' . $regs[1], 'line ' . ($regs[1] - $lines_before), $error_data['msg']);
}
$error_msg[] = $error_data['msg'] . ' at line ' . ($error_data['line'] - $lines_before);
}
$object->SetError('FileContents', 'template_syntax_error', '+Template syntax errors:<br/>' . implode('<br/>', $error_msg));
return false;
}
if ($main_template) {
// 4.1. replace original file with temporary
if (!rename($this->_getTemplateFile(false, '.tmp.tpl'), $filename)) {
// failed to save new content to original template
$error_file = $this->_getTemplateFile(true);
$object->SetError('FileContents', 'template_save_failed', '+Failed to save template "<strong>' . $error_file . '</strong>".');
return false;
}
}
else {
// 4.2. delete backup
unlink( $this->_getTemplateFile(false, '.tpl.bak') );
}
return true;
}
/**
* Returns new template content of "true", when nothing is changed
*
* @param kDBItem $object
* @param string $filename
* @param int $lines_before
* @return mixed
*/
function _getNewTemplateContent(&$object, $filename, &$lines_before)
{
$new_content = $object->GetDBField('FileContents');
$template_body = file_get_contents($filename);
$lines_before = substr_count(substr($template_body, 0, $this->_blockLocation['start_pos']), "\n");
$new_template_body = substr($template_body, 0, $this->_blockLocation['start_pos']) .
$new_content .
substr($template_body, $this->_blockLocation['end_pos']);
return crc32($template_body) == crc32($new_template_body) ? true : $new_template_body;
}
function _saveError($errno, $errstr, $errfile, $errline, $errcontext)
{
if ($errno != E_USER_ERROR) {
// ignore all minor errors, except fatals from parser
return true;
}
/*if (defined('E_STRICT') && ($errno == E_STRICT)) {
// always ignore strict errors here (specially when not in debug mode)
return true;
}*/
$this->_parseErrors[] = Array ('msg' => $errstr, 'file' => $errfile, 'line' => $errline);
return true;
}
}
\ No newline at end of file
Event Timeline
Log In to Comment