mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-23 02:28:44 +02:00
Compare commits
1 Commits
support/2.
...
2.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7f9f1b93e |
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2014 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
* Simple web page with no includes, header or fancy formatting, useful to
|
* Simple web page with no includes, header or fancy formatting, useful to
|
||||||
* generate HTML fragments when called by an AJAX method
|
* generate HTML fragments when called by an AJAX method
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -42,10 +42,7 @@ class ajax_page extends WebPage implements iTabbedPage
|
|||||||
*/
|
*/
|
||||||
function __construct($s_title)
|
function __construct($s_title)
|
||||||
{
|
{
|
||||||
$sPrintable = utils::ReadParam('printable', '0');
|
parent::__construct($s_title);
|
||||||
$bPrintable = ($sPrintable == '1');
|
|
||||||
|
|
||||||
parent::__construct($s_title, $bPrintable);
|
|
||||||
$this->m_sReadyScript = "";
|
$this->m_sReadyScript = "";
|
||||||
//$this->add_header("Content-type: text/html; charset=utf-8");
|
//$this->add_header("Content-type: text/html; charset=utf-8");
|
||||||
$this->add_header("Cache-control: no-cache");
|
$this->add_header("Cache-control: no-cache");
|
||||||
@@ -200,7 +197,7 @@ EOF
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Render the tabs in the page (if any)
|
// Render the tabs in the page (if any)
|
||||||
$this->s_content = $this->m_oTabs->RenderIntoContent($this->s_content, $this);
|
$this->s_content = $this->m_oTabs->RenderIntoContent($this->s_content);
|
||||||
|
|
||||||
// Additional UI widgets to be activated inside the ajax fragment ??
|
// Additional UI widgets to be activated inside the ajax fragment ??
|
||||||
if (($this->sContentType == 'text/html') && (preg_match('/class="date-pick"/', $this->s_content) || preg_match('/class="datetime-pick"/', $this->s_content)) )
|
if (($this->sContentType == 'text/html') && (preg_match('/class="date-pick"/', $this->s_content) || preg_match('/class="datetime-pick"/', $this->s_content)) )
|
||||||
@@ -281,9 +278,9 @@ EOF
|
|||||||
echo self::FilterXSS($s_captured_output);
|
echo self::FilterXSS($s_captured_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (class_exists('DBSearch'))
|
if (class_exists('MetaModel'))
|
||||||
{
|
{
|
||||||
DBSearch::RecordQueryTrace();
|
MetaModel::RecordQueryTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2012 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -781,12 +781,8 @@ class RestUtils
|
|||||||
$oSearch = new DBObjectSearch($sClass);
|
$oSearch = new DBObjectSearch($sClass);
|
||||||
foreach ($oCriteria as $sAttCode => $value)
|
foreach ($oCriteria as $sAttCode => $value)
|
||||||
{
|
{
|
||||||
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
$realValue = self::MakeValue($sClass, $sAttCode, $value);
|
||||||
$oSearch->AddCondition($sAttCode, $realValue, '=');
|
$oSearch->AddCondition($sAttCode, $realValue, '=');
|
||||||
if (is_object($value) || is_array($value))
|
|
||||||
{
|
|
||||||
$value = json_encode($value);
|
|
||||||
}
|
|
||||||
$aCriteriaReport[] = "$sAttCode: $value ($realValue)";
|
$aCriteriaReport[] = "$sAttCode: $value ($realValue)";
|
||||||
}
|
}
|
||||||
$oSet = new DBObjectSet($oSearch);
|
$oSet = new DBObjectSet($oSearch);
|
||||||
@@ -818,7 +814,7 @@ class RestUtils
|
|||||||
{
|
{
|
||||||
if (is_object($key))
|
if (is_object($key))
|
||||||
{
|
{
|
||||||
$res = static::FindObjectFromCriteria($sClass, $key);
|
$res = self::FindObjectFromCriteria($sClass, $key);
|
||||||
}
|
}
|
||||||
elseif (is_numeric($key))
|
elseif (is_numeric($key))
|
||||||
{
|
{
|
||||||
@@ -882,7 +878,7 @@ class RestUtils
|
|||||||
$oSearch = new DBObjectSearch($sClass);
|
$oSearch = new DBObjectSearch($sClass);
|
||||||
foreach ($key as $sAttCode => $value)
|
foreach ($key as $sAttCode => $value)
|
||||||
{
|
{
|
||||||
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
$realValue = self::MakeValue($sClass, $sAttCode, $value);
|
||||||
$oSearch->AddCondition($sAttCode, $realValue, '=');
|
$oSearch->AddCondition($sAttCode, $realValue, '=');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -926,7 +922,7 @@ class RestUtils
|
|||||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||||
if ($oAttDef instanceof AttributeExternalKey)
|
if ($oAttDef instanceof AttributeExternalKey)
|
||||||
{
|
{
|
||||||
$oExtKeyObject = static::FindObjectFromKey($oAttDef->GetTargetClass(), $value, true /* allow null */);
|
$oExtKeyObject = self::FindObjectFromKey($oAttDef->GetTargetClass(), $value, true /* allow null */);
|
||||||
$value = ($oExtKeyObject != null) ? $oExtKeyObject->GetKey() : 0;
|
$value = ($oExtKeyObject != null) ? $oExtKeyObject->GetKey() : 0;
|
||||||
}
|
}
|
||||||
elseif ($oAttDef instanceof AttributeLinkedSet)
|
elseif ($oAttDef instanceof AttributeLinkedSet)
|
||||||
@@ -939,7 +935,7 @@ class RestUtils
|
|||||||
$aLinks = array();
|
$aLinks = array();
|
||||||
foreach($value as $oValues)
|
foreach($value as $oValues)
|
||||||
{
|
{
|
||||||
$oLnk = static::MakeObjectFromFields($sLnkClass, $oValues);
|
$oLnk = self::MakeObjectFromFields($sLnkClass, $oValues);
|
||||||
$aLinks[] = $oLnk;
|
$aLinks[] = $oLnk;
|
||||||
}
|
}
|
||||||
$value = DBObjectSet::FromArray($sLnkClass, $aLinks);
|
$value = DBObjectSet::FromArray($sLnkClass, $aLinks);
|
||||||
@@ -970,7 +966,7 @@ class RestUtils
|
|||||||
$oObject = MetaModel::NewObject($sClass);
|
$oObject = MetaModel::NewObject($sClass);
|
||||||
foreach ($aFields as $sAttCode => $value)
|
foreach ($aFields as $sAttCode => $value)
|
||||||
{
|
{
|
||||||
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
$realValue = self::MakeValue($sClass, $sAttCode, $value);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$oObject->Set($sAttCode, $realValue);
|
$oObject->Set($sAttCode, $realValue);
|
||||||
@@ -997,7 +993,7 @@ class RestUtils
|
|||||||
$sClass = get_class($oObject);
|
$sClass = get_class($oObject);
|
||||||
foreach ($aFields as $sAttCode => $value)
|
foreach ($aFields as $sAttCode => $value)
|
||||||
{
|
{
|
||||||
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
$realValue = self::MakeValue($sClass, $sAttCode, $value);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$oObject->Set($sAttCode, $realValue);
|
$oObject->Set($sAttCode, $realValue);
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class AuditCategory extends cmdbAbstractObject
|
|||||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("description"=>"Short name for this category", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeString("name", array("description"=>"Short name for this category", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||||
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||||
MetaModel::Init_AddAttribute(new AttributeOQL("definition_set", array("allowed_values"=>null, "sql"=>"definition_set", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeOQL("definition_set", array("allowed_values"=>null, "sql"=>"definition_set", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||||
MetaModel::Init_AddAttribute(new AttributeLinkedSet("rules_list", array("linked_class"=>"AuditRule", "ext_key_to_me"=>"category_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array(), "edit_mode" => LINKSET_EDITMODE_INPLACE, "tracking_level" => LINKSET_TRACKING_ALL)));
|
MetaModel::Init_AddAttribute(new AttributeLinkedSet("rules_list", array("linked_class"=>"AuditRule", "ext_key_to_me"=>"category_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array(), "edit_mode" => LINKSET_EDITMODE_INPLACE)));
|
||||||
|
|
||||||
// Display lists
|
// Display lists
|
||||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'definition_set', 'rules_list')); // Attributes to be displayed for the complete details
|
MetaModel::Init_SetZListItems('details', array('name', 'description', 'definition_set', 'rules_list')); // Attributes to be displayed for the complete details
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2012 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
* CLI page
|
* CLI page
|
||||||
* The page adds the content-type text/XML and the encoding into the headers
|
* The page adds the content-type text/XML and the encoding into the headers
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -35,9 +35,9 @@ class CLIPage implements Page
|
|||||||
|
|
||||||
public function output()
|
public function output()
|
||||||
{
|
{
|
||||||
if (class_exists('DBSearch'))
|
if (class_exists('MetaModel'))
|
||||||
{
|
{
|
||||||
DBSearch::RecordQueryTrace();
|
MetaModel::RecordQueryTrace();
|
||||||
}
|
}
|
||||||
if (class_exists('ExecutionKPI'))
|
if (class_exists('ExecutionKPI'))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2013 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
* Abstract class that implements some common and useful methods for displaying
|
* Abstract class that implements some common and useful methods for displaying
|
||||||
* the objects
|
* the objects
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -32,8 +32,6 @@ define('HILIGHT_CLASS_WARNING', 'orange');
|
|||||||
define('HILIGHT_CLASS_OK', 'green');
|
define('HILIGHT_CLASS_OK', 'green');
|
||||||
define('HILIGHT_CLASS_NONE', '');
|
define('HILIGHT_CLASS_NONE', '');
|
||||||
|
|
||||||
define('MIN_WATCHDOG_INTERVAL', 15); // Minimum interval for the watchdog: 15s
|
|
||||||
|
|
||||||
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
|
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
|
||||||
require_once(APPROOT.'/application/applicationextension.inc.php');
|
require_once(APPROOT.'/application/applicationextension.inc.php');
|
||||||
require_once(APPROOT.'/application/utils.inc.php');
|
require_once(APPROOT.'/application/utils.inc.php');
|
||||||
@@ -62,39 +60,6 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
|||||||
{
|
{
|
||||||
return 'UI.php';
|
return 'UI.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function ReloadAndDisplay($oPage, $oObj, $aParams)
|
|
||||||
{
|
|
||||||
$oAppContext = new ApplicationContext();
|
|
||||||
// Reload the page to let the "calling" page execute its 'onunload' method.
|
|
||||||
// Note 1: The redirection MUST NOT be made via an HTTP "header" since onunload is only called when the actual content of the DOM
|
|
||||||
// is replaced by some other content. So the "bouncing" page must provide some content (in our case a script making the redirection).
|
|
||||||
// Note 2: make sure that the URL below is different from the one of the "Modify" button, otherwise the button will have no effect. This is why we add "&a=1" at the end !!!
|
|
||||||
// Note 3: we use the toggle of a flag in the sessionStorage object to prevent an infinite loop of reloads in case the object is actually locked by another window
|
|
||||||
$sSessionStorageKey = get_class($oObj).'_'.$oObj->GetKey();
|
|
||||||
$sParams = '';
|
|
||||||
foreach($aParams as $sName => $value)
|
|
||||||
{
|
|
||||||
$sParams .= $sName.'='.urlencode($value).'&'; // Always add a trailing &
|
|
||||||
}
|
|
||||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/'.$oObj->GetUIPage().'?'.$sParams.'class='.get_class($oObj).'&id='.$oObj->getKey().'&'.$oAppContext->GetForLink().'&a=1';
|
|
||||||
$oPage->add_script(
|
|
||||||
<<<EOF
|
|
||||||
if (!sessionStorage.getItem('$sSessionStorageKey'))
|
|
||||||
{
|
|
||||||
sessionStorage.setItem('$sSessionStorageKey', 1);
|
|
||||||
window.location.href= "$sUrl";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sessionStorage.removeItem('$sSessionStorageKey');
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
);
|
|
||||||
|
|
||||||
$oObj->Reload();
|
|
||||||
$oObj->DisplayDetails($oPage, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a message diplayed to the end-user next time this object will be displayed
|
* Set a message diplayed to the end-user next time this object will be displayed
|
||||||
@@ -125,156 +90,136 @@ EOF
|
|||||||
'message' => $sMessage
|
'message' => $sMessage
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function DisplayBareHeader(WebPage $oPage, $bEditMode = false)
|
function DisplayBareHeader(WebPage $oPage, $bEditMode = false)
|
||||||
{
|
{
|
||||||
// Standard Header with name, actions menu and history block
|
// Standard Header with name, actions menu and history block
|
||||||
//
|
//
|
||||||
|
|
||||||
if (!$oPage->IsPrintableVersion())
|
// Is there a message for this object ??
|
||||||
|
$sMessageKey = get_class($this).'::'.$this->GetKey();
|
||||||
|
if (array_key_exists('obj_messages', $_SESSION) && array_key_exists($sMessageKey, $_SESSION['obj_messages']))
|
||||||
{
|
{
|
||||||
// Is there a message for this object ??
|
|
||||||
$aMessages = array();
|
$aMessages = array();
|
||||||
$aRanks = array();
|
$aRanks = array();
|
||||||
if (MetaModel::GetConfig()->Get('concurrent_lock_enabled'))
|
foreach ($_SESSION['obj_messages'][$sMessageKey] as $sMessageId => $aMessageData)
|
||||||
{
|
{
|
||||||
$aLockInfo = iTopOwnershipLock::IsLocked(get_class($this), $this->GetKey());
|
$sMsgClass = 'message_'.$aMessageData['severity'];
|
||||||
if ($aLockInfo['locked'])
|
$aMessages[] = "<div class=\"header_message $sMsgClass\">".$aMessageData['message']."</div>";
|
||||||
{
|
$aRanks[] = $aMessageData['rank'];
|
||||||
$aRanks[] = 0;
|
|
||||||
$sName = $aLockInfo['owner']->GetName();
|
|
||||||
if ($aLockInfo['owner']->Get('contactid') != 0)
|
|
||||||
{
|
|
||||||
$sName .= ' ('.$aLockInfo['owner']->Get('contactid_friendlyname').')';
|
|
||||||
}
|
|
||||||
$aResult['message'] = Dict::Format('UI:CurrentObjectIsLockedBy_User', $sName); $aMessages[] = "<div class=\"header_message message_error\">".Dict::Format('UI:CurrentObjectIsLockedBy_User', $sName)."</div>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$sMessageKey = get_class($this).'::'.$this->GetKey();
|
|
||||||
if (array_key_exists('obj_messages', $_SESSION) && array_key_exists($sMessageKey, $_SESSION['obj_messages']))
|
|
||||||
{
|
|
||||||
foreach ($_SESSION['obj_messages'][$sMessageKey] as $sMessageId => $aMessageData)
|
|
||||||
{
|
|
||||||
$sMsgClass = 'message_'.$aMessageData['severity'];
|
|
||||||
$aMessages[] = "<div class=\"header_message $sMsgClass\">".$aMessageData['message']."</div>";
|
|
||||||
$aRanks[] = $aMessageData['rank'];
|
|
||||||
}
|
|
||||||
unset($_SESSION['obj_messages'][$sMessageKey]);
|
|
||||||
}
|
}
|
||||||
array_multisort($aRanks, $aMessages);
|
array_multisort($aRanks, $aMessages);
|
||||||
foreach ($aMessages as $sMessage)
|
foreach ($aMessages as $sMessage)
|
||||||
{
|
{
|
||||||
$oPage->add($sMessage);
|
$oPage->add($sMessage);
|
||||||
}
|
}
|
||||||
|
unset($_SESSION['obj_messages'][$sMessageKey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$oPage->IsPrintableVersion())
|
// action menu
|
||||||
{
|
$oSingletonFilter = new DBObjectSearch(get_class($this));
|
||||||
// action menu
|
$oSingletonFilter->AddCondition('id', $this->GetKey(), '=');
|
||||||
$oSingletonFilter = new DBObjectSearch(get_class($this));
|
$oBlock = new MenuBlock($oSingletonFilter, 'details', false);
|
||||||
$oSingletonFilter->AddCondition('id', $this->GetKey(), '=');
|
$oBlock->Display($oPage, -1);
|
||||||
$oBlock = new MenuBlock($oSingletonFilter, 'details', false);
|
|
||||||
$oBlock->Display($oPage, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$oPage->add("<div class=\"page_header\"><h1>".$this->GetIcon()." \n");
|
|
||||||
|
|
||||||
// Master data sources
|
// Master data sources
|
||||||
|
$sSynchroIcon = '';
|
||||||
$bSynchronized = false;
|
$bSynchronized = false;
|
||||||
$aIcons = array();
|
$oCreatorTask = null;
|
||||||
if (!$oPage->IsPrintableVersion())
|
$bCanBeDeletedByTask = false;
|
||||||
|
$bCanBeDeletedByUser = true;
|
||||||
|
$aMasterSources = array();
|
||||||
|
$aSyncData = $this->GetSynchroData();
|
||||||
|
if (count($aSyncData) > 0)
|
||||||
{
|
{
|
||||||
$oCreatorTask = null;
|
$bSynchronized = true;
|
||||||
$bCanBeDeletedByTask = false;
|
foreach ($aSyncData as $iSourceId => $aSourceData)
|
||||||
$bCanBeDeletedByUser = true;
|
|
||||||
$aMasterSources = array();
|
|
||||||
$aSyncData = $this->GetSynchroData();
|
|
||||||
if (count($aSyncData) > 0)
|
|
||||||
{
|
{
|
||||||
$bSynchronized = true;
|
$oDataSource = $aSourceData['source'];
|
||||||
foreach ($aSyncData as $iSourceId => $aSourceData)
|
$oReplica = reset($aSourceData['replica']); // Take the first one!
|
||||||
|
|
||||||
|
$sApplicationURL = $oDataSource->GetApplicationUrl($this, $oReplica);
|
||||||
|
$sLink = $oDataSource->GetName();
|
||||||
|
if (!empty($sApplicationURL))
|
||||||
{
|
{
|
||||||
$oDataSource = $aSourceData['source'];
|
$sLink = "<a href=\"$sApplicationURL\" target=\"_blank\">".$oDataSource->GetName()."</a>";
|
||||||
$oReplica = reset($aSourceData['replica']); // Take the first one!
|
|
||||||
|
|
||||||
$sApplicationURL = $oDataSource->GetApplicationUrl($this, $oReplica);
|
|
||||||
$sLink = $oDataSource->GetName();
|
|
||||||
if (!empty($sApplicationURL))
|
|
||||||
{
|
|
||||||
$sLink = "<a href=\"$sApplicationURL\" target=\"_blank\">".$oDataSource->GetName()."</a>";
|
|
||||||
}
|
|
||||||
if ($oReplica->Get('status_dest_creator') == 1)
|
|
||||||
{
|
|
||||||
$oCreatorTask = $oDataSource;
|
|
||||||
$bCreatedByTask = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$bCreatedByTask = false;
|
|
||||||
}
|
|
||||||
if ($bCreatedByTask)
|
|
||||||
{
|
|
||||||
$sDeletePolicy = $oDataSource->Get('delete_policy');
|
|
||||||
if (($sDeletePolicy == 'delete') || ($sDeletePolicy == 'update_then_delete'))
|
|
||||||
{
|
|
||||||
$bCanBeDeletedByTask = true;
|
|
||||||
}
|
|
||||||
$sUserDeletePolicy = $oDataSource->Get('user_delete_policy');
|
|
||||||
if ($sUserDeletePolicy == 'nobody')
|
|
||||||
{
|
|
||||||
$bCanBeDeletedByUser = false;
|
|
||||||
}
|
|
||||||
elseif (($sUserDeletePolicy == 'administrators') && !UserRights::IsAdministrator())
|
|
||||||
{
|
|
||||||
$bCanBeDeletedByUser = false;
|
|
||||||
}
|
|
||||||
else // everybody...
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$aMasterSources[$iSourceId]['datasource'] = $oDataSource;
|
|
||||||
$aMasterSources[$iSourceId]['url'] = $sLink;
|
|
||||||
$aMasterSources[$iSourceId]['last_synchro'] = $oReplica->Get('status_last_seen');
|
|
||||||
}
|
}
|
||||||
|
if ($oReplica->Get('status_dest_creator') == 1)
|
||||||
if (is_object($oCreatorTask))
|
|
||||||
{
|
{
|
||||||
$sTaskUrl = $aMasterSources[$oCreatorTask->GetKey()]['url'];
|
$oCreatorTask = $oDataSource;
|
||||||
if (!$bCanBeDeletedByUser)
|
$bCreatedByTask = true;
|
||||||
{
|
|
||||||
$sTip = "<p>".Dict::Format('Core:Synchro:TheObjectCannotBeDeletedByUser_Source', $sTaskUrl)."</p>";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sTip = "<p>".Dict::Format('Core:Synchro:TheObjectWasCreatedBy_Source', $sTaskUrl)."</p>";
|
|
||||||
}
|
|
||||||
if ($bCanBeDeletedByTask)
|
|
||||||
{
|
|
||||||
$sTip .= "<p>".Dict::Format('Core:Synchro:TheObjectCanBeDeletedBy_Source', $sTaskUrl)."</p>";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$sTip = "<p>".Dict::S('Core:Synchro:ThisObjectIsSynchronized')."</p>";
|
$bCreatedByTask = false;
|
||||||
}
|
}
|
||||||
|
if ($bCreatedByTask)
|
||||||
$sTip .= "<p><b>".Dict::S('Core:Synchro:ListOfDataSources')."</b></p>";
|
|
||||||
foreach($aMasterSources as $aStruct)
|
|
||||||
{
|
{
|
||||||
$oDataSource = $aStruct['datasource'];
|
$sDeletePolicy = $oDataSource->Get('delete_policy');
|
||||||
$sLink = $aStruct['url'];
|
if (($sDeletePolicy == 'delete') || ($sDeletePolicy == 'update_then_delete'))
|
||||||
$sTip .= "<p style=\"white-space:nowrap\">".$oDataSource->GetIcon(true, 'style="vertical-align:middle"')." $sLink<br/>";
|
{
|
||||||
$sTip .= Dict::S('Core:Synchro:LastSynchro').'<br/>'.$aStruct['last_synchro']."</p>";
|
$bCanBeDeletedByTask = true;
|
||||||
|
}
|
||||||
|
$sUserDeletePolicy = $oDataSource->Get('user_delete_policy');
|
||||||
|
if ($sUserDeletePolicy == 'nobody')
|
||||||
|
{
|
||||||
|
$bCanBeDeletedByUser = false;
|
||||||
|
}
|
||||||
|
elseif (($sUserDeletePolicy == 'administrators') && !UserRights::IsAdministrator())
|
||||||
|
{
|
||||||
|
$bCanBeDeletedByUser = false;
|
||||||
|
}
|
||||||
|
else // everybody...
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$aIcons[] = ' <img style="vertical-align:middle;" id="synchro_icon" src="../images/locked.png"/>';
|
$aMasterSources[$iSourceId]['datasource'] = $oDataSource;
|
||||||
$sTip = addslashes($sTip);
|
$aMasterSources[$iSourceId]['url'] = $sLink;
|
||||||
$oPage->add_ready_script("$('#synchro_icon').qtip( { content: '$sTip', show: 'mouseover', hide: { fixed: true }, style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
|
$aMasterSources[$iSourceId]['last_synchro'] = $oReplica->Get('status_last_seen');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_object($oCreatorTask))
|
||||||
|
{
|
||||||
|
$sTaskUrl = $aMasterSources[$oCreatorTask->GetKey()]['url'];
|
||||||
|
if (!$bCanBeDeletedByUser)
|
||||||
|
{
|
||||||
|
$sTip = "<p>".Dict::Format('Core:Synchro:TheObjectCannotBeDeletedByUser_Source', $sTaskUrl)."</p>";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$sTip = "<p>".Dict::Format('Core:Synchro:TheObjectWasCreatedBy_Source', $sTaskUrl)."</p>";
|
||||||
|
}
|
||||||
|
if ($bCanBeDeletedByTask)
|
||||||
|
{
|
||||||
|
$sTip .= "<p>".Dict::Format('Core:Synchro:TheObjectCanBeDeletedBy_Source', $sTaskUrl)."</p>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$sTip = "<p>".Dict::S('Core:Synchro:ThisObjectIsSynchronized')."</p>";
|
||||||
|
}
|
||||||
|
|
||||||
|
$sTip .= "<p><b>".Dict::S('Core:Synchro:ListOfDataSources')."</b></p>";
|
||||||
|
foreach($aMasterSources as $aStruct)
|
||||||
|
{
|
||||||
|
$oDataSource = $aStruct['datasource'];
|
||||||
|
$sLink = $aStruct['url'];
|
||||||
|
$sTip .= "<p style=\"white-space:nowrap\">".$oDataSource->GetIcon(true, 'style="vertical-align:middle"')." $sLink<br/>";
|
||||||
|
$sTip .= Dict::S('Core:Synchro:LastSynchro').'<br/>'.$aStruct['last_synchro']."</p>";
|
||||||
|
}
|
||||||
|
$sSynchroIcon = ' <img style="vertical-align:middle;" id="synchro_icon" src="../images/locked.png"/>';
|
||||||
|
$sTip = addslashes($sTip);
|
||||||
|
$oPage->add_ready_script("$('#synchro_icon').qtip( { content: '$sTip', show: 'mouseover', hide: { fixed: true }, style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
|
||||||
}
|
}
|
||||||
|
|
||||||
$sIcons = implode(' ', $aIcons);
|
$oPage->add("<div class=\"page_header\"><h1>".$this->GetIcon()." \n");
|
||||||
$oPage->add(MetaModel::GetName(get_class($this)).": <span class=\"hilite\">".$this->GetName()."</span>$sIcons</h1>\n");
|
$sRefreshIcon = '';
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'GET')
|
||||||
|
{
|
||||||
|
$sRefreshIcon = '<img src="../images/reload.png" style="cursor:pointer;vertical-align:middle;margin-left:1em;" onclick="window.location.reload();" title="'.htmlentities(Dict::S('UI:Button:Refresh'), ENT_QUOTES, 'UTF-8').'"/>';
|
||||||
|
}
|
||||||
|
$oPage->add(MetaModel::GetName(get_class($this)).": <span class=\"hilite\">".$this->GetName()."</span>$sRefreshIcon $sSynchroIcon</h1>\n");
|
||||||
$oPage->add("</div>\n");
|
$oPage->add("</div>\n");
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -302,7 +247,7 @@ EOF
|
|||||||
$oExtensionInstance->OnDisplayProperties($this, $oPage, $bEditMode);
|
$oExtensionInstance->OnDisplayProperties($this, $oPage, $bEditMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special case to display the case log, if any...
|
// Special case to display the case log, if any...
|
||||||
// WARNING: if you modify the loop below, also check the corresponding code in UpdateObject and DisplayModifyForm
|
// WARNING: if you modify the loop below, also check the corresponding code in UpdateObject and DisplayModifyForm
|
||||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
|
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
|
||||||
@@ -330,8 +275,6 @@ EOF
|
|||||||
|
|
||||||
function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
|
function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
|
||||||
{
|
{
|
||||||
$aRedundancySettings = $this->FindVisibleRedundancySettings();
|
|
||||||
|
|
||||||
// Related objects: display all the linkset attributes, each as a separate tab
|
// Related objects: display all the linkset attributes, each as a separate tab
|
||||||
// In the order described by the 'display' ZList
|
// In the order described by the 'display' ZList
|
||||||
$aList = $this->FlattenZList(MetaModel::GetZListItems(get_class($this), 'details'));
|
$aList = $this->FlattenZList(MetaModel::GetZListItems(get_class($this), 'details'));
|
||||||
@@ -397,7 +340,6 @@ EOF
|
|||||||
// Non-readable/hidden linkedset... don't display anything
|
// Non-readable/hidden linkedset... don't display anything
|
||||||
if ($iFlags & OPT_ATT_HIDDEN) continue;
|
if ($iFlags & OPT_ATT_HIDDEN) continue;
|
||||||
|
|
||||||
$aArgs = array('this' => $this);
|
|
||||||
$bReadOnly = ($iFlags & (OPT_ATT_READONLY|OPT_ATT_SLAVE));
|
$bReadOnly = ($iFlags & (OPT_ATT_READONLY|OPT_ATT_SLAVE));
|
||||||
if ($bEditMode && (!$bReadOnly))
|
if ($bEditMode && (!$bReadOnly))
|
||||||
{
|
{
|
||||||
@@ -417,6 +359,7 @@ EOF
|
|||||||
|
|
||||||
$oValue = $this->Get($sAttCode);
|
$oValue = $this->Get($sAttCode);
|
||||||
$sDisplayValue = ''; // not used
|
$sDisplayValue = ''; // not used
|
||||||
|
$aArgs = array('this' => $this);
|
||||||
$sHTMLValue = "<span id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $oValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).'</span>';
|
$sHTMLValue = "<span id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $oValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).'</span>';
|
||||||
$this->AddToFieldsMap($sAttCode, $sInputId);
|
$this->AddToFieldsMap($sAttCode, $sInputId);
|
||||||
$oPage->add($sHTMLValue);
|
$oPage->add($sHTMLValue);
|
||||||
@@ -468,29 +411,6 @@ EOF
|
|||||||
$oBlock = new DisplayBlock($this->Get($sAttCode)->GetFilter(), 'list', false);
|
$oBlock = new DisplayBlock($this->Get($sAttCode)->GetFilter(), 'list', false);
|
||||||
$oBlock->Display($oPage, 'rel_'.$sAttCode, $aParams);
|
$oBlock->Display($oPage, 'rel_'.$sAttCode, $aParams);
|
||||||
}
|
}
|
||||||
if (array_key_exists($sAttCode, $aRedundancySettings))
|
|
||||||
{
|
|
||||||
foreach ($aRedundancySettings[$sAttCode] as $oRedundancyAttDef)
|
|
||||||
{
|
|
||||||
$sRedundancyAttCode = $oRedundancyAttDef->GetCode();
|
|
||||||
$sValue = $this->Get($sRedundancyAttCode);
|
|
||||||
$iRedundancyFlags = $this->GetFormAttributeFlags($sRedundancyAttCode);
|
|
||||||
$bRedundancyReadOnly = ($iRedundancyFlags & (OPT_ATT_READONLY|OPT_ATT_SLAVE));
|
|
||||||
|
|
||||||
$oPage->add('<fieldset>');
|
|
||||||
$oPage->add('<legend>'.$oRedundancyAttDef->GetLabel().'</legend>');
|
|
||||||
if ($bEditMode && (!$bRedundancyReadOnly))
|
|
||||||
{
|
|
||||||
$sInputId = $this->m_iFormId.'_'.$sRedundancyAttCode;
|
|
||||||
$oPage->add("<span id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage, $sClass, $sRedundancyAttCode, $oRedundancyAttDef, $sValue, '', $sInputId, '', $iFlags, $aArgs).'</span>');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$oPage->add($oRedundancyAttDef->GetDisplayForm($sValue, $oPage, false, $this->m_iFormId));
|
|
||||||
}
|
|
||||||
$oPage->add('</fieldset>');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$oPage->SetCurrentTab('');
|
$oPage->SetCurrentTab('');
|
||||||
|
|
||||||
@@ -607,7 +527,18 @@ EOF
|
|||||||
|
|
||||||
$sComments = isset($aFieldsComments[$sAttCode]) ? $aFieldsComments[$sAttCode] : ' ';
|
$sComments = isset($aFieldsComments[$sAttCode]) ? $aFieldsComments[$sAttCode] : ' ';
|
||||||
$sInfos = ' ';
|
$sInfos = ' ';
|
||||||
$iFlags = $this->GetFormAttributeFlags($sAttCode);
|
if ($this->IsNew())
|
||||||
|
{
|
||||||
|
$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$iFlags = $this->GetAttributeFlags($sAttCode);
|
||||||
|
}
|
||||||
|
if (($iFlags & OPT_ATT_MANDATORY) && $this->IsNew())
|
||||||
|
{
|
||||||
|
$iFlags = $iFlags & ~OPT_ATT_READONLY; // Mandatory fields cannot be read-only when creating an object
|
||||||
|
}
|
||||||
if (array_key_exists($sAttCode, $aExtraFlags))
|
if (array_key_exists($sAttCode, $aExtraFlags))
|
||||||
{
|
{
|
||||||
// the caller may override some flags if needed
|
// the caller may override some flags if needed
|
||||||
@@ -762,37 +693,6 @@ EOF
|
|||||||
{
|
{
|
||||||
$oPage->add(self::GetDisplaySet($oPage, $oSet, $aExtraParams));
|
$oPage->add(self::GetDisplaySet($oPage, $oSet, $aExtraParams));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Simplifed version of GetDisplaySet() with less "decoration" around the table (and no paging)
|
|
||||||
* that fits better into a printed document (like a PDF or a printable view)
|
|
||||||
* @param WebPage $oPage
|
|
||||||
* @param DBObjectSet $oSet
|
|
||||||
* @param hash $aExtraParams
|
|
||||||
* @return string The HTML representation of the table
|
|
||||||
*/
|
|
||||||
public static function GetDisplaySetForPrinting(WebPage $oPage, DBObjectSet $oSet, $aExtraParams = array())
|
|
||||||
{
|
|
||||||
$iListId = empty($aExtraParams['currentId']) ? $oPage->GetUniqueId() : $aExtraParams['currentId'];
|
|
||||||
$sTableId = isset($aExtraParams['table_id']) ? $aExtraParams['table_id'] : null;
|
|
||||||
|
|
||||||
$bViewLink = true;
|
|
||||||
$sSelectMode = 'none';
|
|
||||||
$iListId = $sTableId;
|
|
||||||
$sClassAlias = $oSet->GetClassAlias();
|
|
||||||
$sClassName = $oSet->GetClass();
|
|
||||||
$sZListName = 'list';
|
|
||||||
$aClassAliases = array( $sClassAlias => $sClassName);
|
|
||||||
$aList = cmdbAbstractObject::FlattenZList(MetaModel::GetZListItems($sClassName, $sZListName));
|
|
||||||
|
|
||||||
$oDataTable = new PrintableDataTable($iListId, $oSet, $aClassAliases, $sTableId);
|
|
||||||
$oSettings = DataTableSettings::GetDataModelSettings($aClassAliases, $bViewLink, array($sClassAlias => $aList));
|
|
||||||
$oSettings->iDefaultPageSize = 0;
|
|
||||||
$oSettings->aSortOrder = MetaModel::GetOrderByDefault($sClassName);
|
|
||||||
|
|
||||||
return $oDataTable->Display($oPage, $oSettings, false /* $bDisplayMenu */, $sSelectMode, $bViewLink, $aExtraParams);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the HTML fragment corresponding to the display of a table representing a set of objects
|
* Get the HTML fragment corresponding to the display of a table representing a set of objects
|
||||||
@@ -803,11 +703,6 @@ EOF
|
|||||||
*/
|
*/
|
||||||
public static function GetDisplaySet(WebPage $oPage, CMDBObjectSet $oSet, $aExtraParams = array())
|
public static function GetDisplaySet(WebPage $oPage, CMDBObjectSet $oSet, $aExtraParams = array())
|
||||||
{
|
{
|
||||||
if ($oPage->IsPrintableVersion() || $oPage->is_pdf())
|
|
||||||
{
|
|
||||||
return self::GetDisplaySetForPrinting($oPage, $oSet, $aExtraParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($aExtraParams['currentId']))
|
if (empty($aExtraParams['currentId']))
|
||||||
{
|
{
|
||||||
$iListId = $oPage->GetUniqueId(); // Works only if not in an Ajax page !!
|
$iListId = $oPage->GetUniqueId(); // Works only if not in an Ajax page !!
|
||||||
@@ -1507,7 +1402,14 @@ EOF
|
|||||||
$sHtml .= "<h2>".Dict::Format('UI:SearchFor_Class_Objects', $sClassesCombo)."</h2>\n";
|
$sHtml .= "<h2>".Dict::Format('UI:SearchFor_Class_Objects', $sClassesCombo)."</h2>\n";
|
||||||
$index = 0;
|
$index = 0;
|
||||||
$sHtml .= "<p>\n";
|
$sHtml .= "<p>\n";
|
||||||
|
$aFilterCriteria = $oSet->GetFilter()->GetCriteria();
|
||||||
$aMapCriteria = array();
|
$aMapCriteria = array();
|
||||||
|
// Todo: Investigate... The search criteria is an expression, i.e. a tree!
|
||||||
|
// I wonder if that code could work... cleanup required/recommended
|
||||||
|
foreach($aFilterCriteria as $aCriteria)
|
||||||
|
{
|
||||||
|
$aMapCriteria[$aCriteria['filtercode']][] = array('value' => $aCriteria['value'], 'opcode' => $aCriteria['opcode']);
|
||||||
|
}
|
||||||
$aList = MetaModel::GetZListItems($sClassName, 'standard_search');
|
$aList = MetaModel::GetZListItems($sClassName, 'standard_search');
|
||||||
$aConsts = $oSet->ListConstantFields(); // Some fields are constants based on the query/context
|
$aConsts = $oSet->ListConstantFields(); // Some fields are constants based on the query/context
|
||||||
$sClassAlias = $oSet->GetFilter()->GetClassAlias();
|
$sClassAlias = $oSet->GetFilter()->GetClassAlias();
|
||||||
@@ -1620,7 +1522,7 @@ EOF
|
|||||||
{
|
{
|
||||||
if (is_scalar($sValue))
|
if (is_scalar($sValue))
|
||||||
{
|
{
|
||||||
$sHtml .= "<input type=\"hidden\" name=\"$sName\" value=\"".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."\" />\n";
|
$sHtml .= "<input type=\"hidden\" name=\"$sName\" value=\"$sValue\" />\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$sHtml .= "<input type=\"hidden\" name=\"class\" value=\"$sClassName\" />\n";
|
$sHtml .= "<input type=\"hidden\" name=\"class\" value=\"$sClassName\" />\n";
|
||||||
@@ -1830,7 +1732,7 @@ EOF
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'HTML':
|
case 'HTML':
|
||||||
$oWidget = new UIHTMLEditorWidget($iId, $oAttDef, $sNameSuffix, $sFieldPrefix, $sHelpText, $sValidationField, $value, $bMandatory);
|
$oWidget = new UIHTMLEditorWidget($iId, $sAttCode, $sNameSuffix, $sFieldPrefix, $sHelpText, $sValidationField, $value, $bMandatory);
|
||||||
$sHTMLValue = $oWidget->Display($oPage, $aArgs);
|
$sHTMLValue = $oWidget->Display($oPage, $aArgs);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1894,20 +1796,6 @@ EOF
|
|||||||
$sHTMLValue .= "<!-- iFlags: $iFlags bMandatory: $bMandatory -->\n";
|
$sHTMLValue .= "<!-- iFlags: $iFlags bMandatory: $bMandatory -->\n";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'RedundancySetting':
|
|
||||||
$sHTMLValue = '<table>';
|
|
||||||
$sHTMLValue .= '<tr>';
|
|
||||||
$sHTMLValue .= '<td>';
|
|
||||||
$sHTMLValue .= '<div id="'.$iId.'">';
|
|
||||||
$sHTMLValue .= $oAttDef->GetDisplayForm($value, $oPage, true);
|
|
||||||
$sHTMLValue .= '</div>';
|
|
||||||
$sHTMLValue .= '</td>';
|
|
||||||
$sHTMLValue .= '<td>'.$sValidationField.'</td>';
|
|
||||||
$sHTMLValue .= '</tr>';
|
|
||||||
$sHTMLValue .= '</table>';
|
|
||||||
$oPage->add_ready_script("$('#$iId :input').bind('keyup change validate', function(evt, sFormId) { return ValidateRedundancySettings('$iId',sFormId); } );"); // Custom validation function
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'String':
|
case 'String':
|
||||||
default:
|
default:
|
||||||
$aEventsList[] ='validate';
|
$aEventsList[] ='validate';
|
||||||
@@ -1984,53 +1872,6 @@ EOF
|
|||||||
|
|
||||||
public function DisplayModifyForm(WebPage $oPage, $aExtraParams = array())
|
public function DisplayModifyForm(WebPage $oPage, $aExtraParams = array())
|
||||||
{
|
{
|
||||||
$sOwnershipToken = null;
|
|
||||||
$iKey = $this->GetKey();
|
|
||||||
$sClass = get_class($this);
|
|
||||||
if ($iKey > 0)
|
|
||||||
{
|
|
||||||
// The concurrent access lock makes sense only for already existing objects
|
|
||||||
$LockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled');
|
|
||||||
if ($LockEnabled)
|
|
||||||
{
|
|
||||||
$sOwnershipToken = utils::ReadPostedParam('ownership_token', null, false, 'raw_data');
|
|
||||||
if ($sOwnershipToken !== null)
|
|
||||||
{
|
|
||||||
// We're probably inside something like "apply_modify" where the validation failed and we must prompt the user again to edit the object
|
|
||||||
// let's extend our lock
|
|
||||||
$aLockInfo = iTopOwnershipLock::ExtendLock($sClass, $iKey, $sOwnershipToken);
|
|
||||||
$sOwnershipDate = $aLockInfo['acquired'];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$aLockInfo = iTopOwnershipLock::AcquireLock($sClass, $iKey);
|
|
||||||
if ($aLockInfo['success'])
|
|
||||||
{
|
|
||||||
$sOwnershipToken = $aLockInfo['token'];
|
|
||||||
$sOwnershipDate = $aLockInfo['acquired'];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$oOwner = $aLockInfo['lock']->GetOwner();
|
|
||||||
// If the object is locked by the current user, it's worth trying again, since
|
|
||||||
// the lock may be released by 'onunload' which is called AFTER loading the current page.
|
|
||||||
//$bTryAgain = $oOwner->GetKey() == UserRights::GetUserId();
|
|
||||||
self::ReloadAndDisplay($oPage, $this, array('operation' => 'modify'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aExtraParams['wizard_container']) && $aExtraParams['wizard_container'])
|
|
||||||
{
|
|
||||||
$sClassLabel = MetaModel::GetName($sClass);
|
|
||||||
$oPage->set_title(Dict::Format('UI:ModificationPageTitle_Object_Class', $this->GetRawName(), $sClassLabel)); // Set title will take care of the encoding
|
|
||||||
$oPage->add("<div class=\"page_header\">\n");
|
|
||||||
$oPage->add("<h1>".$this->GetIcon()." ".Dict::Format('UI:ModificationTitle_Class_Object', $sClassLabel, $this->GetName())."</h1>\n");
|
|
||||||
$oPage->add("</div>\n");
|
|
||||||
$oPage->add("<div class=\"wizContainer\">\n");
|
|
||||||
}
|
|
||||||
self::$iGlobalFormId++;
|
self::$iGlobalFormId++;
|
||||||
$this->aFieldsMap = array();
|
$this->aFieldsMap = array();
|
||||||
$sPrefix = '';
|
$sPrefix = '';
|
||||||
@@ -2041,13 +1882,15 @@ EOF
|
|||||||
$aFieldsComments = (isset($aExtraParams['fieldsComments'])) ? $aExtraParams['fieldsComments'] : array();
|
$aFieldsComments = (isset($aExtraParams['fieldsComments'])) ? $aExtraParams['fieldsComments'] : array();
|
||||||
|
|
||||||
$this->m_iFormId = $sPrefix.self::$iGlobalFormId;
|
$this->m_iFormId = $sPrefix.self::$iGlobalFormId;
|
||||||
|
$sClass = get_class($this);
|
||||||
$oAppContext = new ApplicationContext();
|
$oAppContext = new ApplicationContext();
|
||||||
$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
|
$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
|
||||||
|
$iKey = $this->GetKey();
|
||||||
$aDetails = array();
|
$aDetails = array();
|
||||||
$aFieldsMap = array();
|
$aFieldsMap = array();
|
||||||
if (!isset($aExtraParams['action']))
|
if (!isset($aExtraParams['action']))
|
||||||
{
|
{
|
||||||
$sFormAction = utils::GetAbsoluteUrlAppRoot().'pages/'.$this->GetUIPage(); // No parameter in the URL, the only parameter will be the ones passed through the form
|
$sFormAction = $_SERVER['SCRIPT_NAME']; // No parameter in the URL, the only parameter will be the ones passed through the form
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2143,10 +1986,9 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
$sConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
|
$sConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
|
||||||
$sJSToken = json_encode($sOwnershipToken);
|
|
||||||
$oPage->add_ready_script(
|
$oPage->add_ready_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
$(window).unload(function() { return OnUnload('$iTransactionId', '$sClass', $iKey, $sJSToken) } );
|
$(window).unload(function() { return OnUnload('$iTransactionId') } );
|
||||||
window.onbeforeunload = function() {
|
window.onbeforeunload = function() {
|
||||||
if (!window.bInSubmit && !window.bInCancel)
|
if (!window.bInSubmit && !window.bInCancel)
|
||||||
{
|
{
|
||||||
@@ -2190,10 +2032,6 @@ EOF
|
|||||||
$oPage->add("<input type=\"hidden\" name=\"$sName\" value=\"$value\">\n");
|
$oPage->add("<input type=\"hidden\" name=\"$sName\" value=\"$value\">\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($sOwnershipToken !== null)
|
|
||||||
{
|
|
||||||
$oPage->add("<input type=\"hidden\" name=\"ownership_token\" value=\"".htmlentities($sOwnershipToken, ENT_QUOTES, 'UTF-8')."\">\n");
|
|
||||||
}
|
|
||||||
$oPage->add($oAppContext->GetForForm());
|
$oPage->add($oAppContext->GetForForm());
|
||||||
if ($sButtonsPosition != 'top')
|
if ($sButtonsPosition != 'top')
|
||||||
{
|
{
|
||||||
@@ -2204,29 +2042,21 @@ EOF
|
|||||||
|
|
||||||
// Hook the cancel button via jQuery so that it can be unhooked easily as well if needed
|
// Hook the cancel button via jQuery so that it can be unhooked easily as well if needed
|
||||||
$sDefaultUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=cancel&'.$oAppContext->GetForLink();
|
$sDefaultUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=cancel&'.$oAppContext->GetForLink();
|
||||||
$oPage->add_ready_script("$('#form_{$this->m_iFormId} button.cancel').click( function() { BackToDetails('$sClass', $iKey, '$sDefaultUrl', $sJSToken)} );");
|
$oPage->add_ready_script("$('#form_{$this->m_iFormId} button.cancel').click( function() { BackToDetails('$sClass', $iKey, '$sDefaultUrl')} );");
|
||||||
$oPage->add("</form>\n");
|
$oPage->add("</form>\n");
|
||||||
|
|
||||||
if (isset($aExtraParams['wizard_container']) && $aExtraParams['wizard_container'])
|
|
||||||
{
|
|
||||||
$oPage->add("</div>\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
$iFieldsCount = count($aFieldsMap);
|
$iFieldsCount = count($aFieldsMap);
|
||||||
$sJsonFieldsMap = json_encode($aFieldsMap);
|
$sJsonFieldsMap = json_encode($aFieldsMap);
|
||||||
$sState = $this->GetState();
|
$sState = $this->GetState();
|
||||||
$sSessionStorageKey = $sClass.'_'.$iKey;
|
|
||||||
|
|
||||||
$oPage->add_script(
|
$oPage->add_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
sessionStorage.removeItem('$sSessionStorageKey');
|
|
||||||
|
|
||||||
// Create the object once at the beginning of the page...
|
// Create the object once at the beginning of the page...
|
||||||
var oWizardHelper$sPrefix = new WizardHelper('$sClass', '$sPrefix', '$sState');
|
var oWizardHelper$sPrefix = new WizardHelper('$sClass', '$sPrefix', '$sState');
|
||||||
oWizardHelper$sPrefix.SetFieldsMap($sJsonFieldsMap);
|
oWizardHelper$sPrefix.SetFieldsMap($sJsonFieldsMap);
|
||||||
oWizardHelper$sPrefix.SetFieldsCount($iFieldsCount);
|
oWizardHelper$sPrefix.SetFieldsCount($iFieldsCount);
|
||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
$oPage->add_ready_script(
|
$oPage->add_ready_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
oWizardHelper$sPrefix.UpdateWizard();
|
oWizardHelper$sPrefix.UpdateWizard();
|
||||||
@@ -2234,27 +2064,7 @@ EOF
|
|||||||
CheckFields('form_{$this->m_iFormId}', false);
|
CheckFields('form_{$this->m_iFormId}', false);
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
if ($sOwnershipToken !== null)
|
|
||||||
{
|
|
||||||
$this->GetOwnershipJSHandler($oPage, $sOwnershipToken);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Probably a new object (or no concurrent lock), let's add a watchdog so that the session is kept open while editing
|
|
||||||
$iInterval = MetaModel::GetConfig()->Get('concurrent_lock_expiration_delay') * 1000 / 2;
|
|
||||||
if ($iInterval > 0)
|
|
||||||
{
|
|
||||||
$iInterval = max(MIN_WATCHDOG_INTERVAL*1000, $iInterval); // Minimum interval for the watchdog is MIN_WATCHDOG_INTERVAL
|
|
||||||
$oPage->add_ready_script(
|
|
||||||
<<<EOF
|
|
||||||
window.setInterval(function() {
|
|
||||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'watchdog'});
|
|
||||||
}, $iInterval);
|
|
||||||
EOF
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function DisplayCreationForm(WebPage $oPage, $sClass, $oObjectToClone = null, $aArgs = array(), $aExtraParams = array())
|
public static function DisplayCreationForm(WebPage $oPage, $sClass, $oObjectToClone = null, $aArgs = array(), $aExtraParams = array())
|
||||||
@@ -2333,7 +2143,6 @@ EOF
|
|||||||
public function DisplayStimulusForm(WebPage $oPage, $sStimulus)
|
public function DisplayStimulusForm(WebPage $oPage, $sStimulus)
|
||||||
{
|
{
|
||||||
$sClass = get_class($this);
|
$sClass = get_class($this);
|
||||||
$iKey = $this->GetKey();
|
|
||||||
$aTransitions = $this->EnumTransitions();
|
$aTransitions = $this->EnumTransitions();
|
||||||
$aStimuli = MetaModel::EnumStimuli($sClass);
|
$aStimuli = MetaModel::EnumStimuli($sClass);
|
||||||
if (!isset($aTransitions[$sStimulus]))
|
if (!isset($aTransitions[$sStimulus]))
|
||||||
@@ -2341,28 +2150,6 @@ EOF
|
|||||||
// Invalid stimulus
|
// Invalid stimulus
|
||||||
throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus, $this->GetName(), $this->GetStateLabel()));
|
throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus, $this->GetName(), $this->GetStateLabel()));
|
||||||
}
|
}
|
||||||
// Check for concurrent access lock
|
|
||||||
$LockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled');
|
|
||||||
$sOwnershipToken = null;
|
|
||||||
if ($LockEnabled)
|
|
||||||
{
|
|
||||||
$sOwnershipToken = utils::ReadPostedParam('ownership_token', null, false, 'raw_data');
|
|
||||||
$aLockInfo = iTopOwnershipLock::AcquireLock($sClass, $iKey);
|
|
||||||
if ($aLockInfo['success'])
|
|
||||||
{
|
|
||||||
$sOwnershipToken = $aLockInfo['token'];
|
|
||||||
$sOwnershipDate = $aLockInfo['acquired'];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$oOwner = $aLockInfo['lock']->GetOwner();
|
|
||||||
// If the object is locked by the current user, it's worth trying again, since
|
|
||||||
// the lock may be released by 'onunload' which is called AFTER loading the current page.
|
|
||||||
//$bTryAgain = $oOwner->GetKey() == UserRights::GetUserId();
|
|
||||||
self::ReloadAndDisplay($oPage, $this, array('operation' => 'stimulus', 'stimulus' => $sStimulus));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$sActionLabel = $aStimuli[$sStimulus]->GetLabel();
|
$sActionLabel = $aStimuli[$sStimulus]->GetLabel();
|
||||||
$sActionDetails = $aStimuli[$sStimulus]->GetDescription();
|
$sActionDetails = $aStimuli[$sStimulus]->GetDescription();
|
||||||
$aTransition = $aTransitions[$sStimulus];
|
$aTransition = $aTransitions[$sStimulus];
|
||||||
@@ -2389,27 +2176,15 @@ EOF
|
|||||||
$iFieldIndex = 0;
|
$iFieldIndex = 0;
|
||||||
$aFieldsMap = array();
|
$aFieldsMap = array();
|
||||||
|
|
||||||
// The list of candidate fields is made of the ordered list of "details" attributes + other attributes
|
$aDetailsList =$this->FlattenZList(MetaModel::GetZListItems($sClass, 'details'));
|
||||||
$aAttributes = array();
|
|
||||||
foreach ($this->FlattenZList(MetaModel::GetZListItems($sClass, 'details')) as $sAttCode)
|
|
||||||
{
|
|
||||||
$aAttributes[$sAttCode] = true;
|
|
||||||
}
|
|
||||||
foreach(MetaModel::GetAttributesList($sClass) as $sAttCode)
|
|
||||||
{
|
|
||||||
if (!array_key_exists($sAttCode, $aAttributes))
|
|
||||||
{
|
|
||||||
$aAttributes[$sAttCode] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Order the fields based on their dependencies, set the fields for which there is only one possible value
|
// Order the fields based on their dependencies, set the fields for which there is only one possible value
|
||||||
// and perform this in the order of dependencies to avoid dead-ends
|
// and perform this in the order of dependencies to avoid dead-ends
|
||||||
$aDeps = array();
|
$aDeps = array();
|
||||||
foreach($aAttributes as $sAttCode => $trash)
|
foreach($aDetailsList as $sAttCode)
|
||||||
{
|
{
|
||||||
$aDeps[$sAttCode] = MetaModel::GetPrequisiteAttributes($sClass, $sAttCode);
|
$aDeps[$sAttCode] = MetaModel::GetPrequisiteAttributes($sClass, $sAttCode);
|
||||||
}
|
}
|
||||||
$aList = $this->OrderDependentFields($aDeps);
|
$aList =$this->OrderDependentFields($aDeps);
|
||||||
|
|
||||||
foreach($aList as $sAttCode)
|
foreach($aList as $sAttCode)
|
||||||
{
|
{
|
||||||
@@ -2463,15 +2238,10 @@ EOF
|
|||||||
$oPage->add("<input type=\"hidden\" name=\"class\" value=\"$sClass\">\n");
|
$oPage->add("<input type=\"hidden\" name=\"class\" value=\"$sClass\">\n");
|
||||||
$oPage->add("<input type=\"hidden\" name=\"operation\" value=\"apply_stimulus\">\n");
|
$oPage->add("<input type=\"hidden\" name=\"operation\" value=\"apply_stimulus\">\n");
|
||||||
$oPage->add("<input type=\"hidden\" name=\"stimulus\" value=\"$sStimulus\">\n");
|
$oPage->add("<input type=\"hidden\" name=\"stimulus\" value=\"$sStimulus\">\n");
|
||||||
$iTransactionId = utils::GetNewTransactionId();
|
$oPage->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::GetNewTransactionId()."\">\n");
|
||||||
$oPage->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".$iTransactionId."\">\n");
|
|
||||||
if ($sOwnershipToken !== null)
|
|
||||||
{
|
|
||||||
$oPage->add("<input type=\"hidden\" name=\"ownership_token\" value=\"".htmlentities($sOwnershipToken, ENT_QUOTES, 'UTF-8')."\">\n");
|
|
||||||
}
|
|
||||||
$oAppContext = new ApplicationContext();
|
$oAppContext = new ApplicationContext();
|
||||||
$oPage->add($oAppContext->GetForForm());
|
$oPage->add($oAppContext->GetForForm());
|
||||||
$oPage->add("<button type=\"button\" class=\"action cancel\" onClick=\"BackToDetails('$sClass', ".$this->GetKey().", '', '$sOwnershipToken')\"><span>".Dict::S('UI:Button:Cancel')."</span></button> \n");
|
$oPage->add("<button type=\"button\" class=\"action\" onClick=\"BackToDetails('$sClass', ".$this->GetKey().")\"><span>".Dict::S('UI:Button:Cancel')."</span></button> \n");
|
||||||
$oPage->add("<button type=\"submit\" class=\"action\"><span>$sActionLabel</span></button>\n");
|
$oPage->add("<button type=\"submit\" class=\"action\"><span>$sActionLabel</span></button>\n");
|
||||||
$oPage->add("</form>\n");
|
$oPage->add("</form>\n");
|
||||||
$oPage->add("</div>\n");
|
$oPage->add("</div>\n");
|
||||||
@@ -2494,19 +2264,12 @@ EOF
|
|||||||
oWizardHelper.SetFieldsCount($iFieldsCount);
|
oWizardHelper.SetFieldsCount($iFieldsCount);
|
||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
$sJSToken = json_encode($sOwnershipToken);
|
|
||||||
$oPage->add_ready_script(
|
$oPage->add_ready_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
// Starts the validation when the page is ready
|
// Starts the validation when the page is ready
|
||||||
CheckFields('apply_stimulus', false);
|
CheckFields('apply_stimulus', false);
|
||||||
$(window).unload(function() { return OnUnload('$iTransactionId', '$sClass', $iKey, $sJSToken) } );
|
|
||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($sOwnershipToken !== null)
|
|
||||||
{
|
|
||||||
$this->GetOwnershipJSHandler($oPage, $sOwnershipToken);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function ProcessZlist($aList, $aDetails, $sCurrentTab, $sCurrentCol, $sCurrentSet)
|
public static function ProcessZlist($aList, $aDetails, $sCurrentTab, $sCurrentCol, $sCurrentSet)
|
||||||
@@ -2869,26 +2632,6 @@ EOF
|
|||||||
return $aWriteableAttList;
|
return $aWriteableAttList;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute the attribute flags depending on the object state
|
|
||||||
*/
|
|
||||||
public function GetFormAttributeFlags($sAttCode)
|
|
||||||
{
|
|
||||||
if ($this->IsNew())
|
|
||||||
{
|
|
||||||
$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$iFlags = $this->GetAttributeFlags($sAttCode);
|
|
||||||
}
|
|
||||||
if (($iFlags & OPT_ATT_MANDATORY) && $this->IsNew())
|
|
||||||
{
|
|
||||||
$iFlags = $iFlags & ~OPT_ATT_READONLY; // Mandatory fields cannot be read-only when creating an object
|
|
||||||
}
|
|
||||||
return $iFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the object from a flat array of values
|
* Updates the object from a flat array of values
|
||||||
* @param string $aValues array of attcode => scalar or array (N-N links)
|
* @param string $aValues array of attcode => scalar or array (N-N links)
|
||||||
@@ -3083,10 +2826,6 @@ EOF
|
|||||||
{
|
{
|
||||||
$value = array('fcontents' => utils::ReadPostedDocument("attr_{$sFormPrefix}{$sAttCode}", 'fcontents'));
|
$value = array('fcontents' => utils::ReadPostedDocument("attr_{$sFormPrefix}{$sAttCode}", 'fcontents'));
|
||||||
}
|
}
|
||||||
elseif ($oAttDef->GetEditClass() == 'RedundancySetting')
|
|
||||||
{
|
|
||||||
$value = $oAttDef->ReadValueFromPostedForm($sFormPrefix);
|
|
||||||
}
|
|
||||||
else if (($oAttDef->GetEditClass() == 'LinkedSet') && !$oAttDef->IsIndirect() &&
|
else if (($oAttDef->GetEditClass() == 'LinkedSet') && !$oAttDef->IsIndirect() &&
|
||||||
(($oAttDef->GetEditMode() == LINKSET_EDITMODE_INPLACE) || ($oAttDef->GetEditMode() == LINKSET_EDITMODE_ADDREMOVE)) )
|
(($oAttDef->GetEditMode() == LINKSET_EDITMODE_INPLACE) || ($oAttDef->GetEditMode() == LINKSET_EDITMODE_ADDREMOVE)) )
|
||||||
{
|
{
|
||||||
@@ -3205,7 +2944,7 @@ EOF
|
|||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function BulkUpdateTracked_Internal(DBSearch $oFilter, array $aValues)
|
protected static function BulkUpdateTracked_Internal(DBObjectSearch $oFilter, array $aValues)
|
||||||
{
|
{
|
||||||
// Todo - invoke the extension
|
// Todo - invoke the extension
|
||||||
return parent::BulkUpdateTracked_Internal($oFilter, $aValues);
|
return parent::BulkUpdateTracked_Internal($oFilter, $aValues);
|
||||||
@@ -3688,7 +3427,7 @@ EOF
|
|||||||
$oP->Table($aHeaders, $aRows);
|
$oP->Table($aHeaders, $aRows);
|
||||||
if ($bPreview)
|
if ($bPreview)
|
||||||
{
|
{
|
||||||
$sFormAction = utils::GetAbsoluteUrlAppRoot().'pages/UI.php'; // No parameter in the URL, the only parameter will be the ones passed through the form
|
$sFormAction = $_SERVER['SCRIPT_NAME']; // No parameter in the URL, the only parameter will be the ones passed through the form
|
||||||
// Form to submit:
|
// Form to submit:
|
||||||
$oP->add("<form method=\"post\" action=\"$sFormAction\" enctype=\"multipart/form-data\">\n");
|
$oP->add("<form method=\"post\" action=\"$sFormAction\" enctype=\"multipart/form-data\">\n");
|
||||||
$aDefaults = utils::ReadParam('default', array());
|
$aDefaults = utils::ReadParam('default', array());
|
||||||
@@ -4009,77 +3748,5 @@ EOF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Find redundancy settings that can be viewed and modified in a tab
|
|
||||||
* Settings are distributed to the corresponding link set attribute so as to be shown in the relevant tab
|
|
||||||
*/
|
|
||||||
protected function FindVisibleRedundancySettings()
|
|
||||||
{
|
|
||||||
$aRet = array();
|
|
||||||
foreach (MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
|
|
||||||
{
|
|
||||||
if ($oAttDef instanceof AttributeRedundancySettings)
|
|
||||||
{
|
|
||||||
if ($oAttDef->IsVisible())
|
|
||||||
{
|
|
||||||
$aQueryInfo = $oAttDef->GetRelationQueryData();
|
|
||||||
if (isset($aQueryInfo['sAttribute']))
|
|
||||||
{
|
|
||||||
$oUpperAttDef = MetaModel::GetAttributeDef($aQueryInfo['sFromClass'], $aQueryInfo['sAttribute']);
|
|
||||||
$oHostAttDef = $oUpperAttDef->GetMirrorLinkAttribute();
|
|
||||||
if ($oHostAttDef)
|
|
||||||
{
|
|
||||||
$sHostAttCode = $oHostAttDef->GetCode();
|
|
||||||
$aRet[$sHostAttCode][] = $oAttDef;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $aRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the javascript code handle the "watchdog" associated with the concurrent access locking mechanism
|
|
||||||
* @param Webpage $oPage
|
|
||||||
* @param string $sOwnershipToken
|
|
||||||
*/
|
|
||||||
protected function GetOwnershipJSHandler($oPage, $sOwnershipToken)
|
|
||||||
{
|
|
||||||
$iInterval = max(MIN_WATCHDOG_INTERVAL, MetaModel::GetConfig()->Get('concurrent_lock_expiration_delay')) * 1000 / 2; // Minimum interval for the watchdog is MIN_WATCHDOG_INTERVAL
|
|
||||||
$sJSClass = json_encode(get_class($this));
|
|
||||||
$iKey = (int) $this->GetKey();
|
|
||||||
$sJSToken = json_encode($sOwnershipToken);
|
|
||||||
$sJSTitle = json_encode(Dict::S('UI:DisconnectedDlgTitle'));
|
|
||||||
$sJSOk = json_encode(Dict::S('UI:Button:Ok'));
|
|
||||||
$oPage->add_ready_script(
|
|
||||||
<<<EOF
|
|
||||||
window.setInterval(function() {
|
|
||||||
if (window.bInSubmit || window.bInCancel) return;
|
|
||||||
|
|
||||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'extend_lock', obj_class: $sJSClass, obj_key: $iKey, token: $sJSToken }, function(data) {
|
|
||||||
if (!data.status)
|
|
||||||
{
|
|
||||||
if ($('.lock_owned').length == 0)
|
|
||||||
{
|
|
||||||
$('.ui-layout-content').prepend('<div class="header_message message_error lock_owned">'+data.message+'</div>');
|
|
||||||
$('<div>'+data.popup_message+'</div>').dialog({title: $sJSTitle, modal: true, autoOpen: true, buttons:[ {text: $sJSOk, click: function() { $(this).dialog('close'); } }], close: function() { $(this).remove(); }});
|
|
||||||
}
|
|
||||||
$('.wizContainer form button.action:not(.cancel)').attr('disabled', 'disabled');
|
|
||||||
}
|
|
||||||
else if ((data.operation == 'lost') || (data.operation == 'expired'))
|
|
||||||
{
|
|
||||||
if ($('.lock_owned').length == 0)
|
|
||||||
{
|
|
||||||
$('.ui-layout-content').prepend('<div class="header_message message_error lock_owned">'+data.message+'</div>');
|
|
||||||
$('<div>'+data.popup_message+'</div>').dialog({title: $sJSTitle, modal: true, autoOpen: true, buttons:[ {text: $sJSOk, click: function() { $(this).dialog('close'); } }], close: function() { $(this).remove(); }});
|
|
||||||
}
|
|
||||||
$('.wizContainer form button.action:not(.cancel)').attr('disabled', 'disabled');
|
|
||||||
}
|
|
||||||
}, 'json');
|
|
||||||
}, $iInterval);
|
|
||||||
EOF
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
?>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2014 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
* Simple web page with no includes or fancy formatting, useful to generateXML documents
|
* Simple web page with no includes or fancy formatting, useful to generateXML documents
|
||||||
* The page adds the content-type text/XML and the encoding into the headers
|
* The page adds the content-type text/XML and the encoding into the headers
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -51,9 +51,9 @@ class CSVPage extends WebPage
|
|||||||
echo trim($this->s_content);
|
echo trim($this->s_content);
|
||||||
echo "\n";
|
echo "\n";
|
||||||
|
|
||||||
if (class_exists('DBSearch'))
|
if (class_exists('MetaModel'))
|
||||||
{
|
{
|
||||||
DBSearch::RecordQueryTrace();
|
MetaModel::RecordQueryTrace();
|
||||||
}
|
}
|
||||||
if (class_exists('ExecutionKPI'))
|
if (class_exists('ExecutionKPI'))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ abstract class Dashboard
|
|||||||
|
|
||||||
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
|
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
|
||||||
{
|
{
|
||||||
$oPage->add('<h1>'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'</h1>');
|
$oPage->add('<h1>'.Dict::S($this->sTitle).'</h1>');
|
||||||
$oLayout = new $this->sLayoutClass;
|
$oLayout = new $this->sLayoutClass;
|
||||||
$oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams);
|
$oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams);
|
||||||
if (!$bEditMode)
|
if (!$bEditMode)
|
||||||
@@ -546,9 +546,7 @@ class RuntimeDashboard extends Dashboard
|
|||||||
|
|
||||||
public function RenderEditionTools($oPage)
|
public function RenderEditionTools($oPage)
|
||||||
{
|
{
|
||||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.iframe-transport.js');
|
$sEditMenu = "<td><span id=\"DashboardMenu\"><ul><li><img src=\"../images/edit.png\"><ul>";
|
||||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.fileupload.js');
|
|
||||||
$sEditMenu = "<td><span id=\"DashboardMenu\"><ul><li><img src=\"../images/pencil-menu.png\"><ul>";
|
|
||||||
|
|
||||||
$aActions = array();
|
$aActions = array();
|
||||||
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:Edit'), "return EditDashboard('{$this->sId}')");
|
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:Edit'), "return EditDashboard('{$this->sId}')");
|
||||||
|
|||||||
@@ -733,8 +733,7 @@ abstract class DashletGroupBy extends Dashlet
|
|||||||
if (is_subclass_of($sAttType, 'AttributeFriendlyName')) continue;
|
if (is_subclass_of($sAttType, 'AttributeFriendlyName')) continue;
|
||||||
if ($sAttType == 'AttributeExternalField') continue;
|
if ($sAttType == 'AttributeExternalField') continue;
|
||||||
if (is_subclass_of($sAttType, 'AttributeExternalField')) continue;
|
if (is_subclass_of($sAttType, 'AttributeExternalField')) continue;
|
||||||
if ($sAttType == 'AttributeOneWayPassword') continue;
|
|
||||||
|
|
||||||
$sLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);
|
$sLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);
|
||||||
$aGroupBy[$sAttCode] = $sLabel;
|
$aGroupBy[$sAttCode] = $sLabel;
|
||||||
|
|
||||||
@@ -1571,7 +1570,7 @@ class DashletBadge extends Dashlet
|
|||||||
$oPage->add('<p>');
|
$oPage->add('<p>');
|
||||||
$oPage->add(' <a>'.Dict::Format('UI:ClickToCreateNew', $sClassLabel).'</a>');
|
$oPage->add(' <a>'.Dict::Format('UI:ClickToCreateNew', $sClassLabel).'</a>');
|
||||||
$oPage->add(' <br/>');
|
$oPage->add(' <br/>');
|
||||||
$oPage->add(' <a>'.Dict::Format('UI:SearchFor_Class', $sClassLabel).'</a>');
|
$oPage->add(' <a>Search for Server objects</a>');
|
||||||
$oPage->add('</p>');
|
$oPage->add('</p>');
|
||||||
$oPage->add('</div>');
|
$oPage->add('</div>');
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<itop_design>
|
|
||||||
<portals>
|
|
||||||
<portal id="legacy_portal" _delta="define">
|
|
||||||
<url>portal/index.php</url>
|
|
||||||
<rank>1.0</rank>
|
|
||||||
<handler/>
|
|
||||||
<allow>
|
|
||||||
</allow>
|
|
||||||
<deny/>
|
|
||||||
</portal>
|
|
||||||
<portal id="backoffice" _delta="define">
|
|
||||||
<url>pages/UI.php</url>
|
|
||||||
<rank>2.0</rank>
|
|
||||||
<handler/>
|
|
||||||
<allow/>
|
|
||||||
<deny>
|
|
||||||
<profile id="Portal user"/>
|
|
||||||
</deny>
|
|
||||||
</portal>
|
|
||||||
</portals>
|
|
||||||
</itop_design>
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2013 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
/**
|
/**
|
||||||
* Data Table to display a set of objects in a tabular manner in HTML
|
* Data Table to display a set of objects in a tabular manner in HTML
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -290,24 +290,17 @@ EOF;
|
|||||||
|
|
||||||
protected function GetToolkitMenu(WebPage $oPage, $aExtraParams)
|
protected function GetToolkitMenu(WebPage $oPage, $aExtraParams)
|
||||||
{
|
{
|
||||||
if (!$oPage->IsPrintableVersion())
|
$sMenuTitle = Dict::S('UI:ConfigureThisList');
|
||||||
{
|
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li><img src="../images/toolkit_menu.png"><ul>';
|
||||||
$sMenuTitle = Dict::S('UI:ConfigureThisList');
|
|
||||||
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li><img src="../images/toolkit_menu.png?itopversion='.ITOP_VERSION.'"><ul>';
|
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
|
||||||
|
$aActions = array(
|
||||||
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
|
$oMenuItem1->GetUID() => $oMenuItem1->GetMenuItem(),
|
||||||
$aActions = array(
|
);
|
||||||
$oMenuItem1->GetUID() => $oMenuItem1->GetMenuItem(),
|
$this->oSet->Rewind();
|
||||||
);
|
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_OBJLIST_TOOLKIT, $this->oSet, $aActions, $this->sTableId, $this->iListId);
|
||||||
$this->oSet->Rewind();
|
$this->oSet->Rewind();
|
||||||
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_OBJLIST_TOOLKIT, $this->oSet, $aActions, $this->sTableId, $this->iListId);
|
$sHtml .= $oPage->RenderPopupMenuItems($aActions);
|
||||||
$this->oSet->Rewind();
|
|
||||||
$sHtml .= $oPage->RenderPopupMenuItems($aActions);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sHtml = '';
|
|
||||||
}
|
|
||||||
return $sHtml;
|
return $sHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -613,34 +606,6 @@ EOF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Simplified version of the data table with less "decoration" (and no paging)
|
|
||||||
* which is optimized for printing
|
|
||||||
*/
|
|
||||||
class PrintableDataTable extends DataTable
|
|
||||||
{
|
|
||||||
public function GetAsHTML(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex, $aColumns, $bActionsMenu, $bToolkitMenu, $sSelectMode, $bViewLink, $aExtraParams)
|
|
||||||
{
|
|
||||||
return $this->GetHTMLTable($oPage, $aColumns, $sSelectMode, -1, $bViewLink, $aExtraParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetHTMLTable(WebPage $oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
|
|
||||||
{
|
|
||||||
$iNbPages = ($iPageSize < 1) ? 1 : ceil($this->iNbObjects / $iPageSize);
|
|
||||||
if ($iPageSize < 1)
|
|
||||||
{
|
|
||||||
$iPageSize = -1; // convention: no pagination
|
|
||||||
}
|
|
||||||
$aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink);
|
|
||||||
|
|
||||||
$aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
|
|
||||||
|
|
||||||
$sHtml = $oPage->GetTable($aAttribs, $aValues);
|
|
||||||
|
|
||||||
return $sHtml;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DataTableSettings implements Serializable
|
class DataTableSettings implements Serializable
|
||||||
{
|
{
|
||||||
public $aClassAliases;
|
public $aClassAliases;
|
||||||
@@ -748,12 +713,6 @@ class DataTableSettings implements Serializable
|
|||||||
{
|
{
|
||||||
$sSort = $aSortOrder['friendlyname'] ? 'asc' : 'desc';
|
$sSort = $aSortOrder['friendlyname'] ? 'asc' : 'desc';
|
||||||
}
|
}
|
||||||
$sNormalizedFName = MetaModel::NormalizeFieldSpec($sClass, 'friendlyname');
|
|
||||||
if(array_key_exists($sNormalizedFName, $aSortOrder))
|
|
||||||
{
|
|
||||||
$sSort = $aSortOrder[$sNormalizedFName] ? 'asc' : 'desc';
|
|
||||||
}
|
|
||||||
|
|
||||||
$aColumns[$sAlias]['_key_'] = $oSettings->GetFieldData($sAlias, '_key_', null, true /* bChecked */, $sSort);
|
$aColumns[$sAlias]['_key_'] = $oSettings->GetFieldData($sAlias, '_key_', null, true /* bChecked */, $sSort);
|
||||||
}
|
}
|
||||||
foreach($aList as $sAttCode)
|
foreach($aList as $sAttCode)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2013 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
/**
|
/**
|
||||||
* DisplayBlock and derived class
|
* DisplayBlock and derived class
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ class DisplayBlock
|
|||||||
protected $m_aParams;
|
protected $m_aParams;
|
||||||
protected $m_oSet;
|
protected $m_oSet;
|
||||||
|
|
||||||
public function __construct(DBSearch $oFilter, $sStyle = 'list', $bAsynchronous = false, $aParams = array(), $oSet = null)
|
public function __construct(DBObjectSearch $oFilter, $sStyle = 'list', $bAsynchronous = false, $aParams = array(), $oSet = null)
|
||||||
{
|
{
|
||||||
$this->m_oFilter = $oFilter->DeepClone();
|
$this->m_oFilter = $oFilter->DeepClone();
|
||||||
$this->m_aConditions = array();
|
$this->m_aConditions = array();
|
||||||
@@ -73,7 +73,6 @@ class DisplayBlock
|
|||||||
{
|
{
|
||||||
$oDummyFilter = new DBObjectSearch($oSet->GetClass());
|
$oDummyFilter = new DBObjectSearch($oSet->GetClass());
|
||||||
$aKeys = array();
|
$aKeys = array();
|
||||||
$oSet->OptimizeColumnLoad(array('id')); // No need to load all the columns just to get the id
|
|
||||||
while($oObject = $oSet->Fetch())
|
while($oObject = $oSet->Fetch())
|
||||||
{
|
{
|
||||||
$aKeys[] = $oObject->GetKey();
|
$aKeys[] = $oObject->GetKey();
|
||||||
@@ -180,11 +179,11 @@ class DisplayBlock
|
|||||||
switch($sEncoding)
|
switch($sEncoding)
|
||||||
{
|
{
|
||||||
case 'text/serialize':
|
case 'text/serialize':
|
||||||
$oFilter = DBSearch::unserialize($sITopData);
|
$oFilter = CMDBSearchFilter::unserialize($sITopData);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'text/oql':
|
case 'text/oql':
|
||||||
$oFilter = DBSearch::FromOQL($sITopData);
|
$oFilter = CMDBSearchFilter::FromOQL($sITopData);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return new $sBlockClass($oFilter, $sBlockType, $bAsynchronous, $aParams);
|
return new $sBlockClass($oFilter, $sBlockType, $bAsynchronous, $aParams);
|
||||||
@@ -394,7 +393,7 @@ class DisplayBlock
|
|||||||
{
|
{
|
||||||
if (isset($aExtraParams['group_by_label']))
|
if (isset($aExtraParams['group_by_label']))
|
||||||
{
|
{
|
||||||
$oGroupByExp = Expression::FromOQL($aExtraParams['group_by']);
|
$oGroupByExp = Expression::FromOQL($aExtraParams['group_by']);
|
||||||
$sGroupByLabel = $aExtraParams['group_by_label'];
|
$sGroupByLabel = $aExtraParams['group_by_label'];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -405,24 +404,9 @@ class DisplayBlock
|
|||||||
$sGroupByLabel = MetaModel::GetLabel($this->m_oFilter->GetClass(), $aExtraParams['group_by']);
|
$sGroupByLabel = MetaModel::GetLabel($this->m_oFilter->GetClass(), $aExtraParams['group_by']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Security filtering
|
|
||||||
$aFields = $oGroupByExp->ListRequiredFields();
|
|
||||||
foreach($aFields as $sFieldAlias)
|
|
||||||
{
|
|
||||||
if (preg_match('/^([^.]+)\\.([^.]+)$/', $sFieldAlias, $aMatches))
|
|
||||||
{
|
|
||||||
$sFieldClass = $this->m_oFilter->GetClassName($aMatches[1]);
|
|
||||||
$oAttDef = MetaModel::GetAttributeDef($sFieldClass, $aMatches[2]);
|
|
||||||
if ($oAttDef instanceof AttributeOneWayPassword)
|
|
||||||
{
|
|
||||||
throw new Exception('Grouping on password fields is not supported.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$aGroupBy = array();
|
$aGroupBy = array();
|
||||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||||
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true);
|
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy, true);
|
||||||
$aRes = CMDBSource::QueryToArray($sSql);
|
$aRes = CMDBSource::QueryToArray($sSql);
|
||||||
|
|
||||||
$aGroupBy = array();
|
$aGroupBy = array();
|
||||||
@@ -461,8 +445,6 @@ class DisplayBlock
|
|||||||
$sFormat = isset($aExtraParams['format']) ? $aExtraParams['format'] : 'UI:Pagination:HeaderNoSelection';
|
$sFormat = isset($aExtraParams['format']) ? $aExtraParams['format'] : 'UI:Pagination:HeaderNoSelection';
|
||||||
$sHtml .= $oPage->GetP(Dict::Format($sFormat, $iTotalCount));
|
$sHtml .= $oPage->GetP(Dict::Format($sFormat, $iTotalCount));
|
||||||
$sHtml .= $oPage->GetTable($aAttribs, $aData);
|
$sHtml .= $oPage->GetTable($aAttribs, $aData);
|
||||||
|
|
||||||
$oPage->add_ready_script("LoadGroupBySortOrder('$sId');\n$('#{$sId} table.listResults').unbind('sortEnd.group_by').bind('sortEnd.group_by', function() { SaveGroupBySortOrder('$sId', $(this)[0].config.sortList); })");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -720,7 +702,7 @@ class DisplayBlock
|
|||||||
{
|
{
|
||||||
$sHtml .= "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=new&class={$sClass}&$sParams\">".Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($sClass))."</a><br/>\n";
|
$sHtml .= "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=new&class={$sClass}&$sParams\">".Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($sClass))."</a><br/>\n";
|
||||||
}
|
}
|
||||||
$sHtml .= "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search_form&do_search=0&class={$sClass}&$sParams\">".Dict::Format('UI:SearchFor_Class', MetaModel::GetName($sClass))."</a>\n";
|
$sHtml .= "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search_form&class={$sClass}&$sParams\">".Dict::Format('UI:SearchFor_Class', MetaModel::GetName($sClass))."</a>\n";
|
||||||
$sHtml .= '</p>';
|
$sHtml .= '</p>';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -867,24 +849,21 @@ class DisplayBlock
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'search':
|
case 'search':
|
||||||
if (!$oPage->IsPrintableVersion())
|
$sStyle = (isset($aExtraParams['open']) && ($aExtraParams['open'] == 'true')) ? 'SearchDrawer' : 'SearchDrawer DrawerClosed';
|
||||||
{
|
$sHtml .= "<div id=\"ds_$sId\" class=\"$sStyle\">\n";
|
||||||
$sStyle = (isset($aExtraParams['open']) && ($aExtraParams['open'] == 'true')) ? 'SearchDrawer' : 'SearchDrawer DrawerClosed';
|
$oPage->add_ready_script(
|
||||||
$sHtml .= "<div id=\"ds_$sId\" class=\"$sStyle\">\n";
|
|
||||||
$oPage->add_ready_script(
|
|
||||||
<<<EOF
|
<<<EOF
|
||||||
$("#dh_$sId").click( function() {
|
$("#dh_$sId").click( function() {
|
||||||
$("#ds_$sId").slideToggle('normal', function() { $("#ds_$sId").parent().resize(); FixSearchFormsDisposition(); $("#dh_$sId").trigger('toggle_complete'); } );
|
$("#ds_$sId").slideToggle('normal', function() { $("#ds_$sId").parent().resize(); } );
|
||||||
$("#dh_$sId").toggleClass('open');
|
$("#dh_$sId").toggleClass('open');
|
||||||
});
|
});
|
||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
$aExtraParams['currentId'] = $sId;
|
$aExtraParams['currentId'] = $sId;
|
||||||
$sHtml .= cmdbAbstractObject::GetSearchForm($oPage, $this->m_oSet, $aExtraParams);
|
$sHtml .= cmdbAbstractObject::GetSearchForm($oPage, $this->m_oSet, $aExtraParams);
|
||||||
$sHtml .= "</div>\n";
|
$sHtml .= "</div>\n";
|
||||||
$sHtml .= "<div class=\"HRDrawer\"></div>\n";
|
$sHtml .= "<div class=\"HRDrawer\"></div>\n";
|
||||||
$sHtml .= "<div id=\"dh_$sId\" class=\"DrawerHandle\">".Dict::S('UI:SearchToggle')."</div>\n";
|
$sHtml .= "<div id=\"dh_$sId\" class=\"DrawerHandle\">".Dict::S('UI:SearchToggle')."</div>\n";
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'open_flash_chart':
|
case 'open_flash_chart':
|
||||||
@@ -931,7 +910,7 @@ EOF
|
|||||||
|
|
||||||
$aGroupBy = array();
|
$aGroupBy = array();
|
||||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||||
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true);
|
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy, true);
|
||||||
$aRes = CMDBSource::QueryToArray($sSql);
|
$aRes = CMDBSource::QueryToArray($sSql);
|
||||||
|
|
||||||
$aGroupBy = array();
|
$aGroupBy = array();
|
||||||
@@ -1006,7 +985,7 @@ EOF
|
|||||||
|
|
||||||
$aGroupBy = array();
|
$aGroupBy = array();
|
||||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||||
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true);
|
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy, true);
|
||||||
$aRes = CMDBSource::QueryToArray($sSql);
|
$aRes = CMDBSource::QueryToArray($sSql);
|
||||||
|
|
||||||
$aGroupBy = array();
|
$aGroupBy = array();
|
||||||
@@ -1088,7 +1067,7 @@ EOF
|
|||||||
$aGroupBy = array();
|
$aGroupBy = array();
|
||||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||||
|
|
||||||
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true);
|
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy, true);
|
||||||
$aRes = CMDBSource::QueryToArray($sSql);
|
$aRes = CMDBSource::QueryToArray($sSql);
|
||||||
|
|
||||||
$aGroupBy = array();
|
$aGroupBy = array();
|
||||||
@@ -1148,7 +1127,7 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a condition (restriction) to the current DBSearch on which the display block is based
|
* Add a condition (restriction) to the current DBObjectSearch on which the display block is based
|
||||||
* taking into account the hierarchical keys for which the condition is based on the 'below' operator
|
* taking into account the hierarchical keys for which the condition is based on the 'below' operator
|
||||||
*/
|
*/
|
||||||
protected function AddCondition($sFilterCode, $condition, $sOpCode = null)
|
protected function AddCondition($sFilterCode, $condition, $sOpCode = null)
|
||||||
@@ -1236,7 +1215,7 @@ class HistoryBlock extends DisplayBlock
|
|||||||
protected $iLimitCount;
|
protected $iLimitCount;
|
||||||
protected $iLimitStart;
|
protected $iLimitStart;
|
||||||
|
|
||||||
public function __construct(DBSearch $oFilter, $sStyle = 'list', $bAsynchronous = false, $aParams = array(), $oSet = null)
|
public function __construct(DBObjectSearch $oFilter, $sStyle = 'list', $bAsynchronous = false, $aParams = array(), $oSet = null)
|
||||||
{
|
{
|
||||||
parent::__construct($oFilter, $sStyle, $bAsynchronous, $aParams, $oSet);
|
parent::__construct($oFilter, $sStyle, $bAsynchronous, $aParams, $oSet);
|
||||||
$this->iLimitStart = 0;
|
$this->iLimitStart = 0;
|
||||||
@@ -1254,15 +1233,12 @@ class HistoryBlock extends DisplayBlock
|
|||||||
$sHtml = '';
|
$sHtml = '';
|
||||||
$bTruncated = false;
|
$bTruncated = false;
|
||||||
$oSet = new CMDBObjectSet($this->m_oFilter, array('date'=>false));
|
$oSet = new CMDBObjectSet($this->m_oFilter, array('date'=>false));
|
||||||
if (!$oPage->IsPrintableVersion())
|
if (($this->iLimitStart > 0) || ($this->iLimitCount > 0))
|
||||||
{
|
{
|
||||||
if (($this->iLimitStart > 0) || ($this->iLimitCount > 0))
|
$oSet->SetLimit($this->iLimitCount, $this->iLimitStart);
|
||||||
|
if (($this->iLimitCount - $this->iLimitStart) < $oSet->Count())
|
||||||
{
|
{
|
||||||
$oSet->SetLimit($this->iLimitCount, $this->iLimitStart);
|
$bTruncated = true;
|
||||||
if (($this->iLimitCount - $this->iLimitStart) < $oSet->Count())
|
|
||||||
{
|
|
||||||
$bTruncated = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$sHtml .= "<!-- filter: ".($this->m_oFilter->ToOQL())."-->\n";
|
$sHtml .= "<!-- filter: ".($this->m_oFilter->ToOQL())."-->\n";
|
||||||
@@ -1305,7 +1281,7 @@ class HistoryBlock extends DisplayBlock
|
|||||||
{
|
{
|
||||||
$sHtml .= $this->GetHistoryTable($oPage, $oSet);
|
$sHtml .= $this->GetHistoryTable($oPage, $oSet);
|
||||||
}
|
}
|
||||||
$oPage->add_ready_script("$('.case-log-history-entry-toggle').on('click', function () { $(this).closest('.case-log-history-entry').toggleClass('expanded');});");
|
|
||||||
}
|
}
|
||||||
return $sHtml;
|
return $sHtml;
|
||||||
}
|
}
|
||||||
@@ -1396,8 +1372,7 @@ class MenuBlock extends DisplayBlock
|
|||||||
$sDefault.= "&default[$sKey]=$sValue";
|
$sDefault.= "&default[$sKey]=$sValue";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$bIsCreationAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_CREATE) == UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
$bIsCreationAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_CREATE) == UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
||||||
$sRefreshAction = '';
|
|
||||||
switch($oSet->Count())
|
switch($oSet->Count())
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
@@ -1407,144 +1382,68 @@ class MenuBlock extends DisplayBlock
|
|||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
$oObj = $oSet->Fetch();
|
$oObj = $oSet->Fetch();
|
||||||
if (is_null($oObj))
|
$id = $oObj->GetKey();
|
||||||
|
$bIsModifyAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
||||||
|
$bIsDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, $oSet);
|
||||||
|
// Just one object in the set, possible actions are "new / clone / modify and delete"
|
||||||
|
if (!isset($aExtraParams['link_attr']))
|
||||||
{
|
{
|
||||||
if (!isset($aExtraParams['link_attr']))
|
if ($bIsModifyAllowed) { $aActions['UI:Menu:Modify'] = array ('label' => Dict::S('UI:Menu:Modify'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=modify&class=$sClass&id=$id{$sContext}#"); }
|
||||||
|
if ($bIsCreationAllowed) { $aActions['UI:Menu:New'] = array ('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}"); }
|
||||||
|
if ($bIsDeleteAllowed) { $aActions['UI:Menu:Delete'] = array ('label' => Dict::S('UI:Menu:Delete'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=delete&class=$sClass&id=$id{$sContext}"); }
|
||||||
|
// Transitions / Stimuli
|
||||||
|
$aTransitions = $oObj->EnumTransitions();
|
||||||
|
if (count($aTransitions))
|
||||||
{
|
{
|
||||||
if ($bIsCreationAllowed) { $aActions['UI:Menu:New'] = array ('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}"); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$id = $oObj->GetKey();
|
|
||||||
if (utils::ReadParam('operation') == 'details')
|
|
||||||
{
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] == 'GET')
|
|
||||||
{
|
|
||||||
$sRefreshAction = "window.location.reload();";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sRefreshAction = "window.location.href='".ApplicationContext::MakeObjectUrl(get_class($oObj), $id)."';";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$bLocked = false;
|
|
||||||
if (MetaModel::GetConfig()->Get('concurrent_lock_enabled'))
|
|
||||||
{
|
|
||||||
$aLockInfo = iTopOwnershipLock::IsLocked(get_class($oObj), $id);
|
|
||||||
if ($aLockInfo['locked'])
|
|
||||||
{
|
|
||||||
$bLocked = true;
|
|
||||||
//$this->AddMenuSeparator($aActions);
|
|
||||||
//$aActions['concurrent_lock_unlock'] = array ('label' => Dict::S('UI:Menu:ReleaseConcurrentLock'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=kill_lock&class=$sClass&id=$id{$sContext}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$bRawModifiedAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
|
||||||
$bIsModifyAllowed = !$bLocked && $bRawModifiedAllowed;
|
|
||||||
$bIsDeleteAllowed = !$bLocked && UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, $oSet);
|
|
||||||
// Just one object in the set, possible actions are "new / clone / modify and delete"
|
|
||||||
if (!isset($aExtraParams['link_attr']))
|
|
||||||
{
|
|
||||||
if ($bIsModifyAllowed) { $aActions['UI:Menu:Modify'] = array ('label' => Dict::S('UI:Menu:Modify'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=modify&class=$sClass&id=$id{$sContext}#"); }
|
|
||||||
if ($bIsCreationAllowed) { $aActions['UI:Menu:New'] = array ('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}"); }
|
|
||||||
if ($bIsDeleteAllowed) { $aActions['UI:Menu:Delete'] = array ('label' => Dict::S('UI:Menu:Delete'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=delete&class=$sClass&id=$id{$sContext}"); }
|
|
||||||
// Transitions / Stimuli
|
|
||||||
if (!$bLocked)
|
|
||||||
{
|
|
||||||
$aTransitions = $oObj->EnumTransitions();
|
|
||||||
if (count($aTransitions))
|
|
||||||
{
|
|
||||||
$this->AddMenuSeparator($aActions);
|
|
||||||
$aStimuli = Metamodel::EnumStimuli(get_class($oObj));
|
|
||||||
foreach($aTransitions as $sStimulusCode => $aTransitionDef)
|
|
||||||
{
|
|
||||||
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass, $sStimulusCode, $oSet) : UR_ALLOWED_NO;
|
|
||||||
switch($iActionAllowed)
|
|
||||||
{
|
|
||||||
case UR_ALLOWED_YES:
|
|
||||||
$aActions[$sStimulusCode] = array('label' => $aStimuli[$sStimulusCode]->GetLabel(), 'url' => "{$sRootUrl}pages/UI.php?operation=stimulus&stimulus=$sStimulusCode&class=$sClass&id=$id{$sContext}");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Relations...
|
|
||||||
$aRelations = MetaModel::EnumRelationsEx($sClass);
|
|
||||||
if (count($aRelations))
|
|
||||||
{
|
|
||||||
$this->AddMenuSeparator($aActions);
|
|
||||||
foreach($aRelations as $sRelationCode => $aRelationInfo)
|
|
||||||
{
|
|
||||||
if (array_key_exists('down', $aRelationInfo))
|
|
||||||
{
|
|
||||||
$aActions[$sRelationCode.'_down'] = array ('label' => $aRelationInfo['down'], 'url' => "{$sRootUrl}pages/$sUIPage?operation=swf_navigator&relation=$sRelationCode&direction=down&class=$sClass&id=$id{$sContext}");
|
|
||||||
}
|
|
||||||
if (array_key_exists('up', $aRelationInfo))
|
|
||||||
{
|
|
||||||
$aActions[$sRelationCode.'_up'] = array ('label' => $aRelationInfo['up'], 'url' => "{$sRootUrl}pages/$sUIPage?operation=swf_navigator&relation=$sRelationCode&direction=up&class=$sClass&id=$id{$sContext}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($bLocked && $bRawModifiedAllowed)
|
|
||||||
{
|
|
||||||
// Add a special menu to kill the lock, but only to allowed users who can also modify this object
|
|
||||||
$aAllowedProfiles = MetaModel::GetConfig()->Get('concurrent_lock_override_profiles');
|
|
||||||
$bCanKill = false;
|
|
||||||
|
|
||||||
$oUser = UserRights::GetUserObject();
|
|
||||||
$aUserProfiles = array();
|
|
||||||
if (!is_null($oUser))
|
|
||||||
{
|
|
||||||
$oProfileSet = $oUser->Get('profile_list');
|
|
||||||
while ($oProfile = $oProfileSet->Fetch())
|
|
||||||
{
|
|
||||||
$aUserProfiles[$oProfile->Get('profile')] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($aAllowedProfiles as $sProfile)
|
|
||||||
{
|
|
||||||
if (array_key_exists($sProfile, $aUserProfiles))
|
|
||||||
{
|
|
||||||
$bCanKill = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($bCanKill)
|
|
||||||
{
|
|
||||||
$this->AddMenuSeparator($aActions);
|
|
||||||
$aActions['concurrent_lock_unlock'] = array ('label' => Dict::S('UI:Menu:KillConcurrentLock'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=kill_lock&class=$sClass&id=$id{$sContext}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
$this->AddMenuSeparator($aActions);
|
$this->AddMenuSeparator($aActions);
|
||||||
// Static menus: Email this page & CSV Export
|
$aStimuli = Metamodel::EnumStimuli(get_class($oObj));
|
||||||
$sUrl = ApplicationContext::MakeObjectUrl($sClass, $id);
|
foreach($aTransitions as $sStimulusCode => $aTransitionDef)
|
||||||
$aActions['UI:Menu:EMail'] = array ('label' => Dict::S('UI:Menu:EMail'), 'url' => "mailto:?subject=".urlencode($oObj->GetRawName())."&body=".urlencode($sUrl));
|
|
||||||
$aActions['UI:Menu:CSVExport'] = array ('label' => Dict::S('UI:Menu:CSVExport'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=search&filter=".urlencode($sFilter)."&format=csv{$sContext}");
|
|
||||||
// The style tells us whether the menu is displayed on a list of one object, or on the details of the given object
|
|
||||||
if ($this->m_sStyle == 'list')
|
|
||||||
{
|
{
|
||||||
// Actions specific to the list
|
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass, $sStimulusCode, $oSet) : UR_ALLOWED_NO;
|
||||||
$sOQL = addslashes($sFilterDesc);
|
switch($iActionAllowed)
|
||||||
$aActions['UI:Menu:AddToDashboard'] = array ('label' => Dict::S('UI:Menu:AddToDashboard'), 'url' => "#", 'onclick' => "return DashletCreationDlg('$sOQL')");
|
{
|
||||||
|
case UR_ALLOWED_YES:
|
||||||
|
$aActions[$sStimulusCode] = array('label' => $aStimuli[$sStimulusCode]->GetLabel(), 'url' => "{$sRootUrl}pages/UI.php?operation=stimulus&stimulus=$sStimulusCode&class=$sClass&id=$id{$sContext}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
$this->AddMenuSeparator($aActions);
|
// Relations...
|
||||||
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
|
$aRelations = MetaModel::EnumRelations($sClass);
|
||||||
|
if (count($aRelations))
|
||||||
{
|
{
|
||||||
$oSet->Rewind();
|
$this->AddMenuSeparator($aActions);
|
||||||
foreach($oExtensionInstance->EnumAllowedActions($oSet) as $sLabel => $sUrl)
|
foreach($aRelations as $sRelationCode)
|
||||||
{
|
{
|
||||||
$aActions[$sLabel] = array ('label' => $sLabel, 'url' => $sUrl);
|
$aActions[$sRelationCode] = array ('label' => MetaModel::GetRelationVerbUp($sRelationCode), 'url' => "{$sRootUrl}pages/$sUIPage?operation=swf_navigator&relation=$sRelationCode&class=$sClass&id=$id{$sContext}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
$this->AddMenuSeparator($aActions);
|
||||||
|
// Static menus: Email this page & CSV Export
|
||||||
|
$sUrl = ApplicationContext::MakeObjectUrl($sClass, $id);
|
||||||
|
$aActions['UI:Menu:EMail'] = array ('label' => Dict::S('UI:Menu:EMail'), 'url' => "mailto:?subject=".urlencode($oObj->GetRawName())."&body=".urlencode($sUrl));
|
||||||
|
$aActions['UI:Menu:CSVExport'] = array ('label' => Dict::S('UI:Menu:CSVExport'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=search&filter=".urlencode($sFilter)."&format=csv{$sContext}");
|
||||||
|
// The style tells us whether the menu is displayed on a list of one object, or on the details of the given object
|
||||||
|
if ($this->m_sStyle == 'list')
|
||||||
|
{
|
||||||
|
// Actions specific to the list
|
||||||
|
$sOQL = addslashes($sFilterDesc);
|
||||||
|
$aActions['UI:Menu:AddToDashboard'] = array ('label' => Dict::S('UI:Menu:AddToDashboard'), 'url' => "#", 'onclick' => "return DashletCreationDlg('$sOQL')");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
$this->AddMenuSeparator($aActions);
|
||||||
|
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
|
||||||
|
{
|
||||||
|
$oSet->Rewind();
|
||||||
|
foreach($oExtensionInstance->EnumAllowedActions($oSet) as $sLabel => $sUrl)
|
||||||
|
{
|
||||||
|
$aActions[$sLabel] = array ('label' => $sLabel, 'url' => $sUrl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1589,7 +1488,7 @@ class MenuBlock extends DisplayBlock
|
|||||||
{
|
{
|
||||||
$aQueryParams = $aExtraParams['query_params'];
|
$aQueryParams = $aExtraParams['query_params'];
|
||||||
}
|
}
|
||||||
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy);
|
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy);
|
||||||
$aRes = CMDBSource::QueryToArray($sSql);
|
$aRes = CMDBSource::QueryToArray($sSql);
|
||||||
if (count($aRes) == 1)
|
if (count($aRes) == 1)
|
||||||
{
|
{
|
||||||
@@ -1689,23 +1588,16 @@ class MenuBlock extends DisplayBlock
|
|||||||
$aShortcutActions = array();
|
$aShortcutActions = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$oPage->IsPrintableVersion())
|
if (count($aFavoriteActions) > 0)
|
||||||
{
|
{
|
||||||
if (count($aFavoriteActions) > 0)
|
$sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li>".Dict::S('UI:Menu:OtherActions')."\n<ul>\n";
|
||||||
{
|
|
||||||
$sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li>".Dict::S('UI:Menu:OtherActions')."\n<ul>\n";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li>".Dict::S('UI:Menu:Actions')."\n<ul>\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
$sHtml .= $oPage->RenderPopupMenuItems($aActions, $aFavoriteActions);
|
|
||||||
if (!$oPage->IsPrintableVersion() && ($sRefreshAction!=''))
|
|
||||||
{
|
|
||||||
$sHtml .= "<div class=\"actions_button\" title=\"".htmlentities(Dict::S('UI:Button:Refresh'), ENT_QUOTES, 'UTF-8')."\"><span class=\"refresh-button\" onclick=\"$sRefreshAction\"></span></div>";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li>".Dict::S('UI:Menu:Actions')."\n<ul>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$sHtml .= $oPage->RenderPopupMenuItems($aActions, $aFavoriteActions);
|
||||||
|
|
||||||
static $bPopupScript = false;
|
static $bPopupScript = false;
|
||||||
if (!$bPopupScript)
|
if (!$bPopupScript)
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ EOF
|
|||||||
return '</tr>';
|
return '</tr>';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function RenderAsDialog($oPage, $sDialogId, $sDialogTitle, $iDialogWidth, $sOkButtonLabel, $sIntroduction = null, $bAutoOpen = true)
|
public function RenderAsDialog($oPage, $sDialogId, $sDialogTitle, $iDialogWidth, $sOkButtonLabel, $sIntroduction = null)
|
||||||
{
|
{
|
||||||
$this->SetPrefix('dlg_'); // To make sure that the controls have different IDs that the property sheet which may be displayed at the same time
|
$this->SetPrefix('dlg_'); // To make sure that the controls have different IDs that the property sheet which may be displayed at the same time
|
||||||
|
|
||||||
@@ -355,14 +355,12 @@ EOF
|
|||||||
$this->Render($oPage);
|
$this->Render($oPage);
|
||||||
$oPage->add('</div>');
|
$oPage->add('</div>');
|
||||||
|
|
||||||
$sAutoOpen = $bAutoOpen ? 'true' : 'false';
|
|
||||||
$oPage->add_ready_script(
|
$oPage->add_ready_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
$('#$sDialogId').dialog({
|
$('#$sDialogId').dialog({
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
width: $iDialogWidth,
|
width: $iDialogWidth,
|
||||||
modal: true,
|
modal: true,
|
||||||
autoOpen: $sAutoOpen,
|
|
||||||
title: '$sDialogTitle',
|
title: '$sDialogTitle',
|
||||||
buttons: [
|
buttons: [
|
||||||
{ text: "$sOkButtonLabel", click: function() {
|
{ text: "$sOkButtonLabel", click: function() {
|
||||||
@@ -869,11 +867,13 @@ class DesignerTextField extends DesignerFormField
|
|||||||
{
|
{
|
||||||
protected $sValidationPattern;
|
protected $sValidationPattern;
|
||||||
protected $aForbiddenValues;
|
protected $aForbiddenValues;
|
||||||
|
protected $sExplainForbiddenValues;
|
||||||
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
||||||
{
|
{
|
||||||
parent::__construct($sCode, $sLabel, $defaultValue);
|
parent::__construct($sCode, $sLabel, $defaultValue);
|
||||||
$this->sValidationPattern = '';
|
$this->sValidationPattern = '';
|
||||||
$this->aForbiddenValues = array();
|
$this->aForbiddenValues = null;
|
||||||
|
$this->sExplainForbiddenValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SetValidationPattern($sValidationPattern)
|
public function SetValidationPattern($sValidationPattern)
|
||||||
@@ -883,17 +883,17 @@ class DesignerTextField extends DesignerFormField
|
|||||||
|
|
||||||
public function SetForbiddenValues($aValues, $sExplain)
|
public function SetForbiddenValues($aValues, $sExplain)
|
||||||
{
|
{
|
||||||
$aForbiddenValues = $aValues;
|
$this->aForbiddenValues = $aValues;
|
||||||
|
|
||||||
$iDefaultKey = array_search($this->defaultValue, $aForbiddenValues);
|
$iDefaultKey = array_search($this->defaultValue, $this->aForbiddenValues);
|
||||||
if ($iDefaultKey !== false)
|
if ($iDefaultKey !== false)
|
||||||
{
|
{
|
||||||
// The default (current) value is always allowed...
|
// The default (current) value is always allowed...
|
||||||
unset($aForbiddenValues[$iDefaultKey]);
|
unset($this->aForbiddenValues[$iDefaultKey]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->aForbiddenValues[] = array('values' => $aForbiddenValues, 'message' => $sExplain);
|
$this->sExplainForbiddenValues = $sExplain;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
|
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
|
||||||
@@ -911,15 +911,17 @@ class DesignerTextField extends DesignerFormField
|
|||||||
if (is_array($this->aForbiddenValues))
|
if (is_array($this->aForbiddenValues))
|
||||||
{
|
{
|
||||||
$sForbiddenValues = json_encode($this->aForbiddenValues);
|
$sForbiddenValues = json_encode($this->aForbiddenValues);
|
||||||
|
$sExplainForbiddenValues = addslashes($this->sExplainForbiddenValues);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$sForbiddenValues = '[]'; //Empty JS array
|
$sForbiddenValues = 'null';
|
||||||
|
$sExplainForbiddenValues = 'null';
|
||||||
}
|
}
|
||||||
$sMandatory = $this->bMandatory ? 'true' : 'false';
|
$sMandatory = $this->bMandatory ? 'true' : 'false';
|
||||||
$oP->add_ready_script(
|
$oP->add_ready_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
$('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', $(this).closest('form').attr('id'), $sForbiddenValues); } );
|
$('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', $(this).closest('form').attr('id'), $sForbiddenValues, '$sExplainForbiddenValues'); } );
|
||||||
{
|
{
|
||||||
var myTimer = null;
|
var myTimer = null;
|
||||||
$('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
|
$('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
|
||||||
@@ -939,8 +941,8 @@ EOF
|
|||||||
public function ReadParam(&$aValues)
|
public function ReadParam(&$aValues)
|
||||||
{
|
{
|
||||||
parent::ReadParam($aValues);
|
parent::ReadParam($aValues);
|
||||||
$sPattern = '/'.str_replace('/', '\/', $this->sValidationPattern).'/'; // Escape the forward slashes since they are used as delimiters for preg_match
|
|
||||||
if (($this->sValidationPattern != '') && (!preg_match($sPattern, $aValues[$this->sCode])) )
|
if (($this->sValidationPattern != '') &&(!preg_match('/'.$this->sValidationPattern.'/', $aValues[$this->sCode])) )
|
||||||
{
|
{
|
||||||
$aValues[$this->sCode] = $this->defaultValue;
|
$aValues[$this->sCode] = $this->defaultValue;
|
||||||
}
|
}
|
||||||
@@ -962,35 +964,30 @@ class DesignerLongTextField extends DesignerTextField
|
|||||||
if (is_array($this->aForbiddenValues))
|
if (is_array($this->aForbiddenValues))
|
||||||
{
|
{
|
||||||
$sForbiddenValues = json_encode($this->aForbiddenValues);
|
$sForbiddenValues = json_encode($this->aForbiddenValues);
|
||||||
|
$sExplainForbiddenValues = addslashes($this->sExplainForbiddenValues);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$sForbiddenValues = '[]'; //Empty JS array
|
$sForbiddenValues = 'null';
|
||||||
|
$sExplainForbiddenValues = 'null';
|
||||||
}
|
}
|
||||||
$sMandatory = $this->bMandatory ? 'true' : 'false';
|
$sMandatory = $this->bMandatory ? 'true' : 'false';
|
||||||
$sCSSClasses = '';
|
$sReadOnly = $this->IsReadOnly() ? 'readonly' : '';
|
||||||
if (count($this->aCSSClasses) > 0)
|
$oP->add_ready_script(
|
||||||
{
|
|
||||||
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
|
||||||
}
|
|
||||||
if (!$this->IsReadOnly())
|
|
||||||
{
|
|
||||||
$oP->add_ready_script(
|
|
||||||
<<<EOF
|
<<<EOF
|
||||||
$('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', $(this).closest('form').attr('id'), $sForbiddenValues); } );
|
$('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', $(this).closest('form').attr('id'), $sForbiddenValues, '$sExplainForbiddenValues'); } );
|
||||||
{
|
{
|
||||||
var myTimer = null;
|
var myTimer = null;
|
||||||
$('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
|
$('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
$sValue = "<textarea $sCSSClasses id=\"$sId\" name=\"$sName\">".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."</textarea>";
|
$sCSSClasses = '';
|
||||||
}
|
if (count($this->aCSSClasses) > 0)
|
||||||
else
|
|
||||||
{
|
{
|
||||||
$sValue = "<div $sCSSClasses id=\"$sId\">".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."</div>";
|
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
||||||
}
|
}
|
||||||
return array('label' => $this->sLabel, 'value' => $sValue);
|
return array('label' => $this->sLabel, 'value' => "<textarea $sCSSClasses id=\"$sId\" $sReadOnly name=\"$sName\">".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."</textarea>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1068,7 +1065,6 @@ class DesignerComboField extends DesignerFormField
|
|||||||
protected $bMultipleSelection;
|
protected $bMultipleSelection;
|
||||||
protected $bOtherChoices;
|
protected $bOtherChoices;
|
||||||
protected $sNullLabel;
|
protected $sNullLabel;
|
||||||
protected $bSorted;
|
|
||||||
|
|
||||||
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
||||||
{
|
{
|
||||||
@@ -1079,7 +1075,6 @@ class DesignerComboField extends DesignerFormField
|
|||||||
$this->sNullLabel = Dict::S('UI:SelectOne');
|
$this->sNullLabel = Dict::S('UI:SelectOne');
|
||||||
|
|
||||||
$this->bAutoApply = true;
|
$this->bAutoApply = true;
|
||||||
$this->bSorted = true; // Sorted by default
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SetAllowedValues($aAllowedValues)
|
public function SetAllowedValues($aAllowedValues)
|
||||||
@@ -1105,16 +1100,6 @@ class DesignerComboField extends DesignerFormField
|
|||||||
$this->sNullLabel = $sLabel;
|
$this->sNullLabel = $sLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function IsSorted()
|
|
||||||
{
|
|
||||||
return $this->bSorted;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function SetSorted($bSorted)
|
|
||||||
{
|
|
||||||
$this->bSorted = $bSorted;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
|
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
|
||||||
{
|
{
|
||||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||||
@@ -1122,10 +1107,6 @@ class DesignerComboField extends DesignerFormField
|
|||||||
$sChecked = $this->defaultValue ? 'checked' : '';
|
$sChecked = $this->defaultValue ? 'checked' : '';
|
||||||
$sMandatory = $this->bMandatory ? 'true' : 'false';
|
$sMandatory = $this->bMandatory ? 'true' : 'false';
|
||||||
$sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
|
$sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
|
||||||
if ($this->IsSorted())
|
|
||||||
{
|
|
||||||
asort($this->aAllowedValues);
|
|
||||||
}
|
|
||||||
$sCSSClasses = '';
|
$sCSSClasses = '';
|
||||||
if (count($this->aCSSClasses) > 0)
|
if (count($this->aCSSClasses) > 0)
|
||||||
{
|
{
|
||||||
@@ -1338,7 +1319,7 @@ EOF
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$sValue = '<span style="display:inline-block;line-height:48px;height:48px;"><span><img style="vertical-align:middle" src="'.$this->aAllowedValues[$idx]['icon'].'" /> '.htmlentities($this->aAllowedValues[$idx]['label'], ENT_QUOTES, 'UTF-8').'</span></span>';
|
$sValue = '<img src="'.$this->MakeFileUrl($this->defaultValue).'" />';
|
||||||
}
|
}
|
||||||
$sReadOnly = $this->IsReadOnly() ? 'disabled' : '';
|
$sReadOnly = $this->IsReadOnly() ? 'disabled' : '';
|
||||||
return array('label' => $this->sLabel, 'value' => $sValue);
|
return array('label' => $this->sLabel, 'value' => $sValue);
|
||||||
@@ -1347,19 +1328,14 @@ EOF
|
|||||||
|
|
||||||
class RunTimeIconSelectionField extends DesignerIconSelectionField
|
class RunTimeIconSelectionField extends DesignerIconSelectionField
|
||||||
{
|
{
|
||||||
static $aAllIcons = array();
|
|
||||||
|
|
||||||
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
||||||
{
|
{
|
||||||
parent::__construct($sCode, $sLabel, $defaultValue);
|
parent::__construct($sCode, $sLabel, $defaultValue);
|
||||||
|
|
||||||
if (count(self::$aAllIcons) == 0)
|
$aAllIcons = self::FindIconsOnDisk(APPROOT.'env-'.utils::GetCurrentEnvironment());
|
||||||
{
|
ksort($aAllIcons);
|
||||||
self::$aAllIcons = self::FindIconsOnDisk(APPROOT.'env-'.utils::GetCurrentEnvironment());
|
|
||||||
ksort(self::$aAllIcons);
|
|
||||||
}
|
|
||||||
$aValues = array();
|
$aValues = array();
|
||||||
foreach(self::$aAllIcons as $sFilePath)
|
foreach($aAllIcons as $sFilePath)
|
||||||
{
|
{
|
||||||
$aValues[] = array('value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => utils::GetAbsoluteUrlModulesRoot().$sFilePath);
|
$aValues[] = array('value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => utils::GetAbsoluteUrlModulesRoot().$sFilePath);
|
||||||
}
|
}
|
||||||
@@ -1452,37 +1428,13 @@ class DesignerFormSelectorField extends DesignerFormField
|
|||||||
{
|
{
|
||||||
protected $aSubForms;
|
protected $aSubForms;
|
||||||
protected $defaultRealValue; // What's stored as default value is actually the index
|
protected $defaultRealValue; // What's stored as default value is actually the index
|
||||||
protected $bSorted;
|
|
||||||
|
|
||||||
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
||||||
{
|
{
|
||||||
parent::__construct($sCode, $sLabel, 0);
|
parent::__construct($sCode, $sLabel, 0);
|
||||||
$this->defaultRealValue = $defaultValue;
|
$this->defaultRealValue = $defaultValue;
|
||||||
$this->aSubForms = array();
|
$this->aSubForms = array();
|
||||||
$this->bSorted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function IsSorted()
|
|
||||||
{
|
|
||||||
return $this->bSorted;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SetSorted($bSorted)
|
|
||||||
{
|
|
||||||
$this->bSorted = $bSorted;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback for sorting an array of $aFormData based ont he labels of the subforms
|
|
||||||
* @param unknown $aItem1
|
|
||||||
* @param unknown $aItem2
|
|
||||||
* @return number
|
|
||||||
*/
|
|
||||||
static function SortOnFormLabel($aItem1, $aItem2)
|
|
||||||
{
|
|
||||||
return strcasecmp($aItem1['label'], $aItem2['label']);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetWidgetClass()
|
public function GetWidgetClass()
|
||||||
{
|
{
|
||||||
return 'selector_property_field';
|
return 'selector_property_field';
|
||||||
@@ -1513,10 +1465,6 @@ class DesignerFormSelectorField extends DesignerFormField
|
|||||||
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->IsSorted())
|
|
||||||
{
|
|
||||||
uasort($this->aSubForms, array(get_class($this), 'SortOnFormLabel'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->IsReadOnly())
|
if ($this->IsReadOnly())
|
||||||
{
|
{
|
||||||
@@ -1542,10 +1490,9 @@ class DesignerFormSelectorField extends DesignerFormField
|
|||||||
$sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\" $sReadOnly>";
|
$sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\" $sReadOnly>";
|
||||||
foreach($this->aSubForms as $iKey => $aFormData)
|
foreach($this->aSubForms as $iKey => $aFormData)
|
||||||
{
|
{
|
||||||
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');
|
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');;
|
||||||
$sValue = htmlentities($aFormData['value'], ENT_QUOTES, 'UTF-8');
|
|
||||||
$sSelected = ($iKey == $this->defaultValue) ? 'selected' : '';
|
$sSelected = ($iKey == $this->defaultValue) ? 'selected' : '';
|
||||||
$sHtml .= "<option data-value=\"$sValue\" value=\"$iKey\" $sSelected>".$sDisplayValue."</option>";
|
$sHtml .= "<option value=\"$iKey\" $sSelected>".$sDisplayValue."</option>";
|
||||||
}
|
}
|
||||||
$sHtml .= "</select>";
|
$sHtml .= "</select>";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2014 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
/**
|
/**
|
||||||
* Class iTopWebPage
|
* Class iTopWebPage
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2013 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -38,9 +38,9 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
|
|||||||
private $m_sInitScript;
|
private $m_sInitScript;
|
||||||
protected $m_oTabs;
|
protected $m_oTabs;
|
||||||
|
|
||||||
public function __construct($sTitle, $bPrintable = false)
|
public function __construct($sTitle)
|
||||||
{
|
{
|
||||||
parent::__construct($sTitle, $bPrintable);
|
parent::__construct($sTitle);
|
||||||
$this->m_oTabs = new TabManager();
|
$this->m_oTabs = new TabManager();
|
||||||
|
|
||||||
ApplicationContext::SetUrlMakerClass('iTopStandardURLMaker');
|
ApplicationContext::SetUrlMakerClass('iTopStandardURLMaker');
|
||||||
@@ -73,24 +73,22 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
|
|||||||
$this->add_linked_script('../js/g.pie.js');
|
$this->add_linked_script('../js/g.pie.js');
|
||||||
$this->add_linked_script('../js/g.dot.js');
|
$this->add_linked_script('../js/g.dot.js');
|
||||||
$this->add_linked_script('../js/charts.js');
|
$this->add_linked_script('../js/charts.js');
|
||||||
$this->add_linked_script('../js/jquery.multiselect.js');
|
$this->add_linked_script('../js/jquery.multiselect.min.js');
|
||||||
$this->add_linked_script('../js/ajaxfileupload.js');
|
$this->add_linked_script('../js/ajaxfileupload.js');
|
||||||
$this->add_linked_script('../js/jquery.mousewheel.js');
|
|
||||||
|
|
||||||
|
$aMultiselectOptions = array(
|
||||||
|
'header' => true,
|
||||||
|
'checkAllText' => Dict::S('UI:SearchValue:CheckAll'),
|
||||||
|
'uncheckAllText' => Dict::S('UI:SearchValue:UncheckAll'),
|
||||||
|
'noneSelectedText' => Dict::S('UI:SearchValue:Any'),
|
||||||
|
'selectedText' => Dict::S('UI:SearchValue:NbSelected'),
|
||||||
|
'selectedList' => 1,
|
||||||
|
);
|
||||||
|
$sJSMultiselectOptions = json_encode($aMultiselectOptions);
|
||||||
$sSearchAny = addslashes(Dict::S('UI:SearchValue:Any'));
|
$sSearchAny = addslashes(Dict::S('UI:SearchValue:Any'));
|
||||||
$sSearchNbSelected = addslashes(Dict::S('UI:SearchValue:NbSelected'));
|
$sSearchNbSelected = addslashes(Dict::S('UI:SearchValue:NbSelected'));
|
||||||
$this->add_dict_entry('UI:FillAllMandatoryFields');
|
$this->add_dict_entry('UI:FillAllMandatoryFields');
|
||||||
$this->add_dict_entry('UI:Button:Cancel');
|
|
||||||
$this->add_dict_entry('UI:Button:Done');
|
|
||||||
|
|
||||||
if (!$this->IsPrintableVersion())
|
|
||||||
{
|
|
||||||
$this->PrepareLayout();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function PrepareLayout()
|
|
||||||
{
|
|
||||||
$bForceMenuPane = utils::ReadParam('force_menu_pane', null);
|
$bForceMenuPane = utils::ReadParam('force_menu_pane', null);
|
||||||
$sInitClosed = '';
|
$sInitClosed = '';
|
||||||
if (($bForceMenuPane !== null) && ($bForceMenuPane == 0))
|
if (($bForceMenuPane !== null) && ($bForceMenuPane == 0))
|
||||||
@@ -126,17 +124,6 @@ EOF
|
|||||||
myLayout.addPinBtn( "#tPinMenu", "west" );
|
myLayout.addPinBtn( "#tPinMenu", "west" );
|
||||||
EOF;
|
EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$sJSDisconnectedMessage = json_encode(Dict::S('UI:DisconnectedDlgMessage'));
|
|
||||||
$sJSTitle = json_encode(Dict::S('UI:DisconnectedDlgTitle'));
|
|
||||||
$sJSLoginAgain = json_encode(Dict::S('UI:LoginAgain'));
|
|
||||||
$sJSStayOnThePage = json_encode(Dict::S('UI:StayOnThePage'));
|
|
||||||
$sJSDaysMin = json_encode(array(Dict::S('DayOfWeek-Sunday-Min'), Dict::S('DayOfWeek-Monday-Min'), Dict::S('DayOfWeek-Tuesday-Min'), Dict::S('DayOfWeek-Wednesday-Min'),
|
|
||||||
Dict::S('DayOfWeek-Thursday-Min'), Dict::S('DayOfWeek-Friday-Min'), Dict::S('DayOfWeek-Saturday-Min')));
|
|
||||||
$sJSMonthsShort = json_encode(array(Dict::S('Month-01-Short'), Dict::S('Month-02-Short'), Dict::S('Month-03-Short'), Dict::S('Month-04-Short'), Dict::S('Month-05-Short'), Dict::S('Month-06-Short'),
|
|
||||||
Dict::S('Month-07-Short'), Dict::S('Month-08-Short'), Dict::S('Month-09-Short'), Dict::S('Month-10-Short'), Dict::S('Month-11-Short'), Dict::S('Month-12-Short')));
|
|
||||||
$iFirstDayOfWeek = (int) Dict::S('Calendar-FirstDayOfWeek');
|
|
||||||
|
|
||||||
$this->m_sInitScript =
|
$this->m_sInitScript =
|
||||||
<<< EOF
|
<<< EOF
|
||||||
@@ -184,7 +171,6 @@ EOF;
|
|||||||
var innerWidth = $(this).innerWidth() - 10;
|
var innerWidth = $(this).innerWidth() - 10;
|
||||||
$(this).find('.item').width(innerWidth);
|
$(this).find('.item').width(innerWidth);
|
||||||
});
|
});
|
||||||
$('.panel-resized').trigger('resized');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -240,7 +226,9 @@ EOF;
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('.multiselect').multiselect($sJSMultiselectOptions);
|
||||||
|
|
||||||
$('.resizable').filter(':visible').resizable();
|
$('.resizable').filter(':visible').resizable();
|
||||||
}
|
}
|
||||||
catch(err)
|
catch(err)
|
||||||
@@ -309,21 +297,6 @@ EOF
|
|||||||
$.bbq.pushState( state );
|
$.bbq.pushState( state );
|
||||||
});
|
});
|
||||||
|
|
||||||
// refresh the hash when the tab is changed (from a JS script)
|
|
||||||
$('body').on( 'tabsactivate', '.ui-tabs', function(event, ui) {
|
|
||||||
var state = {};
|
|
||||||
|
|
||||||
// Get the id of this tab widget.
|
|
||||||
var id = $(ui.newTab).closest( 'div[id^=tabbedContent]' ).attr( 'id' );
|
|
||||||
|
|
||||||
// Get the index of this tab.
|
|
||||||
var idx = $(ui.newTab).prevAll().length;
|
|
||||||
|
|
||||||
// Set the state!
|
|
||||||
state[ id ] = idx;
|
|
||||||
$.bbq.pushState( state );
|
|
||||||
});
|
|
||||||
|
|
||||||
// Bind an event to window.onhashchange that, when the history state changes,
|
// Bind an event to window.onhashchange that, when the history state changes,
|
||||||
// iterates over all tab widgets, changing the current tab as necessary.
|
// iterates over all tab widgets, changing the current tab as necessary.
|
||||||
$(window).bind( 'hashchange', function(e)
|
$(window).bind( 'hashchange', function(e)
|
||||||
@@ -381,10 +354,7 @@ EOF
|
|||||||
dateFormat: 'yy-mm-dd',
|
dateFormat: 'yy-mm-dd',
|
||||||
constrainInput: false,
|
constrainInput: false,
|
||||||
changeMonth: true,
|
changeMonth: true,
|
||||||
changeYear: true,
|
changeYear: true
|
||||||
dayNamesMin: $sJSDaysMin,
|
|
||||||
monthNamesShort: $sJSMonthsShort,
|
|
||||||
firstDay: $iFirstDayOfWeek
|
|
||||||
});
|
});
|
||||||
$(".datetime-pick").datepicker({
|
$(".datetime-pick").datepicker({
|
||||||
showOn: 'button',
|
showOn: 'button',
|
||||||
@@ -393,10 +363,7 @@ EOF
|
|||||||
dateFormat: 'yy-mm-dd 00:00:00',
|
dateFormat: 'yy-mm-dd 00:00:00',
|
||||||
constrainInput: false,
|
constrainInput: false,
|
||||||
changeMonth: true,
|
changeMonth: true,
|
||||||
changeYear: true,
|
changeYear: true
|
||||||
dayNamesMin: $sJSDaysMin,
|
|
||||||
monthNamesShort: $sJSMonthsShort,
|
|
||||||
firstDay: $iFirstDayOfWeek
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Make sortable, everything that claims to be sortable
|
// Make sortable, everything that claims to be sortable
|
||||||
@@ -417,26 +384,6 @@ EOF
|
|||||||
$('#logOffBtn>ul').popupmenu();
|
$('#logOffBtn>ul').popupmenu();
|
||||||
|
|
||||||
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry').toggle(); });
|
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry').toggle(); });
|
||||||
|
|
||||||
$(document).ajaxSend(function(event, jqxhr, options) {
|
|
||||||
jqxhr.setRequestHeader('X-Combodo-Ajax', 'true');
|
|
||||||
});
|
|
||||||
$(document).ajaxError(function(event, jqxhr, options) {
|
|
||||||
if (jqxhr.status == 401)
|
|
||||||
{
|
|
||||||
$('<div>'+$sJSDisconnectedMessage+'</div>').dialog({
|
|
||||||
modal:true,
|
|
||||||
title: $sJSTitle,
|
|
||||||
close: function() { $(this).remove(); },
|
|
||||||
minWidth: 400,
|
|
||||||
buttons: [
|
|
||||||
{ text: $sJSLoginAgain, click: function() { window.location.href= GetAbsoluteUrlAppRoot()+'pages/UI.php' } },
|
|
||||||
{ text: $sJSStayOnThePage, click: function() { $(this).dialog('close'); } }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
$sUserPrefs = appUserPreferences::GetAsJSON();
|
$sUserPrefs = appUserPreferences::GetAsJSON();
|
||||||
@@ -459,17 +406,12 @@ EOF
|
|||||||
window.history.back();
|
window.history.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
function BackToDetails(sClass, id, sDefaultUrl, sOwnershipToken)
|
function BackToDetails(sClass, id, sDefaultUrl)
|
||||||
{
|
{
|
||||||
window.bInCancel = true;
|
window.bInCancel = true;
|
||||||
if (id > 0)
|
if (id > 0)
|
||||||
{
|
{
|
||||||
sToken = '';
|
window.location.href = AddAppContext(GetAbsoluteUrlAppRoot()+'pages/UI.php?operation=details&class='+sClass+'&id='+id);
|
||||||
if (sOwnershipToken != undefined)
|
|
||||||
{
|
|
||||||
sToken = '&token='+sOwnershipToken;
|
|
||||||
}
|
|
||||||
window.location.href = AddAppContext(GetAbsoluteUrlAppRoot()+'pages/UI.php?operation=release_lock_and_details&class='+sClass+'&id='+id+sToken);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -477,6 +419,7 @@ EOF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function BackToList(sClass)
|
function BackToList(sClass)
|
||||||
{
|
{
|
||||||
window.location.href = AddAppContext(GetAbsoluteUrlAppRoot()+'pages/UI.php?operation=search_oql&oql_class='+sClass+'&oql_clause=WHERE id=0');
|
window.location.href = AddAppContext(GetAbsoluteUrlAppRoot()+'pages/UI.php?operation=search_oql&oql_class='+sClass+'&oql_clause=WHERE id=0');
|
||||||
@@ -570,7 +513,7 @@ EOF
|
|||||||
array('iFieldSize' => 20, 'iMinChars' => MetaModel::GetConfig()->Get('min_autocomplete_chars'), 'sDefaultValue' => Dict::S('UI:AllOrganizations')),
|
array('iFieldSize' => 20, 'iMinChars' => MetaModel::GetConfig()->Get('min_autocomplete_chars'), 'sDefaultValue' => Dict::S('UI:AllOrganizations')),
|
||||||
null, 'select', false /* bSearchMultiple */);
|
null, 'select', false /* bSearchMultiple */);
|
||||||
$this->add_ready_script('$("#org_id").bind("extkeychange", function() { $("#SiloSelection form").submit(); } )');
|
$this->add_ready_script('$("#org_id").bind("extkeychange", function() { $("#SiloSelection form").submit(); } )');
|
||||||
$this->add_ready_script("$('#label_org_id').click( function() { if ($('#org_id').val() == '') { $(this).val(''); } } );\n");
|
$this->add_ready_script("$('#label_org_id').click( function() { $(this).val(''); $('#org_id').val(''); return true; } );\n");
|
||||||
// Add other dimensions/context information to this form
|
// Add other dimensions/context information to this form
|
||||||
$oAppContext->Reset('org_id'); // org_id is handled above and we want to be able to change it here !
|
$oAppContext->Reset('org_id'); // org_id is handled above and we want to be able to change it here !
|
||||||
$oAppContext->Reset('menu'); // don't pass the menu, since a menu may expect more parameters
|
$oAppContext->Reset('menu'); // don't pass the menu, since a menu may expect more parameters
|
||||||
@@ -625,21 +568,8 @@ EOF
|
|||||||
{
|
{
|
||||||
$sSouthPane .= $oExtensionInstance->GetSouthPaneHtml($this);
|
$sSouthPane .= $oExtensionInstance->GetSouthPaneHtml($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render the tabs in the page (if any)
|
|
||||||
$this->s_content = $this->m_oTabs->RenderIntoContent($this->s_content, $this);
|
|
||||||
|
|
||||||
// Put here the 'ready scripts' that must be executed after all others
|
// Put here the 'ready scripts' that must be executed after all others
|
||||||
$aMultiselectOptions = array(
|
|
||||||
'header' => true,
|
|
||||||
'checkAllText' => Dict::S('UI:SearchValue:CheckAll'),
|
|
||||||
'uncheckAllText' => Dict::S('UI:SearchValue:UncheckAll'),
|
|
||||||
'noneSelectedText' => Dict::S('UI:SearchValue:Any'),
|
|
||||||
'selectedText' => Dict::S('UI:SearchValue:NbSelected'),
|
|
||||||
'selectedList' => 1,
|
|
||||||
);
|
|
||||||
$sJSMultiselectOptions = json_encode($aMultiselectOptions);
|
|
||||||
|
|
||||||
$this->add_ready_script(
|
$this->add_ready_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
// Since the event is only triggered when the hash changes, we need to trigger
|
// Since the event is only triggered when the hash changes, we need to trigger
|
||||||
@@ -648,11 +578,6 @@ EOF
|
|||||||
|
|
||||||
// Some table are sort-able, some are not, let's fix this
|
// Some table are sort-able, some are not, let's fix this
|
||||||
$('table.listResults').each( function() { FixTableSorter($(this)); } );
|
$('table.listResults').each( function() { FixTableSorter($(this)); } );
|
||||||
|
|
||||||
$('.multiselect').multiselect($sJSMultiselectOptions);
|
|
||||||
|
|
||||||
FixSearchFormsDisposition();
|
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
if ($this->GetOutputFormat() == 'html')
|
if ($this->GetOutputFormat() == 'html')
|
||||||
@@ -675,26 +600,18 @@ EOF
|
|||||||
// jQuery scripts may face some spurious problems (like failing on a 'reload')
|
// jQuery scripts may face some spurious problems (like failing on a 'reload')
|
||||||
foreach($this->a_linked_stylesheets as $a_stylesheet)
|
foreach($this->a_linked_stylesheets as $a_stylesheet)
|
||||||
{
|
{
|
||||||
if (strpos($a_stylesheet['link'], '?') === false)
|
|
||||||
{
|
|
||||||
$s_stylesheet = $a_stylesheet['link']."?itopversion=".ITOP_VERSION;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$s_stylesheet = $a_stylesheet['link']."&itopversion=".ITOP_VERSION;
|
|
||||||
}
|
|
||||||
if ($a_stylesheet['condition'] != "")
|
if ($a_stylesheet['condition'] != "")
|
||||||
{
|
{
|
||||||
$sHtml .= "<!--[if {$a_stylesheet['condition']}]>\n";
|
$sHtml .= "<!--[if {$a_stylesheet['condition']}]>\n";
|
||||||
}
|
}
|
||||||
$sHtml .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"{$s_stylesheet}\" />\n";
|
$sHtml .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"{$a_stylesheet['link']}\" />\n";
|
||||||
if ($a_stylesheet['condition'] != "")
|
if ($a_stylesheet['condition'] != "")
|
||||||
{
|
{
|
||||||
$sHtml .= "<![endif]-->\n";
|
$sHtml .= "<![endif]-->\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// special stylesheet for printing, hides the navigation gadgets
|
// special stylesheet for printing, hides the navigation gadgets
|
||||||
$sHtml .= "<link rel=\"stylesheet\" media=\"print\" type=\"text/css\" href=\"../css/print.css?itopversion=".ITOP_VERSION."\" />\n";
|
$sHtml .= "<link rel=\"stylesheet\" media=\"print\" type=\"text/css\" href=\"../css/print.css\" />\n";
|
||||||
|
|
||||||
if ($this->GetOutputFormat() == 'html')
|
if ($this->GetOutputFormat() == 'html')
|
||||||
{
|
{
|
||||||
@@ -713,40 +630,7 @@ EOF
|
|||||||
}
|
}
|
||||||
$sHtml .= "<script type=\"text/javascript\" src=\"$s_script\"></script>\n";
|
$sHtml .= "<script type=\"text/javascript\" src=\"$s_script\"></script>\n";
|
||||||
}
|
}
|
||||||
if (!$this->IsPrintableVersion())
|
$this->add_script("var iPaneVisWatchDog = window.setTimeout('FixPaneVis()',5000);\n\$(document).ready(function() {\n{$this->m_sInitScript};\nwindow.setTimeout('onDelayedReady()',10)\n});");
|
||||||
{
|
|
||||||
$this->add_script("var iPaneVisWatchDog = window.setTimeout('FixPaneVis()',5000);");
|
|
||||||
}
|
|
||||||
$this->add_script("\$(document).ready(function() {\n{$this->m_sInitScript};\nwindow.setTimeout('onDelayedReady()',10)\n});");
|
|
||||||
if ($this->IsPrintableVersion())
|
|
||||||
{
|
|
||||||
$this->add_ready_script(
|
|
||||||
<<<EOF
|
|
||||||
var sHiddeableChapters = '<div class="light ui-tabs ui-widget ui-widget-content ui-corner-all">';
|
|
||||||
sHiddeableChapters += '<ul role="tablist" class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all">';
|
|
||||||
for (sId in oHiddeableChapters)
|
|
||||||
{
|
|
||||||
sHiddeableChapters += '<li tabindex="-1" role="tab" class="ui-state-default ui-corner-top hideable-chapter" chapter-id="'+sId+'"><span class="tab ui-tabs-anchor">' + oHiddeableChapters[sId] + '</span></li>';
|
|
||||||
//alert(oHiddeableChapters[sId]);
|
|
||||||
}
|
|
||||||
sHiddeableChapters += '</ul></div>';
|
|
||||||
$('#hiddeable_chapters').html(sHiddeableChapters);
|
|
||||||
$('.hideable-chapter').click(function(){
|
|
||||||
var sChapterId = $(this).attr('chapter-id');
|
|
||||||
$('#'+sChapterId).toggle();
|
|
||||||
$(this).toggleClass('strikethrough');
|
|
||||||
});
|
|
||||||
$('fieldset').each(function() {
|
|
||||||
var jLegend = $(this).find('legend');
|
|
||||||
jLegend.remove();
|
|
||||||
$(this).wrapInner('<span></span>').prepend(jLegend);
|
|
||||||
});
|
|
||||||
$('legend').css('cursor', 'pointer').click(function(){
|
|
||||||
$(this).parent('fieldset').toggleClass('not-printable strikethrough');
|
|
||||||
});
|
|
||||||
EOF
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (count($this->m_aReadyScripts)>0)
|
if (count($this->m_aReadyScripts)>0)
|
||||||
{
|
{
|
||||||
$this->add_script("\nonDelayedReady = function() {\n".implode("\n", $this->m_aReadyScripts)."\n}\n");
|
$this->add_script("\nonDelayedReady = function() {\n".implode("\n", $this->m_aReadyScripts)."\n}\n");
|
||||||
@@ -772,25 +656,10 @@ EOF
|
|||||||
$sHtml .= "</style>\n";
|
$sHtml .= "</style>\n";
|
||||||
}
|
}
|
||||||
$sHtml .= "<link rel=\"search\" type=\"application/opensearchdescription+xml\" title=\"iTop\" href=\"".utils::GetAbsoluteUrlAppRoot()."pages/opensearch.xml.php\" />\n";
|
$sHtml .= "<link rel=\"search\" type=\"application/opensearchdescription+xml\" title=\"iTop\" href=\"".utils::GetAbsoluteUrlAppRoot()."pages/opensearch.xml.php\" />\n";
|
||||||
$sHtml .= "<link rel=\"shortcut icon\" href=\"".utils::GetAbsoluteUrlAppRoot()."images/favicon.ico?itopversion=".ITOP_VERSION."\" />\n";
|
$sHtml .= "<link rel=\"shortcut icon\" href=\"".utils::GetAbsoluteUrlAppRoot()."images/favicon.ico\" />\n";
|
||||||
|
|
||||||
$sHtml .= "</head>\n";
|
$sHtml .= "</head>\n";
|
||||||
$sBodyClass = "";
|
$sHtml .= "<body>\n";
|
||||||
if ($this->IsPrintableVersion())
|
|
||||||
{
|
|
||||||
$sBodyClass = 'printable-version';
|
|
||||||
}
|
|
||||||
$sHtml .= "<body class=\"$sBodyClass\">\n";
|
|
||||||
if ($this->IsPrintableVersion())
|
|
||||||
{
|
|
||||||
$sHtml .= "<div class=\"explain-printable not-printable\">";
|
|
||||||
$sHtml .= '<p>'.Dict::Format('UI:ExplainPrintable', '<img src="../images/eye-open-555.png" style="vertical-align:middle">').'</p>';
|
|
||||||
$sHtml .= "<div id=\"hiddeable_chapters\"></div>";
|
|
||||||
$sHtml .= '<button onclick="window.print()">'.htmlentities(Dict::S('UI:Button:GoPrint'), ENT_QUOTES, 'UTF-8').'</button>';
|
|
||||||
$sHtml .= ' ';
|
|
||||||
$sHtml .= '<button onclick="window.close()">'.htmlentities(Dict::S('UI:Button:Cancel'), ENT_QUOTES, 'UTF-8').'</button>';
|
|
||||||
$sHtml .= "</div>";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render the revision number
|
// Render the revision number
|
||||||
if (ITOP_REVISION == '$WCREV$')
|
if (ITOP_REVISION == '$WCREV$')
|
||||||
@@ -806,19 +675,19 @@ EOF
|
|||||||
|
|
||||||
// Render the text of the global search form
|
// Render the text of the global search form
|
||||||
$sText = htmlentities(utils::ReadParam('text', '', false, 'raw_data'), ENT_QUOTES, 'UTF-8');
|
$sText = htmlentities(utils::ReadParam('text', '', false, 'raw_data'), ENT_QUOTES, 'UTF-8');
|
||||||
$sOnClick = " onclick=\"if ($('#global-search-input').val() != '') { $('#global-search form').submit(); } \"";
|
$sOnClick = "";
|
||||||
if (empty($sText))
|
if (empty($sText))
|
||||||
{
|
{
|
||||||
|
// if no search text is supplied then
|
||||||
|
// 1) the search text is filled with "your search"
|
||||||
|
// 2) clicking on it will erase it
|
||||||
$sText = Dict::S("UI:YourSearch");
|
$sText = Dict::S("UI:YourSearch");
|
||||||
|
$sOnClick = " onclick=\"this.value='';this.onclick=null;\"";
|
||||||
}
|
}
|
||||||
|
// Render the tabs in the page (if any)
|
||||||
|
$this->s_content = $this->m_oTabs->RenderIntoContent($this->s_content);
|
||||||
|
|
||||||
if ($this->IsPrintableVersion())
|
if ($this->GetOutputFormat() == 'html')
|
||||||
{
|
|
||||||
$sHtml .= ' <!-- Beginning of page content -->';
|
|
||||||
$sHtml .= self::FilterXSS($this->s_content);
|
|
||||||
$sHtml .= ' <!-- End of page content -->';
|
|
||||||
}
|
|
||||||
elseif ($this->GetOutputFormat() == 'html')
|
|
||||||
{
|
{
|
||||||
$oAppContext = new ApplicationContext();
|
$oAppContext = new ApplicationContext();
|
||||||
|
|
||||||
@@ -832,7 +701,7 @@ EOF
|
|||||||
{
|
{
|
||||||
$sLogonMessage = Dict::Format('UI:LoggedAsMessage', $sUserName);
|
$sLogonMessage = Dict::Format('UI:LoggedAsMessage', $sUserName);
|
||||||
}
|
}
|
||||||
$sLogOffMenu = "<span id=\"logOffBtn\"><ul><li><img src=\"../images/on-off-menu.png\"><ul>";
|
$sLogOffMenu = "<span id=\"logOffBtn\"><ul><li><img src=\"../images/onOffBtn.png\"><ul>";
|
||||||
$sLogOffMenu .= "<li><span>$sLogonMessage</span></li>\n";
|
$sLogOffMenu .= "<li><span>$sLogonMessage</span></li>\n";
|
||||||
$aActions = array();
|
$aActions = array();
|
||||||
|
|
||||||
@@ -841,7 +710,7 @@ EOF
|
|||||||
|
|
||||||
if (utils::CanLogOff())
|
if (utils::CanLogOff())
|
||||||
{
|
{
|
||||||
$oLogOff = new URLPopupMenuItem('UI:LogOffMenu', Dict::S('UI:LogOffMenu'), utils::GetAbsoluteUrlAppRoot().'pages/logoff.php?operation=do_logoff');
|
$oLogOff = new URLPopupMenuItem('UI:LogOffMenu', Dict::S('UI:LogOffMenu'), utils::GetAbsoluteUrlAppRoot().'pages/logoff.php');
|
||||||
$aActions[$oLogOff->GetUID()] = $oLogOff->GetMenuItem();
|
$aActions[$oLogOff->GetUID()] = $oLogOff->GetMenuItem();
|
||||||
}
|
}
|
||||||
if (UserRights::CanChangePassword())
|
if (UserRights::CanChangePassword())
|
||||||
@@ -905,10 +774,10 @@ EOF
|
|||||||
$sOnlineHelpUrl = MetaModel::GetConfig()->Get('online_help');
|
$sOnlineHelpUrl = MetaModel::GetConfig()->Get('online_help');
|
||||||
//$sLogOffMenu = "<span id=\"logOffBtn\" style=\"height:55px;padding:0;margin:0;\"><img src=\"../images/onOffBtn.png\"></span>";
|
//$sLogOffMenu = "<span id=\"logOffBtn\" style=\"height:55px;padding:0;margin:0;\"><img src=\"../images/onOffBtn.png\"></span>";
|
||||||
|
|
||||||
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/itop-logo.png?itopversion='.ITOP_VERSION;
|
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/itop-logo.png';
|
||||||
if (file_exists(MODULESROOT.'branding/main-logo.png'))
|
if (file_exists(MODULESROOT.'branding/main-logo.png'))
|
||||||
{
|
{
|
||||||
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/main-logo.png?itopversion='.ITOP_VERSION;
|
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/main-logo.png';
|
||||||
}
|
}
|
||||||
|
|
||||||
$sHtml .= $sNorthPane;
|
$sHtml .= $sNorthPane;
|
||||||
@@ -935,17 +804,17 @@ EOF
|
|||||||
$sHtml .= ' </div>';
|
$sHtml .= ' </div>';
|
||||||
$sHtml .= ' </div> <!-- /inner menu -->';
|
$sHtml .= ' </div> <!-- /inner menu -->';
|
||||||
$sHtml .= ' </div> <!-- /menu -->';
|
$sHtml .= ' </div> <!-- /menu -->';
|
||||||
$sHtml .= ' <div class="footer ui-layout-south"><div id="combodo_logo"><a href="http://www.combodo.com" title="www.combodo.com" target="_blank"><img src="../images/logo-combodo.png?itopversion='.ITOP_VERSION.'"/></a></div></div>';
|
$sHtml .= ' <div class="footer ui-layout-south"><div id="combodo_logo"><a href="http://www.combodo.com" title="www.combodo.com" target="_blank"><img src="../images/logo-combodo.png"/></a></div></div>';
|
||||||
$sHtml .= '<!-- End of the left pane -->';
|
$sHtml .= '<!-- End of the left pane -->';
|
||||||
$sHtml .= '</div>';
|
$sHtml .= '</div>';
|
||||||
|
|
||||||
$sHtml .= '<div class="ui-layout-center">';
|
$sHtml .= '<div class="ui-layout-center">';
|
||||||
$sHtml .= ' <div id="top-bar" style="width:100%">';
|
$sHtml .= ' <div id="top-bar" style="width:100%">';
|
||||||
$sHtml .= self::FilterXSS($sApplicationBanner);
|
$sHtml .= self::FilterXSS($sApplicationBanner);
|
||||||
$sHtml .= ' <div id="global-search"><form action="'.utils::GetAbsoluteUrlAppRoot().'pages/UI.php"><table><tr><td></td><td><div id="global-search-area"><input id="global-search-input" type="text" name="text" placeholder="'.$sText.'"></input><div '.$sOnClick.' id="global-search-image"></div></div></td>';
|
$sHtml .= ' <div id="global-search"><form action="'.utils::GetAbsoluteUrlAppRoot().'pages/UI.php"><table><tr><td></td><td id="g-search-input"><input type="text" name="text" value="'.$sText.'"'.$sOnClick.'/></td>';
|
||||||
//$sHtml .= '<td><input type="image" src="../images/searchBtn.png"/></a></td>';
|
$sHtml .= '<td><input type="image" src="../images/searchBtn.png"/></a></td>';
|
||||||
$sHtml .= '<td><a id="help-link" href="'.$sOnlineHelpUrl.'" target="_blank"><img title="'.Dict::S('UI:Help').'" src="../images/help.png?itopversion='.ITOP_VERSION.'"/></td>';
|
$sHtml .= '<td><a style="background:transparent;" href="'.$sOnlineHelpUrl.'" target="_blank"><img style="border:0;padding-left:20px;padding-right:10px;" title="'.Dict::S('UI:Help').'" src="../images/help.png"/></td>';
|
||||||
$sHtml .= '<td>'.self::FilterXSS($sLogOffMenu).'</td><td><input type="hidden" name="operation" value="full_text"/></td></tr></table></form></div>';
|
$sHtml .= '<td style="padding-right:20px;padding-left:10px;">'.self::FilterXSS($sLogOffMenu).'</td><td><input type="hidden" name="operation" value="full_text"/></td></tr></table></form></div>';
|
||||||
//echo '<td> <input type="hidden" name="operation" value="full_text"/></td></tr></table></form></div>';
|
//echo '<td> <input type="hidden" name="operation" value="full_text"/></td></tr></table></form></div>';
|
||||||
$sHtml .= ' </div>';
|
$sHtml .= ' </div>';
|
||||||
$sHtml .= ' <div class="ui-layout-content" style="overflow:auto;">';
|
$sHtml .= ' <div class="ui-layout-content" style="overflow:auto;">';
|
||||||
@@ -982,31 +851,28 @@ EOF
|
|||||||
}
|
}
|
||||||
else if ($this->GetOutputFormat() == 'pdf' && $this->IsOutputFormatAvailable('pdf') )
|
else if ($this->GetOutputFormat() == 'pdf' && $this->IsOutputFormatAvailable('pdf') )
|
||||||
{
|
{
|
||||||
if (@is_readable(APPROOT.'lib/MPDF/mpdf.php'))
|
require_once(APPROOT.'lib/MPDF/mpdf.php');
|
||||||
|
$oMPDF = new mPDF('c');
|
||||||
|
$oMPDF->mirroMargins = false;
|
||||||
|
if ($this->a_base['href'] != '')
|
||||||
{
|
{
|
||||||
require_once(APPROOT.'lib/MPDF/mpdf.php');
|
$oMPDF->setBasePath($this->a_base['href']); // Seems that the <BASE> tag is not recognized by mPDF...
|
||||||
$oMPDF = new mPDF('c');
|
|
||||||
$oMPDF->mirroMargins = false;
|
|
||||||
if ($this->a_base['href'] != '')
|
|
||||||
{
|
|
||||||
$oMPDF->setBasePath($this->a_base['href']); // Seems that the <BASE> tag is not recognized by mPDF...
|
|
||||||
}
|
|
||||||
$oMPDF->showWatermarkText = true;
|
|
||||||
if ($this->GetOutputOption('pdf', 'template_path'))
|
|
||||||
{
|
|
||||||
$oMPDF->setImportUse(); // Allow templates
|
|
||||||
$oMPDF->SetDocTemplate ($this->GetOutputOption('pdf', 'template_path'), 1);
|
|
||||||
}
|
|
||||||
$oMPDF->WriteHTML($sHtml);
|
|
||||||
$sOutputName = $this->s_title.'.pdf';
|
|
||||||
if ($this->GetOutputOption('pdf', 'output_name'))
|
|
||||||
{
|
|
||||||
$sOutputName = $this->GetOutputOption('pdf', 'output_name');
|
|
||||||
}
|
|
||||||
$oMPDF->Output($sOutputName, 'I');
|
|
||||||
}
|
}
|
||||||
|
$oMPDF->showWatermarkText = true;
|
||||||
|
if ($this->GetOutputOption('pdf', 'template_path'))
|
||||||
|
{
|
||||||
|
$oMPDF->setImportUse(); // Allow templates
|
||||||
|
$oMPDF->SetDocTemplate ($this->GetOutputOption('pdf', 'template_path'), 1);
|
||||||
|
}
|
||||||
|
$oMPDF->WriteHTML($sHtml);
|
||||||
|
$sOutputName = $this->s_title.'.pdf';
|
||||||
|
if ($this->GetOutputOption('pdf', 'output_name'))
|
||||||
|
{
|
||||||
|
$sOutputName = $this->GetOutputOption('pdf', 'output_name');
|
||||||
|
}
|
||||||
|
$oMPDF->Output($sOutputName, 'I');
|
||||||
}
|
}
|
||||||
DBSearch::RecordQueryTrace();
|
MetaModel::RecordQueryTrace();
|
||||||
ExecutionKPI::ReportStats();
|
ExecutionKPI::ReportStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
require_once(APPROOT."/application/nicewebpage.class.inc.php");
|
require_once(APPROOT."/application/nicewebpage.class.inc.php");
|
||||||
require_once(APPROOT.'/application/portaldispatcher.class.inc.php');
|
|
||||||
/**
|
/**
|
||||||
* Web page used for displaying the login form
|
* Web page used for displaying the login form
|
||||||
*/
|
*/
|
||||||
@@ -92,10 +91,10 @@ class LoginWebPage extends NiceWebPage
|
|||||||
}
|
}
|
||||||
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_VERSION);
|
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_VERSION);
|
||||||
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
|
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
|
||||||
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/'.$sLogo.'?itopversion='.ITOP_VERSION;
|
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/'.$sLogo;
|
||||||
if (file_exists(MODULESROOT.'branding/'.$sBrandingLogo))
|
if (file_exists(MODULESROOT.'branding/'.$sBrandingLogo))
|
||||||
{
|
{
|
||||||
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/'.$sBrandingLogo.'?itopversion='.ITOP_VERSION;
|
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/'.$sBrandingLogo;
|
||||||
}
|
}
|
||||||
$this->add("<div id=\"login-logo\"><a href=\"".htmlentities($sIconUrl, ENT_QUOTES, 'UTF-8')."\"><img title=\"$sVersionShort\" src=\"$sDisplayIcon\"></a></div>\n");
|
$this->add("<div id=\"login-logo\"><a href=\"".htmlentities($sIconUrl, ENT_QUOTES, 'UTF-8')."\"><img title=\"$sVersionShort\" src=\"$sDisplayIcon\"></a></div>\n");
|
||||||
}
|
}
|
||||||
@@ -306,20 +305,16 @@ class LoginWebPage extends NiceWebPage
|
|||||||
{
|
{
|
||||||
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser)."</p>\n");
|
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser)."</p>\n");
|
||||||
}
|
}
|
||||||
|
elseif ($oUser->Get('reset_pwd_token') != $sToken)
|
||||||
|
{
|
||||||
|
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$oEncryptedToken = $oUser->Get('reset_pwd_token');
|
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-EnterPassword', $oUser->GetFriendlyName())."</p>\n");
|
||||||
|
|
||||||
if (!$oEncryptedToken->CheckPassword($sToken))
|
$sInconsistenPwdMsg = Dict::S('UI:Login:RetypePwdDoesNotMatch');
|
||||||
{
|
$this->add_script(
|
||||||
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-EnterPassword', $oUser->GetFriendlyName())."</p>\n");
|
|
||||||
|
|
||||||
$sInconsistenPwdMsg = Dict::S('UI:Login:RetypePwdDoesNotMatch');
|
|
||||||
$this->add_script(
|
|
||||||
<<<EOF
|
<<<EOF
|
||||||
function DoCheckPwd()
|
function DoCheckPwd()
|
||||||
{
|
{
|
||||||
@@ -331,19 +326,18 @@ function DoCheckPwd()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
$this->add("<form method=\"post\">\n");
|
$this->add("<form method=\"post\">\n");
|
||||||
$this->add("<table>\n");
|
$this->add("<table>\n");
|
||||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"new_pwd\">".Dict::S('UI:Login:NewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"new_pwd\" name=\"new_pwd\" value=\"\" /></td></tr>\n");
|
$this->add("<tr><td style=\"text-align:right\"><label for=\"new_pwd\">".Dict::S('UI:Login:NewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"new_pwd\" name=\"new_pwd\" value=\"\" /></td></tr>\n");
|
||||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"retype_new_pwd\">".Dict::S('UI:Login:RetypeNewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"retype_new_pwd\" name=\"retype_new_pwd\" value=\"\" /></td></tr>\n");
|
$this->add("<tr><td style=\"text-align:right\"><label for=\"retype_new_pwd\">".Dict::S('UI:Login:RetypeNewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"retype_new_pwd\" name=\"retype_new_pwd\" value=\"\" /></td></tr>\n");
|
||||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"submit\" onClick=\"return DoCheckPwd();\" value=\"".Dict::S('UI:Button:ChangePassword')."\" /></span></td></tr>\n");
|
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"submit\" onClick=\"return DoCheckPwd();\" value=\"".Dict::S('UI:Button:ChangePassword')."\" /></span></td></tr>\n");
|
||||||
$this->add("</table>\n");
|
$this->add("</table>\n");
|
||||||
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"do_reset_pwd\" />\n");
|
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"do_reset_pwd\" />\n");
|
||||||
$this->add("<input type=\"hidden\" name=\"auth_user\" value=\"".htmlentities($sAuthUser, ENT_QUOTES, 'UTF-8')."\" />\n");
|
$this->add("<input type=\"hidden\" name=\"auth_user\" value=\"".htmlentities($sAuthUser, ENT_QUOTES, 'UTF-8')."\" />\n");
|
||||||
$this->add("<input type=\"hidden\" name=\"token\" value=\"".htmlentities($sToken, ENT_QUOTES, 'UTF-8')."\" />\n");
|
$this->add("<input type=\"hidden\" name=\"token\" value=\"".htmlentities($sToken, ENT_QUOTES, 'UTF-8')."\" />\n");
|
||||||
$this->add("</form>\n");
|
$this->add("</form>\n");
|
||||||
$this->add("</div\n");
|
$this->add("</div\n");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,25 +357,21 @@ EOF
|
|||||||
{
|
{
|
||||||
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser)."</p>\n");
|
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser)."</p>\n");
|
||||||
}
|
}
|
||||||
|
elseif ($oUser->Get('reset_pwd_token') != $sToken)
|
||||||
|
{
|
||||||
|
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$oEncryptedPassword = $oUser->Get('reset_pwd_token');
|
// Trash the token and change the password
|
||||||
if (!$oEncryptedPassword->CheckPassword($sToken))
|
$oUser->Set('reset_pwd_token', '');
|
||||||
{
|
$oUser->SetPassword($sNewPwd); // Does record the change into the DB
|
||||||
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
|
|
||||||
}
|
$this->add("<p>".Dict::S('UI:ResetPwd-Ready')."</p>");
|
||||||
else
|
$sUrl = utils::GetAbsoluteUrlAppRoot();
|
||||||
{
|
$this->add("<p><a href=\"$sUrl\">".Dict::S('UI:ResetPwd-Login')."</a></p>");
|
||||||
// Trash the token and change the password
|
|
||||||
$oUser->Set('reset_pwd_token', '');
|
|
||||||
$oUser->SetPassword($sNewPwd); // Does record the change into the DB
|
|
||||||
|
|
||||||
$this->add("<p>".Dict::S('UI:ResetPwd-Ready')."</p>");
|
|
||||||
$sUrl = utils::GetAbsoluteUrlAppRoot();
|
|
||||||
$this->add("<p><a href=\"$sUrl\">".Dict::S('UI:ResetPwd-Login')."</a></p>");
|
|
||||||
}
|
|
||||||
$this->add("</div\n");
|
|
||||||
}
|
}
|
||||||
|
$this->add("</div\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function DisplayChangePwdForm($bFailedLogin = false)
|
public function DisplayChangePwdForm($bFailedLogin = false)
|
||||||
@@ -438,7 +428,6 @@ EOF
|
|||||||
// Unset all of the session variables.
|
// Unset all of the session variables.
|
||||||
unset($_SESSION['auth_user']);
|
unset($_SESSION['auth_user']);
|
||||||
unset($_SESSION['login_mode']);
|
unset($_SESSION['login_mode']);
|
||||||
unset($_SESSION['profile_list']);
|
|
||||||
// If it's desired to kill the session, also delete the session cookie.
|
// If it's desired to kill the session, also delete the session cookie.
|
||||||
// Note: This will destroy the session, and not just the session data!
|
// Note: This will destroy the session, and not just the session data!
|
||||||
}
|
}
|
||||||
@@ -590,13 +579,6 @@ EOF
|
|||||||
{
|
{
|
||||||
$sLoginMode = $aAllowedLoginTypes[0]; // First in the list...
|
$sLoginMode = $aAllowedLoginTypes[0]; // First in the list...
|
||||||
}
|
}
|
||||||
if (array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER))
|
|
||||||
{
|
|
||||||
// X-Combodo-Ajax is a special header automatically added to all ajax requests
|
|
||||||
// Let's reply that we're currently logged-out
|
|
||||||
header('HTTP/1.0 401 Unauthorized');
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
if (($iOnExit == self::EXIT_HTTP_401) || ($sLoginMode == 'basic'))
|
if (($iOnExit == self::EXIT_HTTP_401) || ($sLoginMode == 'basic'))
|
||||||
{
|
{
|
||||||
header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_VERSION));
|
header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_VERSION));
|
||||||
@@ -672,22 +654,12 @@ EOF
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Overridable: depending on the user, head toward a dedicated portal
|
* Overridable: depending on the user, head toward a dedicated portal
|
||||||
* @param string|null $sRequestedPortalId
|
* @param bool $bIsAllowedToPortalUsers Whether or not the current page is considered as part of the portal
|
||||||
* @param int $iOnExit How to complete the call: redirect or return a code
|
* @param int $iOnExit How to complete the call: redirect or return a code
|
||||||
*/
|
*/
|
||||||
protected static function ChangeLocation($sRequestedPortalId = null, $iOnExit = self::EXIT_PROMPT)
|
protected static function ChangeLocation($bIsAllowedToPortalUsers, $iOnExit = self::EXIT_PROMPT)
|
||||||
{
|
{
|
||||||
$fStart = microtime(true);
|
if ( (!$bIsAllowedToPortalUsers) && (UserRights::IsPortalUser()))
|
||||||
$ret = call_user_func(array(self::$sHandlerClass, 'Dispatch'), $sRequestedPortalId);
|
|
||||||
if ($ret === true)
|
|
||||||
{
|
|
||||||
return self::EXIT_CODE_OK;
|
|
||||||
}
|
|
||||||
else if($ret === false)
|
|
||||||
{
|
|
||||||
throw new Exception('Nowhere to go??');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if ($iOnExit == self::EXIT_RETURN)
|
if ($iOnExit == self::EXIT_RETURN)
|
||||||
{
|
{
|
||||||
@@ -696,11 +668,16 @@ EOF
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// No rights to be here, redirect to the portal
|
// No rights to be here, redirect to the portal
|
||||||
header('Location: '.$ret);
|
header('Location: '.utils::GetAbsoluteUrlAppRoot().'portal/index.php');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return self::EXIT_CODE_OK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the user is already authentified, if yes, then performs some additional validations:
|
* Check if the user is already authentified, if yes, then performs some additional validations:
|
||||||
* - if $bMustBeAdmin is true, then the user must be an administrator, otherwise an error is displayed
|
* - if $bMustBeAdmin is true, then the user must be an administrator, otherwise an error is displayed
|
||||||
@@ -711,56 +688,9 @@ EOF
|
|||||||
*/
|
*/
|
||||||
static function DoLogin($bMustBeAdmin = false, $bIsAllowedToPortalUsers = false, $iOnExit = self::EXIT_PROMPT)
|
static function DoLogin($bMustBeAdmin = false, $bIsAllowedToPortalUsers = false, $iOnExit = self::EXIT_PROMPT)
|
||||||
{
|
{
|
||||||
$sRequestedPortalId = $bIsAllowedToPortalUsers ? 'legacy_portal' : 'backoffice';
|
$sMessage = ''; // In case we need to return a message to the calling web page
|
||||||
return self::DoLoginEx($sRequestedPortalId, $bMustBeAdmin, $iOnExit);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the user is already authentified, if yes, then performs some additional validations to redirect towards the desired "portal"
|
|
||||||
* @param string|null $sRequestedPortalId The requested "portal" interface, null for any
|
|
||||||
* @param bool $bMustBeAdmin Whether or not the user must be an admin to access the current page
|
|
||||||
* @param int iOnExit What action to take if the user is not logged on (one of the class constants EXIT_...)
|
|
||||||
*/
|
|
||||||
static function DoLoginEx($sRequestedPortalId = null, $bMustBeAdmin = false, $iOnExit = self::EXIT_PROMPT)
|
|
||||||
{
|
|
||||||
$operation = utils::ReadParam('loginop', '');
|
$operation = utils::ReadParam('loginop', '');
|
||||||
|
|
||||||
$sMessage = self::HandleOperations($operation); // May exit directly
|
|
||||||
|
|
||||||
$iRet = self::Login($iOnExit);
|
|
||||||
|
|
||||||
if ($iRet == self::EXIT_CODE_OK)
|
|
||||||
{
|
|
||||||
if ($bMustBeAdmin && !UserRights::IsAdministrator())
|
|
||||||
{
|
|
||||||
if ($iOnExit == self::EXIT_RETURN)
|
|
||||||
{
|
|
||||||
return self::EXIT_CODE_MUSTBEADMIN;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
require_once(APPROOT.'/setup/setuppage.class.inc.php');
|
|
||||||
$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
|
|
||||||
$oP->add("<h1>".Dict::S('UI:Login:Error:AccessAdmin')."</h1>\n");
|
|
||||||
$oP->p("<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a>");
|
|
||||||
$oP->output();
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$iRet = call_user_func(array(self::$sHandlerClass, 'ChangeLocation'), $sRequestedPortalId, $iOnExit);
|
|
||||||
}
|
|
||||||
if ($iOnExit == self::EXIT_RETURN)
|
|
||||||
{
|
|
||||||
return $iRet;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return $sMessage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected static function HandleOperations($operation)
|
|
||||||
{
|
|
||||||
$sMessage = ''; // most of the operations never return, but some can return a message to be displayed
|
|
||||||
if ($operation == 'logoff')
|
if ($operation == 'logoff')
|
||||||
{
|
{
|
||||||
if (isset($_SESSION['login_mode']))
|
if (isset($_SESSION['login_mode']))
|
||||||
@@ -784,7 +714,7 @@ EOF
|
|||||||
$oPage->DisplayLoginForm( $sLoginMode, false /* not a failed attempt */);
|
$oPage->DisplayLoginForm( $sLoginMode, false /* not a failed attempt */);
|
||||||
$oPage->output();
|
$oPage->output();
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
else if ($operation == 'forgot_pwd')
|
else if ($operation == 'forgot_pwd')
|
||||||
{
|
{
|
||||||
$oPage = self::NewLoginWebPage();
|
$oPage = self::NewLoginWebPage();
|
||||||
@@ -837,54 +767,36 @@ EOF
|
|||||||
}
|
}
|
||||||
$sMessage = Dict::S('UI:Login:PasswordChanged');
|
$sMessage = Dict::S('UI:Login:PasswordChanged');
|
||||||
}
|
}
|
||||||
return $sMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function Dispatch($sRequestedPortalId)
|
|
||||||
{
|
|
||||||
if ($sRequestedPortalId === null) return true; // allowed to any portal => return true
|
|
||||||
|
|
||||||
$aPortalsConf = PortalDispatcherData::GetData();
|
$iRet = self::Login($iOnExit);
|
||||||
$aDispatchers = array();
|
|
||||||
foreach($aPortalsConf as $sPortalId => $aConf)
|
if ($iRet == self::EXIT_CODE_OK)
|
||||||
{
|
{
|
||||||
$sHandlerClass = $aConf['handler'];
|
if ($bMustBeAdmin && !UserRights::IsAdministrator())
|
||||||
$aDispatchers[$sPortalId] = new $sHandlerClass($sPortalId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists($sRequestedPortalId, $aDispatchers) && $aDispatchers[$sRequestedPortalId]->IsUserAllowed())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
foreach($aDispatchers as $sPortalId => $oDispatcher)
|
|
||||||
{
|
|
||||||
if ($oDispatcher->IsUserAllowed()) return $oDispatcher->GetUrl();
|
|
||||||
}
|
|
||||||
return false; // nothing matched !!
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function GetAllowedPortals()
|
|
||||||
{
|
|
||||||
$aAllowedPortals = array();
|
|
||||||
$aPortalsConf = PortalDispatcherData::GetData();
|
|
||||||
$aDispatchers = array();
|
|
||||||
foreach($aPortalsConf as $sPortalId => $aConf)
|
|
||||||
{
|
|
||||||
$sHandlerClass = $aConf['handler'];
|
|
||||||
$aDispatchers[$sPortalId] = new $sHandlerClass($sPortalId);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($aDispatchers as $sPortalId => $oDispatcher)
|
|
||||||
{
|
|
||||||
if ($oDispatcher->IsUserAllowed())
|
|
||||||
{
|
{
|
||||||
$aAllowedPortals[] = array(
|
if ($iOnExit == self::EXIT_RETURN)
|
||||||
'id' => $sPortalId,
|
{
|
||||||
'label' => $oDispatcher->GetLabel(),
|
return self::EXIT_CODE_MUSTBEADMIN;
|
||||||
'url' => $oDispatcher->GetUrl(),
|
}
|
||||||
);
|
else
|
||||||
|
{
|
||||||
|
require_once(APPROOT.'/setup/setuppage.class.inc.php');
|
||||||
|
$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
|
||||||
|
$oP->add("<h1>".Dict::S('UI:Login:Error:AccessAdmin')."</h1>\n");
|
||||||
|
$oP->p("<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a>");
|
||||||
|
$oP->output();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
$iRet = call_user_func(array(self::$sHandlerClass, 'ChangeLocation'), $bIsAllowedToPortalUsers, $iOnExit);
|
||||||
}
|
}
|
||||||
return $aAllowedPortals;
|
if ($iOnExit == self::EXIT_RETURN)
|
||||||
}
|
{
|
||||||
|
return $iRet;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return $sMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
} // End of class
|
} // End of class
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2012 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
/**
|
/**
|
||||||
* Class NiceWebPage
|
* Class NiceWebPage
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -33,9 +33,9 @@ class NiceWebPage extends WebPage
|
|||||||
var $m_aReadyScripts;
|
var $m_aReadyScripts;
|
||||||
var $m_sRootUrl;
|
var $m_sRootUrl;
|
||||||
|
|
||||||
public function __construct($s_title, $bPrintable = false)
|
public function __construct($s_title)
|
||||||
{
|
{
|
||||||
parent::__construct($s_title, $bPrintable);
|
parent::__construct($s_title);
|
||||||
$this->m_aReadyScripts = array();
|
$this->m_aReadyScripts = array();
|
||||||
$this->add_linked_script("../js/jquery-1.10.0.min.js");
|
$this->add_linked_script("../js/jquery-1.10.0.min.js");
|
||||||
$this->add_linked_script("../js/jquery-migrate-1.2.1.min.js"); // Needed since many other plugins still rely on oldies like $.browser
|
$this->add_linked_script("../js/jquery-migrate-1.2.1.min.js"); // Needed since many other plugins still rely on oldies like $.browser
|
||||||
@@ -94,7 +94,7 @@ class NiceWebPage extends WebPage
|
|||||||
$("table.listResults").tableHover(); // hover tables
|
$("table.listResults").tableHover(); // hover tables
|
||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
$this->add_saas("css/light-grey.scss");
|
$this->add_linked_stylesheet("../css/light-grey.css");
|
||||||
|
|
||||||
$this->m_sRootUrl = $this->GetAbsoluteUrlAppRoot();
|
$this->m_sRootUrl = $this->GetAbsoluteUrlAppRoot();
|
||||||
$sAbsURLAppRoot = addslashes($this->m_sRootUrl);
|
$sAbsURLAppRoot = addslashes($this->m_sRootUrl);
|
||||||
|
|||||||
@@ -1,194 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once(APPROOT.'lib/tcpdf/tcpdf.php');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom class derived from TCPDF for providing custom headers and footers
|
|
||||||
* @author denis
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class iTopPDF extends TCPDF
|
|
||||||
{
|
|
||||||
protected $sDocumentTitle;
|
|
||||||
|
|
||||||
public function SetDocumentTitle($sDocumentTitle)
|
|
||||||
{
|
|
||||||
$this->sDocumentTitle = $sDocumentTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds the custom header. Called for each new page.
|
|
||||||
* @see TCPDF::Header()
|
|
||||||
*/
|
|
||||||
public function Header()
|
|
||||||
{
|
|
||||||
// Title
|
|
||||||
// Set font
|
|
||||||
$this->SetFont('dejavusans', 'B', 10);
|
|
||||||
|
|
||||||
$iPageNumberWidth = 25;
|
|
||||||
$aMargins = $this->getMargins();
|
|
||||||
|
|
||||||
// Display the title (centered)
|
|
||||||
$this->SetXY($aMargins['left'] + $iPageNumberWidth, 0);
|
|
||||||
$this->MultiCell($this->getPageWidth() - $aMargins['left'] - $aMargins['right'] - 2*$iPageNumberWidth, 15, $this->sDocumentTitle, 0, 'C', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
|
|
||||||
$this->SetFont('dejavusans', '', 10);
|
|
||||||
|
|
||||||
// Display the page number (right aligned)
|
|
||||||
// Warning: the 'R'ight alignment does not work when using placeholders like $this->getAliasNumPage() or $this->getAliasNbPages()
|
|
||||||
$this->MultiCell($iPageNumberWidth, 15, 'Page '.$this->page, 0, 'R', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
|
|
||||||
|
|
||||||
// Branding logo
|
|
||||||
$sBrandingIcon = APPROOT.'images/itop-logo.png';
|
|
||||||
if (file_exists(MODULESROOT.'branding/main-logo.png'))
|
|
||||||
{
|
|
||||||
$sBrandingIcon = MODULESROOT.'branding/main-logo.png';
|
|
||||||
}
|
|
||||||
$this->Image($sBrandingIcon, $aMargins['left'], 5, 0, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Page footer
|
|
||||||
public function Footer()
|
|
||||||
{
|
|
||||||
// No footer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Special class of WebPage for printing into a PDF document
|
|
||||||
*/
|
|
||||||
class PDFPage extends WebPage
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Instance of the TCPDF object for creating the PDF
|
|
||||||
* @var TCPDF
|
|
||||||
*/
|
|
||||||
protected $oPdf;
|
|
||||||
|
|
||||||
public function __construct($s_title, $sPageFormat = 'A4', $sPageOrientation = 'L')
|
|
||||||
{
|
|
||||||
parent::__construct($s_title);
|
|
||||||
define(K_PATH_FONTS, APPROOT.'lib/tcpdf/fonts');
|
|
||||||
$this->oPdf = new iTopPDF($sPageOrientation, 'mm', $sPageFormat, true, 'UTF-8', false);
|
|
||||||
|
|
||||||
// set document information
|
|
||||||
$this->oPdf->SetCreator(PDF_CREATOR);
|
|
||||||
$this->oPdf->SetAuthor('iTop');
|
|
||||||
$this->oPdf->SetTitle($s_title);
|
|
||||||
$this->oPdf->SetDocumentTitle($s_title);
|
|
||||||
|
|
||||||
$this->oPdf->setFontSubsetting(true);
|
|
||||||
|
|
||||||
// Set font
|
|
||||||
// dejavusans is a UTF-8 Unicode font. Standard PDF fonts like helvetica or times new roman are NOT UTF-8
|
|
||||||
$this->oPdf->SetFont('dejavusans', '', 10, '', true);
|
|
||||||
|
|
||||||
// set auto page breaks
|
|
||||||
$this->oPdf->SetAutoPageBreak(true, 15); // 15 mm break margin at the bottom
|
|
||||||
$this->oPdf->SetTopMargin(15);
|
|
||||||
|
|
||||||
// Add a page, we're ready to start
|
|
||||||
$this->oPdf->AddPage();
|
|
||||||
|
|
||||||
$this->SetContentDisposition('inline', $s_title.'.pdf');
|
|
||||||
$this->SetDefaultStyle();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a default style (suitable for printing) to be included each time $this->oPdf->writeHTML() is called
|
|
||||||
*/
|
|
||||||
protected function SetDefaultStyle()
|
|
||||||
{
|
|
||||||
$this->add_style(
|
|
||||||
<<<EOF
|
|
||||||
table {
|
|
||||||
padding: 2pt;
|
|
||||||
}
|
|
||||||
table.listResults td {
|
|
||||||
border: 0.5pt solid #000 ;
|
|
||||||
}
|
|
||||||
table.listResults th {
|
|
||||||
background-color: #eee;
|
|
||||||
border: 0.5pt solid #000 ;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
table.section td {
|
|
||||||
vertical-align: middle;
|
|
||||||
font-size: 10pt;
|
|
||||||
background-color:#eee;
|
|
||||||
}
|
|
||||||
td.icon {
|
|
||||||
width: 30px;
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get access to the underlying TCPDF object
|
|
||||||
* @return TCPDF
|
|
||||||
*/
|
|
||||||
public function get_tcpdf()
|
|
||||||
{
|
|
||||||
$this->flush();
|
|
||||||
return $this->oPdf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the currently buffered HTML content into the PDF. This can be useful:
|
|
||||||
* - to sync the flow in case you want to access the underlying TCPDF object for some specific/graphic output
|
|
||||||
* - to process the HTML by smaller chunks instead of processing the whole page at once for performance reasons
|
|
||||||
*/
|
|
||||||
public function flush()
|
|
||||||
{
|
|
||||||
if (strlen($this->s_content) > 0)
|
|
||||||
{
|
|
||||||
$sHtml = '';
|
|
||||||
if (count($this->a_styles) > 0)
|
|
||||||
{
|
|
||||||
$sHtml .= "<style>\n".implode("\n", $this->a_styles)."\n</style>\n";
|
|
||||||
}
|
|
||||||
$sHtml .= $this->s_content;
|
|
||||||
$this->oPdf->writeHTML($sHtml); // The style(s) must be supplied each time we call writeHtml
|
|
||||||
$this->s_content = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not the page is a PDF page
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function is_pdf()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the PDF document and returns the PDF content as a string
|
|
||||||
* @return string
|
|
||||||
* @see WebPage::output()
|
|
||||||
*/
|
|
||||||
public function output()
|
|
||||||
{
|
|
||||||
$this->add_header('Content-type: application/x-pdf');
|
|
||||||
if (!empty($this->sContentDisposition))
|
|
||||||
{
|
|
||||||
$this->add_header('Content-Disposition: '.$this->sContentDisposition.'; filename="'.$this->sContentFileName.'"');
|
|
||||||
}
|
|
||||||
foreach($this->a_headers as $s_header)
|
|
||||||
{
|
|
||||||
header($s_header);
|
|
||||||
}
|
|
||||||
$this->flush();
|
|
||||||
echo $this->oPdf->Output($this->s_title.'.pdf', 'S');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_pdf()
|
|
||||||
{
|
|
||||||
$this->flush();
|
|
||||||
return $this->oPdf->Output($this->s_title.'.pdf', 'S');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
<?php
|
|
||||||
class PortalDispatcher
|
|
||||||
{
|
|
||||||
protected $sPortalid;
|
|
||||||
protected $aData;
|
|
||||||
|
|
||||||
public function __construct($sPortalId)
|
|
||||||
{
|
|
||||||
$this->sPortalid = $sPortalId;
|
|
||||||
$this->aData = PortalDispatcherData::GetData($sPortalId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function IsUserAllowed()
|
|
||||||
{
|
|
||||||
$bRet = true;
|
|
||||||
if (array_key_exists('profile_list', $_SESSION))
|
|
||||||
{
|
|
||||||
$aProfiles = $_SESSION['profile_list'];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$oUser = UserRights::GetUserObject();
|
|
||||||
$oSet = $oUser->Get('profile_list');
|
|
||||||
while(($oLnkUserProfile = $oSet->Fetch()) !== null)
|
|
||||||
{
|
|
||||||
$aProfiles[] = $oLnkUserProfile->Get('profileid_friendlyname');
|
|
||||||
}
|
|
||||||
$_SESSION['profile_list'] = $aProfiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($this->aData['deny'] as $sDeniedProfile)
|
|
||||||
{
|
|
||||||
// If one denied profile is present, it's enough => return false
|
|
||||||
if (in_array($sDeniedProfile, $aProfiles))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If there are some "allow" profiles, then by default the result is false
|
|
||||||
// since the user must have at least one of the profiles to be allowed
|
|
||||||
if (count($this->aData['allow']) > 0)
|
|
||||||
{
|
|
||||||
$bRet = false;
|
|
||||||
}
|
|
||||||
foreach($this->aData['allow'] as $sAllowProfile)
|
|
||||||
{
|
|
||||||
// If one "allow" profile is present, it's enough => return true
|
|
||||||
if (in_array($sAllowProfile, $aProfiles))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $bRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetURL()
|
|
||||||
{
|
|
||||||
return utils::GetAbsoluteUrlAppRoot().$this->aData['url'];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetLabel()
|
|
||||||
{
|
|
||||||
return Dict::S('portal:'.$this->sPortalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetRank()
|
|
||||||
{
|
|
||||||
return $this->aData['rank'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -88,21 +88,8 @@ class PortalWebPage extends NiceWebPage
|
|||||||
$this->add_linked_script("../js/forms-json-utils.js");
|
$this->add_linked_script("../js/forms-json-utils.js");
|
||||||
$this->add_linked_script("../js/swfobject.js");
|
$this->add_linked_script("../js/swfobject.js");
|
||||||
$this->add_linked_script("../js/jquery.qtip-1.0.min.js");
|
$this->add_linked_script("../js/jquery.qtip-1.0.min.js");
|
||||||
$this->add_linked_script('../js/jquery.multiselect.js');
|
$this->add_linked_script('../js/jquery.multiselect.min.js');
|
||||||
$this->add_linked_script("../js/ajaxfileupload.js");
|
$this->add_linked_script("../js/ajaxfileupload.js");
|
||||||
$this->add_linked_script("../js/ckeditor/ckeditor.js");
|
|
||||||
$this->add_linked_script("../js/ckeditor/adapters/jquery.js");
|
|
||||||
|
|
||||||
$sJSDisconnectedMessage = json_encode(Dict::S('UI:DisconnectedDlgMessage'));
|
|
||||||
$sJSTitle = json_encode(Dict::S('UI:DisconnectedDlgTitle'));
|
|
||||||
$sJSLoginAgain = json_encode(Dict::S('UI:LoginAgain'));
|
|
||||||
$sJSStayOnThePage = json_encode(Dict::S('UI:StayOnThePage'));
|
|
||||||
$sJSDaysMin = json_encode(array(Dict::S('DayOfWeek-Sunday-Min'), Dict::S('DayOfWeek-Monday-Min'), Dict::S('DayOfWeek-Tuesday-Min'), Dict::S('DayOfWeek-Wednesday-Min'),
|
|
||||||
Dict::S('DayOfWeek-Thursday-Min'), Dict::S('DayOfWeek-Friday-Min'), Dict::S('DayOfWeek-Saturday-Min')));
|
|
||||||
$sJSMonthsShort = json_encode(array(Dict::S('Month-01-Short'), Dict::S('Month-02-Short'), Dict::S('Month-03-Short'), Dict::S('Month-04-Short'), Dict::S('Month-05-Short'), Dict::S('Month-06-Short'),
|
|
||||||
Dict::S('Month-07-Short'), Dict::S('Month-08-Short'), Dict::S('Month-09-Short'), Dict::S('Month-10-Short'), Dict::S('Month-11-Short'), Dict::S('Month-12-Short')));
|
|
||||||
$iFirstDayOfWeek = (int) Dict::S('Calendar-FirstDayOfWeek');
|
|
||||||
|
|
||||||
$this->add_ready_script(
|
$this->add_ready_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
try
|
try
|
||||||
@@ -144,17 +131,14 @@ try
|
|||||||
});
|
});
|
||||||
|
|
||||||
$(".date-pick").datepicker({
|
$(".date-pick").datepicker({
|
||||||
showOn: 'button',
|
showOn: 'button',
|
||||||
buttonImage: '../images/calendar.png',
|
buttonImage: '../images/calendar.png',
|
||||||
buttonImageOnly: true,
|
buttonImageOnly: true,
|
||||||
dateFormat: 'yy-mm-dd',
|
dateFormat: 'yy-mm-dd',
|
||||||
constrainInput: false,
|
constrainInput: false,
|
||||||
changeMonth: true,
|
changeMonth: true,
|
||||||
changeYear: true,
|
changeYear: true
|
||||||
dayNamesMin: $sJSDaysMin,
|
});
|
||||||
monthNamesShort: $sJSMonthsShort,
|
|
||||||
firstDay: $iFirstDayOfWeek
|
|
||||||
});
|
|
||||||
|
|
||||||
$(".datetime-pick").datepicker({
|
$(".datetime-pick").datepicker({
|
||||||
showOn: 'button',
|
showOn: 'button',
|
||||||
@@ -163,33 +147,11 @@ try
|
|||||||
dateFormat: 'yy-mm-dd 00:00:00',
|
dateFormat: 'yy-mm-dd 00:00:00',
|
||||||
constrainInput: false,
|
constrainInput: false,
|
||||||
changeMonth: true,
|
changeMonth: true,
|
||||||
changeYear: true,
|
changeYear: true
|
||||||
dayNamesMin: $sJSDaysMin,
|
});
|
||||||
monthNamesShort: $sJSMonthsShort,
|
|
||||||
firstDay: $iFirstDayOfWeek
|
|
||||||
});
|
|
||||||
|
|
||||||
//$('.resizable').resizable(); // Make resizable everything that claims to be resizable !
|
//$('.resizable').resizable(); // Make resizable everything that claims to be resizable !
|
||||||
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry').toggle(); });
|
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry').toggle(); });
|
||||||
|
|
||||||
$(document).ajaxSend(function(event, jqxhr, options) {
|
|
||||||
jqxhr.setRequestHeader('X-Combodo-Ajax', 'true');
|
|
||||||
});
|
|
||||||
$(document).ajaxError(function(event, jqxhr, options) {
|
|
||||||
if (jqxhr.status == 401)
|
|
||||||
{
|
|
||||||
$('<div>'+$sJSDisconnectedMessage+'</div>').dialog({
|
|
||||||
modal:true,
|
|
||||||
title: $sJSTitle,
|
|
||||||
close: function() { $(this).remove(); },
|
|
||||||
minWidth: 400,
|
|
||||||
buttons: [
|
|
||||||
{ text: $sJSLoginAgain, click: function() { window.location.href= GetAbsoluteUrlAppRoot()+'pages/UI.php' } },
|
|
||||||
{ text: $sJSStayOnThePage, click: function() { $(this).dialog('close'); } }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
catch(err)
|
catch(err)
|
||||||
{
|
{
|
||||||
@@ -260,7 +222,7 @@ EOF
|
|||||||
{
|
{
|
||||||
var form = $('FORM');
|
var form = $('FORM');
|
||||||
form.unbind('submit'); // De-activate validation
|
form.unbind('submit'); // De-activate validation
|
||||||
window.location.href = window.location.href.replace(/[&?]operation=[^&]*/, '');
|
window.location.href = '?operation=';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,20 +231,6 @@ EOF
|
|||||||
var next_step = $('input[id=next_step]');
|
var next_step = $('input[id=next_step]');
|
||||||
next_step.val(sStep);
|
next_step.val(sStep);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For disabling the CKEditor at init time when the corresponding textarea is disabled !
|
|
||||||
CKEDITOR.plugins.add( 'disabler',
|
|
||||||
{
|
|
||||||
init : function( editor )
|
|
||||||
{
|
|
||||||
editor.on( 'instanceReady', function(e)
|
|
||||||
{
|
|
||||||
e.removeListener();
|
|
||||||
$('#'+ editor.name).trigger('update');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -348,7 +296,7 @@ EOF
|
|||||||
$sMenu = '';
|
$sMenu = '';
|
||||||
if ($this->m_bEnableDisconnectButton)
|
if ($this->m_bEnableDisconnectButton)
|
||||||
{
|
{
|
||||||
$this->AddMenuButton('logoff', 'Portal:Disconnect', utils::GetAbsoluteUrlAppRoot().'pages/logoff.php?operation=do_logoff'); // This menu is always present and is the last one
|
$this->AddMenuButton('logoff', 'Portal:Disconnect', utils::GetAbsoluteUrlAppRoot().'pages/logoff.php'); // This menu is always present and is the last one
|
||||||
}
|
}
|
||||||
foreach($this->m_aMenuButtons as $aMenuItem)
|
foreach($this->m_aMenuButtons as $aMenuItem)
|
||||||
{
|
{
|
||||||
@@ -807,17 +755,6 @@ EOF
|
|||||||
|
|
||||||
$this->p("<h1>".Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oObj)), $oObj->GetName())."</h1>\n");
|
$this->p("<h1>".Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oObj)), $oObj->GetName())."</h1>\n");
|
||||||
}
|
}
|
||||||
$bLockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled');
|
|
||||||
if ($bLockEnabled)
|
|
||||||
{
|
|
||||||
// Release the concurrent lock, if any
|
|
||||||
$sOwnershipToken = utils::ReadPostedParam('ownership_token', null, false, 'raw_data');
|
|
||||||
if ($sOwnershipToken !== null)
|
|
||||||
{
|
|
||||||
// We're done, let's release the lock
|
|
||||||
iTopOwnershipLock::ReleaseLock(get_class($oObj), $oObj->GetKey(), $sOwnershipToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -889,7 +826,7 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
$sStepHistory = implode(',', $aPreviousSteps);
|
$sStepHistory = implode(',', $aPreviousSteps);
|
||||||
$this->add("<input type=\"hidden\" id=\"step_history\" name=\"step_history\" value=\"".htmlentities($sStepHistory, ENT_QUOTES, 'UTF-8')."\">");
|
$this->add("<input type=\"hidden\" id=\"step_history\" name=\"step_history\" value=\"$sStepHistory\">");
|
||||||
|
|
||||||
if (!is_null($sNextStep))
|
if (!is_null($sNextStep))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2012 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
* Application internal events
|
* Application internal events
|
||||||
* There is also a file log
|
* There is also a file log
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -92,18 +92,7 @@ class QueryOQL extends Query
|
|||||||
|
|
||||||
if (!$bEditMode)
|
if (!$bEditMode)
|
||||||
{
|
{
|
||||||
$sFields = trim($this->Get('fields'));
|
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?format=spreadsheet&login_mode=basic&query='.$this->GetKey();
|
||||||
$bExportV1Recommended = ($sFields == '');
|
|
||||||
if ($bExportV1Recommended)
|
|
||||||
{
|
|
||||||
$oFieldAttDef = MetaModel::GetAttributeDef('QueryOQL', 'fields');
|
|
||||||
$oPage->add('<div class="message message_error" style="padding-left: 30px;"><div style="padding: 10px;">'.Dict::Format('UI:Query:UrlV1', $oFieldAttDef->GetLabel()).'</div></div>');
|
|
||||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?format=spreadsheet&login_mode=basic&query='.$this->GetKey();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php?format=spreadsheet&login_mode=basic&query='.$this->GetKey();
|
|
||||||
}
|
|
||||||
$sOql = $this->Get('oql');
|
$sOql = $this->Get('oql');
|
||||||
$sMessage = null;
|
$sMessage = null;
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -28,11 +28,10 @@ require_once(APPROOT.'/core/cmdbobject.class.inc.php');
|
|||||||
require_once(APPROOT.'/application/utils.inc.php');
|
require_once(APPROOT.'/application/utils.inc.php');
|
||||||
session_name('itop-'.md5(APPROOT));
|
session_name('itop-'.md5(APPROOT));
|
||||||
session_start();
|
session_start();
|
||||||
$sSwitchEnv = utils::ReadParam('switch_env', null);
|
if (isset($_REQUEST['switch_env']))
|
||||||
if (($sSwitchEnv != null) && (file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE)))
|
|
||||||
{
|
{
|
||||||
$_SESSION['itop_env'] = $sSwitchEnv;
|
$sEnv = $_REQUEST['switch_env'];
|
||||||
$sEnv = $sSwitchEnv;
|
$_SESSION['itop_env'] = $sEnv;
|
||||||
// TODO: reset the credentials as well ??
|
// TODO: reset the credentials as well ??
|
||||||
}
|
}
|
||||||
else if (isset($_SESSION['itop_env']))
|
else if (isset($_SESSION['itop_env']))
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2012 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -19,8 +19,7 @@
|
|||||||
/**
|
/**
|
||||||
* This class records the pending "transactions" corresponding to forms that have not been
|
* This class records the pending "transactions" corresponding to forms that have not been
|
||||||
* submitted yet, in order to prevent double submissions. When created a transaction remains valid
|
* submitted yet, in order to prevent double submissions. When created a transaction remains valid
|
||||||
* until the user's session expires. This class is actually a wrapper to the underlying implementation
|
* until the user's session expires
|
||||||
* which choice is configured via the parameter 'transaction_storage'
|
|
||||||
*
|
*
|
||||||
* @package iTop
|
* @package iTop
|
||||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
@@ -29,81 +28,6 @@
|
|||||||
|
|
||||||
|
|
||||||
class privUITransaction
|
class privUITransaction
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Create a new transaction id, store it in the session and return its id
|
|
||||||
* @param void
|
|
||||||
* @return int The identifier of the new transaction
|
|
||||||
*/
|
|
||||||
public static function GetNewTransactionId()
|
|
||||||
{
|
|
||||||
$bTransactionsEnabled = MetaModel::GetConfig()->Get('transactions_enabled');
|
|
||||||
if (!$bTransactionsEnabled)
|
|
||||||
{
|
|
||||||
return 'notransactions'; // Any value will do
|
|
||||||
}
|
|
||||||
$sClass = 'privUITransaction'.MetaModel::GetConfig()->Get('transaction_storage');
|
|
||||||
if (!class_exists($sClass, false))
|
|
||||||
{
|
|
||||||
IssueLog::Error("Incorrect value '".MetaModel::GetConfig()->Get('transaction_storage')."' for 'transaction_storage', the class '$sClass' does not exists. Using privUITransactionSession instead for storing sessions.");
|
|
||||||
$sClass = 'privUITransactionSession';
|
|
||||||
}
|
|
||||||
|
|
||||||
return (string)$sClass::GetNewTransactionId();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether a transaction is valid or not and (optionally) remove the valid transaction from
|
|
||||||
* the session so that another call to IsTransactionValid for the same transaction id
|
|
||||||
* will return false
|
|
||||||
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
|
|
||||||
* @param bool $bRemoveTransaction True if the transaction must be removed
|
|
||||||
* @return bool True if the transaction is valid, false otherwise
|
|
||||||
*/
|
|
||||||
public static function IsTransactionValid($id, $bRemoveTransaction = true)
|
|
||||||
{
|
|
||||||
$bTransactionsEnabled = MetaModel::GetConfig()->Get('transactions_enabled');
|
|
||||||
if (!$bTransactionsEnabled)
|
|
||||||
{
|
|
||||||
return true; // All values are valid
|
|
||||||
}
|
|
||||||
$sClass = 'privUITransaction'.MetaModel::GetConfig()->Get('transaction_storage');
|
|
||||||
if (!class_exists($sClass, false))
|
|
||||||
{
|
|
||||||
$sClass = 'privUITransactionSession';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sClass::IsTransactionValid($id, $bRemoveTransaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the transaction specified by its id
|
|
||||||
* @param int $id The Identifier (as returned by GetNewTranscationId) of the transaction to be removed.
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function RemoveTransaction($id)
|
|
||||||
{
|
|
||||||
$bTransactionsEnabled = MetaModel::GetConfig()->Get('transactions_enabled');
|
|
||||||
if (!$bTransactionsEnabled)
|
|
||||||
{
|
|
||||||
return; // Nothing to do
|
|
||||||
}
|
|
||||||
$sClass = 'privUITransaction'.MetaModel::GetConfig()->Get('transaction_storage');
|
|
||||||
if (!class_exists($sClass, false))
|
|
||||||
{
|
|
||||||
$sClass = 'privUITransactionSession';
|
|
||||||
}
|
|
||||||
|
|
||||||
$sClass::RemoveTransaction($id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The original (and by default) mechanism for storing transaction information
|
|
||||||
* as an array in the $_SESSION variable
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class privUITransactionSession
|
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Create a new transaction id, store it in the session and return its id
|
* Create a new transaction id, store it in the session and return its id
|
||||||
@@ -175,178 +99,4 @@ class privUITransactionSession
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
?>
|
||||||
/**
|
|
||||||
* An alternate implementation for storing the transactions as temporary files
|
|
||||||
* Useful when using an in-memory storage for the session which do not
|
|
||||||
* guarantee mutual exclusion for writing
|
|
||||||
*/
|
|
||||||
class privUITransactionFile
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Create a new transaction id, store it in the session and return its id
|
|
||||||
* @param void
|
|
||||||
* @return int The identifier of the new transaction
|
|
||||||
*/
|
|
||||||
public static function GetNewTransactionId()
|
|
||||||
{
|
|
||||||
if (!is_dir(APPROOT.'data/transactions'))
|
|
||||||
{
|
|
||||||
if (!is_writable(APPROOT.'data'))
|
|
||||||
{
|
|
||||||
throw new Exception('The directory "'.APPROOT.'data" must be writable to the application.');
|
|
||||||
}
|
|
||||||
if (!@mkdir(APPROOT.'data/transactions'))
|
|
||||||
{
|
|
||||||
throw new Exception('Failed to create the directory "'.APPROOT.'data/transactions". Ajust the rights on the parent directory or let an administrator create the transactions directory and give the web sever enough rights to write into it.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!is_writable(APPROOT.'data/transactions'))
|
|
||||||
{
|
|
||||||
throw new Exception('The directory "'.APPROOT.'data/transactions" must be writable to the application.');
|
|
||||||
}
|
|
||||||
self::CleanupOldTransactions();
|
|
||||||
$id = basename(tempnam(APPROOT.'data/transactions', self::GetUserPrefix()));
|
|
||||||
self::Info('GetNewTransactionId: Created transaction: '.$id);
|
|
||||||
|
|
||||||
return (string)$id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether a transaction is valid or not and (optionally) remove the valid transaction from
|
|
||||||
* the session so that another call to IsTransactionValid for the same transaction id
|
|
||||||
* will return false
|
|
||||||
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
|
|
||||||
* @param bool $bRemoveTransaction True if the transaction must be removed
|
|
||||||
* @return bool True if the transaction is valid, false otherwise
|
|
||||||
*/
|
|
||||||
public static function IsTransactionValid($id, $bRemoveTransaction = true)
|
|
||||||
{
|
|
||||||
$sFilepath = APPROOT.'data/transactions/'.$id;
|
|
||||||
clearstatcache(true, $sFilepath);
|
|
||||||
$bResult = file_exists($sFilepath);
|
|
||||||
if ($bResult)
|
|
||||||
{
|
|
||||||
if ($bRemoveTransaction)
|
|
||||||
{
|
|
||||||
$bResult = @unlink($sFilepath);
|
|
||||||
if (!$bResult)
|
|
||||||
{
|
|
||||||
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self::Info('IsTransactionValid: OK. Removed transaction: '.$id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self::Info("IsTransactionValid: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
|
|
||||||
}
|
|
||||||
return $bResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the transaction specified by its id
|
|
||||||
* @param int $id The Identifier (as returned by GetNewTransactionId) of the transaction to be removed.
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function RemoveTransaction($id)
|
|
||||||
{
|
|
||||||
$bSuccess = true;
|
|
||||||
$sFilepath = APPROOT.'data/transactions/'.$id;
|
|
||||||
clearstatcache(true, $sFilepath);
|
|
||||||
if(!file_exists($sFilepath))
|
|
||||||
{
|
|
||||||
$bSuccess = false;
|
|
||||||
self::Error("RemoveTransaction: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
|
|
||||||
}
|
|
||||||
$bSuccess = @unlink($sFilepath);
|
|
||||||
if (!$bSuccess)
|
|
||||||
{
|
|
||||||
self::Error('RemoveTransaction: FAILED to remove transaction '.$id);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self::Info('RemoveTransaction: OK '.$id);
|
|
||||||
}
|
|
||||||
return $bSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cleanup old transactions which have been pending since more than 24 hours
|
|
||||||
* Use filemtime instead of filectime since filectime may be affected by operations on the directory (like changing the access rights)
|
|
||||||
*/
|
|
||||||
protected static function CleanupOldTransactions()
|
|
||||||
{
|
|
||||||
$iLimit = time() - 24*3600;
|
|
||||||
clearstatcache();
|
|
||||||
$aTransactions = glob(APPROOT.'data/transactions/*-*');
|
|
||||||
foreach($aTransactions as $sFileName)
|
|
||||||
{
|
|
||||||
if (filemtime($sFileName) < $iLimit)
|
|
||||||
{
|
|
||||||
@unlink($sFileName);
|
|
||||||
self::Info('CleanupOldTransactions: Deleted transaction: '.$sFileName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For debugging purposes: gets the pending transactions of the current user
|
|
||||||
* as an array, with the date of the creation of the transaction file
|
|
||||||
*/
|
|
||||||
protected static function GetPendingTransactions()
|
|
||||||
{
|
|
||||||
clearstatcache();
|
|
||||||
$aResult = array();
|
|
||||||
$aTransactions = glob(APPROOT.'data/transactions/'.self::GetUserPrefix().'*');
|
|
||||||
foreach($aTransactions as $sFileName)
|
|
||||||
{
|
|
||||||
$aResult[] = date('Y-m-d H:i:s', filemtime($sFileName)).' - '.basename($sFileName);
|
|
||||||
}
|
|
||||||
sort($aResult);
|
|
||||||
return $aResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function GetUserPrefix()
|
|
||||||
{
|
|
||||||
$sPrefix = substr(UserRights::GetUser(), 0, 10);
|
|
||||||
$sPrefix = preg_replace('/[^a-zA-Z0-9-_]/', '_', $sPrefix);
|
|
||||||
return $sPrefix.'-';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function Info($sText)
|
|
||||||
{
|
|
||||||
self::Write('Info | '.$sText);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function Warning($sText)
|
|
||||||
{
|
|
||||||
self::Write('Warning | '.$sText);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function Error($sText)
|
|
||||||
{
|
|
||||||
self::Write('Error | '.$sText);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function Write($sText)
|
|
||||||
{
|
|
||||||
$bLogEnabled = MetaModel::GetConfig()->Get('log_transactions');
|
|
||||||
if ($bLogEnabled)
|
|
||||||
{
|
|
||||||
$hLogFile = @fopen(APPROOT.'log/transactions.log', 'a');
|
|
||||||
if ($hLogFile !== false)
|
|
||||||
{
|
|
||||||
flock($hLogFile, LOCK_EX);
|
|
||||||
$sDate = date('Y-m-d H:i:s');
|
|
||||||
fwrite($hLogFile, "$sDate | $sText\n");
|
|
||||||
fflush($hLogFile);
|
|
||||||
flock($hLogFile, LOCK_UN);
|
|
||||||
fclose($hLogFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -164,7 +164,6 @@ class UIExtKeyWidget
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'select':
|
case 'select':
|
||||||
case 'list':
|
|
||||||
default:
|
default:
|
||||||
$sSelectMode = 'true';
|
$sSelectMode = 'true';
|
||||||
|
|
||||||
@@ -253,14 +252,14 @@ EOF
|
|||||||
$sDisplayValue = $this->GetObjectName($value);
|
$sDisplayValue = $this->GetObjectName($value);
|
||||||
}
|
}
|
||||||
$iMinChars = isset($aArgs['iMinChars']) ? $aArgs['iMinChars'] : 3; //@@@ $this->oAttDef->GetMinAutoCompleteChars();
|
$iMinChars = isset($aArgs['iMinChars']) ? $aArgs['iMinChars'] : 3; //@@@ $this->oAttDef->GetMinAutoCompleteChars();
|
||||||
$iFieldSize = isset($aArgs['iFieldSize']) ? $aArgs['iFieldSize'] : 20; //@@@ $this->oAttDef->GetMaxSize();
|
$iFieldSize = isset($aArgs['iFieldSize']) ? $aArgs['iFieldSize'] : 30; //@@@ $this->oAttDef->GetMaxSize();
|
||||||
|
|
||||||
// the input for the auto-complete
|
// the input for the auto-complete
|
||||||
$sHTMLValue = "<input count=\"".$oAllowedValues->Count()."\" type=\"text\" id=\"label_$this->iId\" size=\"$iFieldSize\" value=\"$sDisplayValue\"/> ";
|
$sHTMLValue = "<input count=\"".$oAllowedValues->Count()."\" type=\"text\" id=\"label_$this->iId\" size=\"$iFieldSize\" value=\"$sDisplayValue\"/> ";
|
||||||
$sHTMLValue .= "<img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.Search();\"/>";
|
$sHTMLValue .= "<img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif\" onClick=\"oACWidget_{$this->iId}.Search();\"/> ";
|
||||||
|
|
||||||
// another hidden input to store & pass the object's Id
|
// another hidden input to store & pass the object's Id
|
||||||
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" />\n";
|
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"$value\" />\n";
|
||||||
|
|
||||||
$JSSearchMode = $this->bSearchMode ? 'true' : 'false';
|
$JSSearchMode = $this->bSearchMode ? 'true' : 'false';
|
||||||
// Scripts to start the autocomplete and bind some events to it
|
// Scripts to start the autocomplete and bind some events to it
|
||||||
@@ -281,7 +280,7 @@ EOF
|
|||||||
}
|
}
|
||||||
if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false)
|
if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false)
|
||||||
{
|
{
|
||||||
$sHTMLValue .= "<img id=\"mini_tree_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_tree.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"/> ";
|
$sHTMLValue .= "<img id=\"mini_tree_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_tree.gif\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"/> ";
|
||||||
$oPage->add_ready_script(
|
$oPage->add_ready_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
if ($('#ac_tree_{$this->iId}').length == 0)
|
if ($('#ac_tree_{$this->iId}').length == 0)
|
||||||
@@ -293,7 +292,7 @@ EOF
|
|||||||
}
|
}
|
||||||
if ($bCreate && $bExtensions)
|
if ($bCreate && $bExtensions)
|
||||||
{
|
{
|
||||||
$sHTMLValue .= "<img id=\"mini_add_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_add.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.CreateObject();\"/> ";
|
$sHTMLValue .= "<img id=\"mini_add_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_add.gif\" onClick=\"oACWidget_{$this->iId}.CreateObject();\"/> ";
|
||||||
$oPage->add_ready_script(
|
$oPage->add_ready_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
if ($('#ajax_{$this->iId}').length == 0)
|
if ($('#ajax_{$this->iId}').length == 0)
|
||||||
@@ -303,7 +302,7 @@ EOF
|
|||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (($sDisplayStyle == 'select') || ($sDisplayStyle == 'list'))
|
if ($sDisplayStyle == 'select')
|
||||||
{
|
{
|
||||||
$sHTMLValue .= "<span id=\"v_{$this->iId}\"></span>";
|
$sHTMLValue .= "<span id=\"v_{$this->iId}\"></span>";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2012 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -21,15 +21,13 @@
|
|||||||
* UI wdiget for displaying and editing one-way encrypted passwords
|
* UI wdiget for displaying and editing one-way encrypted passwords
|
||||||
*
|
*
|
||||||
* @author Phil Eddies
|
* @author Phil Eddies
|
||||||
* @author Romain Quetiez
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class UIHTMLEditorWidget
|
class UIHTMLEditorWidget
|
||||||
{
|
{
|
||||||
protected $m_iId;
|
protected $m_iId;
|
||||||
protected $m_oAttDef;
|
|
||||||
protected $m_sAttCode;
|
protected $m_sAttCode;
|
||||||
protected $m_sNameSuffix;
|
protected $m_sNameSuffix;
|
||||||
protected $m_sFieldPrefix;
|
protected $m_sFieldPrefix;
|
||||||
@@ -38,11 +36,10 @@ class UIHTMLEditorWidget
|
|||||||
protected $m_sValue;
|
protected $m_sValue;
|
||||||
protected $m_sMandatory;
|
protected $m_sMandatory;
|
||||||
|
|
||||||
public function __construct($iInputId, $oAttDef, $sNameSuffix, $sFieldPrefix, $sHelpText, $sValidationField, $sValue, $sMandatory)
|
public function __construct($iInputId, $sAttCode, $sNameSuffix, $sFieldPrefix, $sHelpText, $sValidationField, $sValue, $sMandatory)
|
||||||
{
|
{
|
||||||
$this->m_iId = $iInputId;
|
$this->m_iId = $iInputId;
|
||||||
$this->m_oAttDef = $oAttDef;
|
$this->m_sAttCode = $sAttCode;
|
||||||
$this->m_sAttCode = $oAttDef->GetCode();
|
|
||||||
$this->m_sNameSuffix = $sNameSuffix;
|
$this->m_sNameSuffix = $sNameSuffix;
|
||||||
$this->m_sHelpText = $sHelpText;
|
$this->m_sHelpText = $sHelpText;
|
||||||
$this->m_sValidationField = $sValidationField;
|
$this->m_sValidationField = $sValidationField;
|
||||||
@@ -71,24 +68,8 @@ class UIHTMLEditorWidget
|
|||||||
// To change the default settings of the editor,
|
// To change the default settings of the editor,
|
||||||
// a) edit the file /js/ckeditor/config.js
|
// a) edit the file /js/ckeditor/config.js
|
||||||
// b) or override some of the configuration settings, using the second parameter of ckeditor()
|
// b) or override some of the configuration settings, using the second parameter of ckeditor()
|
||||||
$aConfig = array();
|
|
||||||
$sLanguage = strtolower(trim(UserRights::GetUserLanguage()));
|
$sLanguage = strtolower(trim(UserRights::GetUserLanguage()));
|
||||||
$aConfig['language'] = $sLanguage;
|
$oPage->add_ready_script("$('#$iId').ckeditor(function() { /* callback code */ }, { language : '$sLanguage' , contentsLanguage : '$sLanguage', extraPlugins: 'disabler' });"); // Transform $iId into a CKEdit
|
||||||
$aConfig['contentsLanguage'] = $sLanguage;
|
|
||||||
$aConfig['extraPlugins'] = 'disabler';
|
|
||||||
$sWidthSpec = addslashes(trim($this->m_oAttDef->GetWidth()));
|
|
||||||
if ($sWidthSpec != '')
|
|
||||||
{
|
|
||||||
$aConfig['width'] = $sWidthSpec;
|
|
||||||
}
|
|
||||||
$sHeightSpec = addslashes(trim($this->m_oAttDef->GetHeight()));
|
|
||||||
if ($sHeightSpec != '')
|
|
||||||
{
|
|
||||||
$aConfig['height'] = $sHeightSpec;
|
|
||||||
}
|
|
||||||
$sConfigJS = json_encode($aConfig);
|
|
||||||
|
|
||||||
$oPage->add_ready_script("$('#$iId').ckeditor(function() { /* callback code */ }, $sConfigJS);"); // Transform $iId into a CKEdit
|
|
||||||
|
|
||||||
// Please read...
|
// Please read...
|
||||||
// ValidateCKEditField triggers a timer... calling itself indefinitely
|
// ValidateCKEditField triggers a timer... calling itself indefinitely
|
||||||
@@ -99,7 +80,7 @@ class UIHTMLEditorWidget
|
|||||||
|
|
||||||
// Could also be bound to 'instanceReady.ckeditor'
|
// Could also be bound to 'instanceReady.ckeditor'
|
||||||
$oPage->add_ready_script("$('#$iId').bind('validate', function(evt, sFormId) { return ValidateCKEditField('$iId', '', {$this->m_sMandatory}, sFormId, '') } );\n");
|
$oPage->add_ready_script("$('#$iId').bind('validate', function(evt, sFormId) { return ValidateCKEditField('$iId', '', {$this->m_sMandatory}, sFormId, '') } );\n");
|
||||||
$oPage->add_ready_script("$('#$iId').bind('update', function() { BlockField('cke_$iId', $('#$iId').attr('disabled')); $(this).data('ckeditorInstance').setReadOnly($(this).prop('disabled')); } );\n");
|
$oPage->add_ready_script("$('#$iId').bind('update', function() { BlockField('cke_$iId', $('#$iId').attr('disabled')); } );\n");
|
||||||
|
|
||||||
return $sHtmlValue;
|
return $sHtmlValue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2012 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
/**
|
/**
|
||||||
* Class UILinksWidgetDirect
|
* Class UILinksWidgetDirect
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ class UILinksWidgetDirect
|
|||||||
$sDefault = "default[$sExtKeyToMe]=".$oCurrentObj->GetKey();
|
$sDefault = "default[$sExtKeyToMe]=".$oCurrentObj->GetKey();
|
||||||
$oAppContext = new ApplicationContext();
|
$oAppContext = new ApplicationContext();
|
||||||
$sParams = $oAppContext->GetForLink();
|
$sParams = $oAppContext->GetForLink();
|
||||||
$oPage->p("<a target=\"_blank\" href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=new&class=$sTargetClass&$sParams&{$sDefault}\">".Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sTargetClass))."</a>\n");
|
$oPage->p("<a target=\"_blank\" href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=new&class=$sTargetClass&$sParams{$sDefault}\">".Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sTargetClass))."</a>\n");
|
||||||
}
|
}
|
||||||
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, false /* bDisplayMenu*/);
|
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, false /* bDisplayMenu*/);
|
||||||
break;
|
break;
|
||||||
@@ -294,7 +294,7 @@ class UILinksWidgetDirect
|
|||||||
$valuesDef = $oLinksetDef->GetValuesDef();
|
$valuesDef = $oLinksetDef->GetValuesDef();
|
||||||
if ($valuesDef === null)
|
if ($valuesDef === null)
|
||||||
{
|
{
|
||||||
$oFilter = new DBObjectSearch($sRemoteClass);
|
$oFilter = new DBObjectSearch($this->sLinkedClass);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -382,7 +382,7 @@ class UILinksWidgetDirect
|
|||||||
/**
|
/**
|
||||||
* Initializes the default search parameters based on 1) a 'current' object and 2) the silos defined by the context
|
* Initializes the default search parameters based on 1) a 'current' object and 2) the silos defined by the context
|
||||||
* @param DBObject $oSourceObj
|
* @param DBObject $oSourceObj
|
||||||
* @param DBSearch $oSearch
|
* @param DBObjectSearch $oSearch
|
||||||
*/
|
*/
|
||||||
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
|
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2012 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
/**
|
/**
|
||||||
* Class UILinksWidget
|
* Class UILinksWidget
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -163,12 +163,6 @@ class UILinksWidget
|
|||||||
$aFieldsMap[$sFieldCode] = $sSafeId;
|
$aFieldsMap[$sFieldCode] = $sSafeId;
|
||||||
}
|
}
|
||||||
$sState = '';
|
$sState = '';
|
||||||
$sJSDaysMin = json_encode(array(Dict::S('DayOfWeek-Sunday-Min'), Dict::S('DayOfWeek-Monday-Min'), Dict::S('DayOfWeek-Tuesday-Min'), Dict::S('DayOfWeek-Wednesday-Min'),
|
|
||||||
Dict::S('DayOfWeek-Thursday-Min'), Dict::S('DayOfWeek-Friday-Min'), Dict::S('DayOfWeek-Saturday-Min')));
|
|
||||||
$sJSMonthsShort = json_encode(array(Dict::S('Month-01-Short'), Dict::S('Month-02-Short'), Dict::S('Month-03-Short'), Dict::S('Month-04-Short'), Dict::S('Month-05-Short'), Dict::S('Month-06-Short'),
|
|
||||||
Dict::S('Month-07-Short'), Dict::S('Month-08-Short'), Dict::S('Month-09-Short'), Dict::S('Month-10-Short'), Dict::S('Month-11-Short'), Dict::S('Month-12-Short')));
|
|
||||||
$iFirstDayOfWeek = (int) Dict::S('Calendar-FirstDayOfWeek');
|
|
||||||
|
|
||||||
$oP->add_script(
|
$oP->add_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
$(".date-pick").datepicker({
|
$(".date-pick").datepicker({
|
||||||
@@ -178,11 +172,8 @@ $(".date-pick").datepicker({
|
|||||||
dateFormat: 'yy-mm-dd',
|
dateFormat: 'yy-mm-dd',
|
||||||
constrainInput: false,
|
constrainInput: false,
|
||||||
changeMonth: true,
|
changeMonth: true,
|
||||||
changeYear: true,
|
changeYear: true
|
||||||
dayNamesMin: $sJSDaysMin,
|
});
|
||||||
monthNamesShort: $sJSMonthsShort,
|
|
||||||
firstDay: $iFirstDayOfWeek
|
|
||||||
});
|
|
||||||
$(".datetime-pick").datepicker({
|
$(".datetime-pick").datepicker({
|
||||||
showOn: 'button',
|
showOn: 'button',
|
||||||
buttonImage: '../images/calendar.png',
|
buttonImage: '../images/calendar.png',
|
||||||
@@ -190,10 +181,7 @@ $(".datetime-pick").datepicker({
|
|||||||
dateFormat: 'yy-mm-dd 00:00:00',
|
dateFormat: 'yy-mm-dd 00:00:00',
|
||||||
constrainInput: false,
|
constrainInput: false,
|
||||||
changeMonth: true,
|
changeMonth: true,
|
||||||
changeYear: true,
|
changeYear: true
|
||||||
dayNamesMin: $sJSDaysMin,
|
|
||||||
monthNamesShort: $sJSMonthsShort,
|
|
||||||
firstDay: $iFirstDayOfWeek
|
|
||||||
});
|
});
|
||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
@@ -452,7 +440,7 @@ EOF
|
|||||||
/**
|
/**
|
||||||
* Initializes the default search parameters based on 1) a 'current' object and 2) the silos defined by the context
|
* Initializes the default search parameters based on 1) a 'current' object and 2) the silos defined by the context
|
||||||
* @param DBObject $oSourceObj
|
* @param DBObject $oSourceObj
|
||||||
* @param DBSearch $oSearch
|
* @param DBObjectSearch $oSearch
|
||||||
*/
|
*/
|
||||||
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
|
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ EOF
|
|||||||
$this->DisplayFormTable($oP, $this->m_aTableConfig, $aForm);
|
$this->DisplayFormTable($oP, $this->m_aTableConfig, $aForm);
|
||||||
$oP->add("<span style=\"float:left;\"> <img src=\"../images/tv-item-last.gif\"> <input id=\"btnRemove\" type=\"button\" value=\"".Dict::S('UI:RemoveLinkedObjectsOf_Class')."\" onClick=\"RemoveSelected();\" >");
|
$oP->add("<span style=\"float:left;\"> <img src=\"../images/tv-item-last.gif\"> <input id=\"btnRemove\" type=\"button\" value=\"".Dict::S('UI:RemoveLinkedObjectsOf_Class')."\" onClick=\"RemoveSelected();\" >");
|
||||||
$oP->add(" <input id=\"btnAdd\" type=\"button\" value=\"".Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sLinkedClass))."\" onClick=\"AddObjects();\"></span>\n");
|
$oP->add(" <input id=\"btnAdd\" type=\"button\" value=\"".Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sLinkedClass))."\" onClick=\"AddObjects();\"></span>\n");
|
||||||
$oP->add("<span style=\"float:right;\"><input id=\"btnCancel\" type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"BackToDetails('".$sTargetClass."', ".$this->m_iObjectId.", '', '');\">");
|
$oP->add("<span style=\"float:right;\"><input id=\"btnCancel\" type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"BackToDetails('".$sTargetClass."', ".$this->m_iObjectId.");\">");
|
||||||
$oP->add(" <input id=\"btnOk\" type=\"submit\" value=\"".Dict::S('UI:Button:Ok')."\"></span>\n");
|
$oP->add(" <input id=\"btnOk\" type=\"submit\" value=\"".Dict::S('UI:Button:Ok')."\"></span>\n");
|
||||||
$oP->add("<span style=\"clear:both;\"><p> </p></span>\n");
|
$oP->add("<span style=\"clear:both;\"><p> </p></span>\n");
|
||||||
$oP->add("</div>\n");
|
$oP->add("</div>\n");
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2013 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
/**
|
/**
|
||||||
* Static class utils
|
* Static class utils
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -334,7 +334,7 @@ class utils
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Interprets the results posted by a normal or paginated list (in multiple selection mode)
|
* Interprets the results posted by a normal or paginated list (in multiple selection mode)
|
||||||
* @param $oFullSetFilter DBSearch The criteria defining the whole sets of objects being selected
|
* @param $oFullSetFilter DBObjectSearch The criteria defining the whole sets of objects being selected
|
||||||
* @return Array An arry of object IDs corresponding to the objects selected in the set
|
* @return Array An arry of object IDs corresponding to the objects selected in the set
|
||||||
*/
|
*/
|
||||||
public static function ReadMultipleSelection($oFullSetFilter)
|
public static function ReadMultipleSelection($oFullSetFilter)
|
||||||
@@ -487,23 +487,19 @@ class utils
|
|||||||
*/
|
*/
|
||||||
static public function GetAbsoluteUrlAppRoot()
|
static public function GetAbsoluteUrlAppRoot()
|
||||||
{
|
{
|
||||||
static $sUrl = null;
|
$sUrl = self::GetConfig()->Get('app_root_url');
|
||||||
if ($sUrl === null)
|
if (strpos($sUrl, SERVER_NAME_PLACEHOLDER) > -1)
|
||||||
{
|
{
|
||||||
$sUrl = self::GetConfig()->Get('app_root_url');
|
if (isset($_SERVER['SERVER_NAME']))
|
||||||
if (strpos($sUrl, SERVER_NAME_PLACEHOLDER) > -1)
|
|
||||||
{
|
{
|
||||||
if (isset($_SERVER['SERVER_NAME']))
|
$sServerName = $_SERVER['SERVER_NAME'];
|
||||||
{
|
|
||||||
$sServerName = $_SERVER['SERVER_NAME'];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// CLI mode ?
|
|
||||||
$sServerName = php_uname('n');
|
|
||||||
}
|
|
||||||
$sUrl = str_replace(SERVER_NAME_PLACEHOLDER, $sServerName, $sUrl);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// CLI mode ?
|
||||||
|
$sServerName = php_uname('n');
|
||||||
|
}
|
||||||
|
$sUrl = str_replace(SERVER_NAME_PLACEHOLDER, $sServerName, $sUrl);
|
||||||
}
|
}
|
||||||
return $sUrl;
|
return $sUrl;
|
||||||
}
|
}
|
||||||
@@ -787,53 +783,38 @@ class utils
|
|||||||
$sOQL = addslashes($param->GetFilter()->ToOQL(true));
|
$sOQL = addslashes($param->GetFilter()->ToOQL(true));
|
||||||
$sFilter = urlencode($param->GetFilter()->serialize());
|
$sFilter = urlencode($param->GetFilter()->serialize());
|
||||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
|
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
|
||||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/tabularfieldsselector.js');
|
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/xlsx-export.js');
|
||||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.dragtable.js');
|
$sXlsxFilter = $param->GetFilter()->serialize();
|
||||||
$oPage->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/dragtable.css');
|
$sXlsxJSFilter = addslashes($sXlsxFilter);
|
||||||
|
|
||||||
$aResult = array(
|
$aResult = array(
|
||||||
new SeparatorPopupMenuItem(),
|
new SeparatorPopupMenuItem(),
|
||||||
// Static menus: Email this page, CSV Export & Add to Dashboard
|
// Static menus: Email this page, CSV Export & Add to Dashboard
|
||||||
new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'), "mailto:?body=".urlencode($sUrl).' '), // Add an extra space to make it work in Outlook
|
new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'), "mailto:?body=".urlencode($sUrl).' '), // Add an extra space to make it work in Outlook
|
||||||
|
new URLPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), $sUrl."&format=csv"),
|
||||||
|
new JSPopupMenuItem('xlsx-export', Dict::S('ExcelExporter:ExportMenu'), "XlsxExportDialog('$sXlsxJSFilter');", array()),
|
||||||
|
new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL')"),
|
||||||
|
new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')"),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (UserRights::IsActionAllowed($param->GetFilter()->GetClass(), UR_ACTION_BULK_READ, $param) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
|
|
||||||
{
|
|
||||||
// Bulk export actions
|
|
||||||
$aResult[] = new JSPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), "ExportListDlg('$sOQL', '$sDataTableId', 'csv', ".json_encode(Dict::S('UI:Menu:CSVExport')).")");
|
|
||||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportXLSX', Dict::S('ExcelExporter:ExportMenu'), "ExportListDlg('$sOQL', '$sDataTableId', 'xlsx', ".json_encode(Dict::S('ExcelExporter:ExportMenu')).")");
|
|
||||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportPDF', Dict::S('UI:Menu:ExportPDF'), "ExportListDlg('$sOQL', '$sDataTableId', 'pdf', ".json_encode(Dict::S('UI:Menu:ExportPDF')).")");
|
|
||||||
}
|
|
||||||
$aResult[] = new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL')");
|
|
||||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')");
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS:
|
case iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS:
|
||||||
// $param is a DBObject
|
// $param is a DBObject
|
||||||
$oObj = $param;
|
$oObj = $param;
|
||||||
$sOQL = "SELECT ".get_class($oObj)." WHERE id=".$oObj->GetKey();
|
$oFilter = DBobjectSearch::FromOQL("SELECT ".get_class($oObj)." WHERE id=".$oObj->GetKey());
|
||||||
$oFilter = DBObjectSearch::FromOQL($sOQL);
|
|
||||||
$sFilter = $oFilter->serialize();
|
$sFilter = $oFilter->serialize();
|
||||||
$sUrl = ApplicationContext::MakeObjectUrl(get_class($oObj), $oObj->GetKey());
|
$sUrl = ApplicationContext::MakeObjectUrl(get_class($oObj), $oObj->GetKey());
|
||||||
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage(get_class($oObj));
|
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage(get_class($oObj));
|
||||||
$oAppContext = new ApplicationContext();
|
$oAppContext = new ApplicationContext();
|
||||||
$sContext = $oAppContext->GetForLink();
|
$sContext = $oAppContext->GetForLink();
|
||||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/tabularfieldsselector.js');
|
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/xlsx-export.js');
|
||||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.dragtable.js');
|
$sXlsxJSFilter = addslashes($sFilter);
|
||||||
$oPage->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/dragtable.css');
|
|
||||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/tabularfieldsselector.js');
|
|
||||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.dragtable.js');
|
|
||||||
$oPage->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/dragtable.css');
|
|
||||||
|
|
||||||
$aResult = array(
|
$aResult = array(
|
||||||
new SeparatorPopupMenuItem(),
|
new SeparatorPopupMenuItem(),
|
||||||
// Static menus: Email this page & CSV Export
|
// Static menus: Email this page & CSV Export
|
||||||
new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'), "mailto:?subject=".urlencode($oObj->GetRawName())."&body=".urlencode($sUrl).' '), // Add an extra space to make it work in Outlook
|
new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'), "mailto:?subject=".urlencode($oObj->GetRawName())."&body=".urlencode($sUrl).' '), // Add an extra space to make it work in Outlook
|
||||||
new JSPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), "ExportListDlg('$sOQL', '', 'csv', ".json_encode(Dict::S('UI:Menu:CSVExport')).")"),
|
new URLPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".urlencode($sFilter)."&format=csv&{$sContext}"),
|
||||||
new JSPopupMenuItem('UI:Menu:ExportXLSX', Dict::S('ExcelExporter:ExportMenu'), "ExportListDlg('$sOQL', '', 'xlsx', ".json_encode(Dict::S('ExcelExporter:ExportMenu')).")"),
|
new JSPopupMenuItem('xlsx-export', Dict::S('ExcelExporter:ExportMenu'), "XlsxExportDialog('$sXlsxJSFilter');", array()),
|
||||||
new SeparatorPopupMenuItem(),
|
|
||||||
new URLPopupMenuItem('UI:Menu:PrintableVersion', Dict::S('UI:Menu:PrintableVersion'), $sUrl.'&printable=1', '_blank'),
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -980,11 +961,10 @@ class utils
|
|||||||
* @param hash $aData The data to POST as an array('param_name' => value)
|
* @param hash $aData The data to POST as an array('param_name' => value)
|
||||||
* @param string $sOptionnalHeaders Additional HTTP headers as a string with newlines between headers
|
* @param string $sOptionnalHeaders Additional HTTP headers as a string with newlines between headers
|
||||||
* @param hash $aResponseHeaders An array to be filled with reponse headers: WARNING: the actual content of the array depends on the library used: cURL or fopen, test with both !! See: http://fr.php.net/manual/en/function.curl-getinfo.php
|
* @param hash $aResponseHeaders An array to be filled with reponse headers: WARNING: the actual content of the array depends on the library used: cURL or fopen, test with both !! See: http://fr.php.net/manual/en/function.curl-getinfo.php
|
||||||
* @param hash $aCurlOptions An (optional) array of options to pass to curl_init. The format is 'option_code' => 'value'. These values have precedence over the default ones. Example: CURLOPT_SSLVERSION => CURL_SSLVERSION_SSLv3
|
|
||||||
* @return string The result of the POST request
|
* @return string The result of the POST request
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
static public function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null, $aCurlOptions = array())
|
static public function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null)
|
||||||
{
|
{
|
||||||
// $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
|
// $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
|
||||||
|
|
||||||
@@ -1002,7 +982,6 @@ class utils
|
|||||||
$aHTTPHeaders[$aMatches[1]] = $aMatches[2];
|
$aHTTPHeaders[$aMatches[1]] = $aMatches[2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Default options, can be overloaded/extended with the 4th parameter of this method, see above $aCurlOptions
|
|
||||||
$aOptions = array(
|
$aOptions = array(
|
||||||
CURLOPT_RETURNTRANSFER => true, // return the content of the request
|
CURLOPT_RETURNTRANSFER => true, // return the content of the request
|
||||||
CURLOPT_HEADER => false, // don't return the headers in the output
|
CURLOPT_HEADER => false, // don't return the headers in the output
|
||||||
@@ -1014,17 +993,14 @@ class utils
|
|||||||
CURLOPT_TIMEOUT => 120, // timeout on response
|
CURLOPT_TIMEOUT => 120, // timeout on response
|
||||||
CURLOPT_MAXREDIRS => 10, // stop after 10 redirects
|
CURLOPT_MAXREDIRS => 10, // stop after 10 redirects
|
||||||
CURLOPT_SSL_VERIFYPEER => false, // Disabled SSL Cert checks
|
CURLOPT_SSL_VERIFYPEER => false, // Disabled SSL Cert checks
|
||||||
// SSLV3 (CURL_SSLVERSION_SSLv3 = 3) is now considered as obsolete/dangerous: http://disablessl3.com/#why
|
CURLOPT_SSLVERSION => 3, // MUST to prevent a strange SSL error: http://stackoverflow.com/questions/18191672/php-curl-ssl-routinesssl23-get-server-helloreason1112
|
||||||
// but it used to be a MUST to prevent a strange SSL error: http://stackoverflow.com/questions/18191672/php-curl-ssl-routinesssl23-get-server-helloreason1112
|
|
||||||
// CURLOPT_SSLVERSION => 3,
|
|
||||||
CURLOPT_POST => count($aData),
|
CURLOPT_POST => count($aData),
|
||||||
CURLOPT_POSTFIELDS => http_build_query($aData),
|
CURLOPT_POSTFIELDS => http_build_query($aData),
|
||||||
CURLOPT_HTTPHEADER => $aHTTPHeaders,
|
CURLOPT_HTTPHEADER => $aHTTPHeaders,
|
||||||
);
|
);
|
||||||
|
|
||||||
$aAllOptions = $aCurlOptions + $aOptions;
|
|
||||||
$ch = curl_init($sUrl);
|
$ch = curl_init($sUrl);
|
||||||
curl_setopt_array($ch, $aAllOptions);
|
curl_setopt_array($ch, $aOptions);
|
||||||
$response = curl_exec($ch);
|
$response = curl_exec($ch);
|
||||||
$iErr = curl_errno($ch);
|
$iErr = curl_errno($ch);
|
||||||
$sErrMsg = curl_error( $ch );
|
$sErrMsg = curl_error( $ch );
|
||||||
@@ -1097,28 +1073,5 @@ class utils
|
|||||||
}
|
}
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a standard list of character sets
|
|
||||||
*
|
|
||||||
* @param array $aAdditionalEncodings Additional values
|
|
||||||
* @return array of iconv code => english label, sorted by label
|
|
||||||
*/
|
|
||||||
public static function GetPossibleEncodings($aAdditionalEncodings = array())
|
|
||||||
{
|
|
||||||
// Encodings supported:
|
|
||||||
// ICONV_CODE => Display Name
|
|
||||||
// Each iconv installation supports different encodings
|
|
||||||
// Some reasonably common and useful encodings are listed here
|
|
||||||
$aPossibleEncodings = array(
|
|
||||||
'UTF-8' => 'Unicode (UTF-8)',
|
|
||||||
'ISO-8859-1' => 'Western (ISO-8859-1)',
|
|
||||||
'WINDOWS-1251' => 'Cyrilic (Windows 1251)',
|
|
||||||
'WINDOWS-1252' => 'Western (Windows 1252)',
|
|
||||||
'ISO-8859-15' => 'Western (ISO-8859-15)',
|
|
||||||
);
|
|
||||||
$aPossibleEncodings = array_merge($aPossibleEncodings, $aAdditionalEncodings);
|
|
||||||
asort($aPossibleEncodings);
|
|
||||||
return $aPossibleEncodings;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
?>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2014 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
/**
|
/**
|
||||||
* Class WebPage
|
* Class WebPage
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -71,9 +71,8 @@ class WebPage implements Page
|
|||||||
protected $bTrashUnexpectedOutput;
|
protected $bTrashUnexpectedOutput;
|
||||||
protected $s_sOutputFormat;
|
protected $s_sOutputFormat;
|
||||||
protected $a_OutputOptions;
|
protected $a_OutputOptions;
|
||||||
protected $bPrintable;
|
|
||||||
|
|
||||||
public function __construct($s_title, $bPrintable = false)
|
public function __construct($s_title)
|
||||||
{
|
{
|
||||||
$this->s_title = $s_title;
|
$this->s_title = $s_title;
|
||||||
$this->s_content = "";
|
$this->s_content = "";
|
||||||
@@ -93,7 +92,6 @@ class WebPage implements Page
|
|||||||
$this->bTrashUnexpectedOutput = false;
|
$this->bTrashUnexpectedOutput = false;
|
||||||
$this->s_OutputFormat = utils::ReadParam('output_format', 'html');
|
$this->s_OutputFormat = utils::ReadParam('output_format', 'html');
|
||||||
$this->a_OutputOptions = array();
|
$this->a_OutputOptions = array();
|
||||||
$this->bPrintable = $bPrintable;
|
|
||||||
ob_start(); // Start capturing the output
|
ob_start(); // Start capturing the output
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,33 +268,7 @@ class WebPage implements Page
|
|||||||
{
|
{
|
||||||
$this->a_linked_stylesheets[] = array( 'link' => $s_linked_stylesheet, 'condition' => $s_condition);
|
$this->a_linked_stylesheets[] = array( 'link' => $s_linked_stylesheet, 'condition' => $s_condition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function add_saas($sSaasRelPath)
|
|
||||||
{
|
|
||||||
$sSaasPath = APPROOT.$sSaasRelPath;
|
|
||||||
$sCssRelPath = preg_replace('/\.scss$/', '.css', $sSaasRelPath);
|
|
||||||
$sCssPath = APPROOT.$sCssRelPath;
|
|
||||||
clearstatcache();
|
|
||||||
if (!file_exists($sCssPath) || (is_writable($sCssPath) && (filemtime($sCssPath) < filemtime($sSaasPath))))
|
|
||||||
{
|
|
||||||
// Rebuild the CSS file from the Saas file
|
|
||||||
if (file_exists(APPROOT.'lib/sass/sass/SassParser.php'))
|
|
||||||
{
|
|
||||||
require_once(APPROOT.'lib/sass/sass/SassParser.php'); //including Sass libary (Syntactically Awesome Stylesheets)
|
|
||||||
$oParser = new SassParser(array('style'=>'expanded'));
|
|
||||||
$sCss = $oParser->toCss($sSaasPath);
|
|
||||||
file_put_contents($sCssPath, $sCss);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$sRootUrl = utils::GetAbsoluteUrlAppRoot();
|
|
||||||
if ($sRootUrl === '')
|
|
||||||
{
|
|
||||||
// We're running the setup of the first install...
|
|
||||||
$sRootUrl = '../';
|
|
||||||
}
|
|
||||||
$sCSSUrl = $sRootUrl.$sCssRelPath;
|
|
||||||
$this->add_linked_stylesheet($sCSSUrl);
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Add some custom header to the page
|
* Add some custom header to the page
|
||||||
*/
|
*/
|
||||||
@@ -322,15 +294,6 @@ class WebPage implements Page
|
|||||||
|
|
||||||
$this->add($this->GetDetails($aFields));
|
$this->add($this->GetDetails($aFields));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not the page is a PDF page
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function is_pdf()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records the current state of the 'html' part of the page output
|
* Records the current state of the 'html' part of the page output
|
||||||
@@ -525,19 +488,11 @@ class WebPage implements Page
|
|||||||
$this->output_dict_entries();
|
$this->output_dict_entries();
|
||||||
foreach($this->a_linked_stylesheets as $a_stylesheet)
|
foreach($this->a_linked_stylesheets as $a_stylesheet)
|
||||||
{
|
{
|
||||||
if (strpos($a_stylesheet['link'], '?') === false)
|
|
||||||
{
|
|
||||||
$s_stylesheet = $a_stylesheet['link']."?itopversion=".ITOP_VERSION;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$s_stylesheet = $a_stylesheet['link']."&itopversion=".ITOP_VERSION;
|
|
||||||
}
|
|
||||||
if ($a_stylesheet['condition'] != "")
|
if ($a_stylesheet['condition'] != "")
|
||||||
{
|
{
|
||||||
echo "<!--[if {$a_stylesheet['condition']}]>\n";
|
echo "<!--[if {$a_stylesheet['condition']}]>\n";
|
||||||
}
|
}
|
||||||
echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"{$s_stylesheet}\" />\n";
|
echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"{$a_stylesheet['link']}\" />\n";
|
||||||
if ($a_stylesheet['condition'] != "")
|
if ($a_stylesheet['condition'] != "")
|
||||||
{
|
{
|
||||||
echo "<![endif]-->\n";
|
echo "<![endif]-->\n";
|
||||||
@@ -555,7 +510,7 @@ class WebPage implements Page
|
|||||||
}
|
}
|
||||||
if (class_exists('MetaModel') && MetaModel::GetConfig())
|
if (class_exists('MetaModel') && MetaModel::GetConfig())
|
||||||
{
|
{
|
||||||
echo "<link rel=\"shortcut icon\" href=\"".utils::GetAbsoluteUrlAppRoot()."images/favicon.ico?itopversion=".ITOP_VERSION."\" />\n";
|
echo "<link rel=\"shortcut icon\" href=\"".utils::GetAbsoluteUrlAppRoot()."images/favicon.ico\" />\n";
|
||||||
}
|
}
|
||||||
echo "</head>\n";
|
echo "</head>\n";
|
||||||
echo "<body>\n";
|
echo "<body>\n";
|
||||||
@@ -568,9 +523,9 @@ class WebPage implements Page
|
|||||||
echo "</body>\n";
|
echo "</body>\n";
|
||||||
echo "</html>\n";
|
echo "</html>\n";
|
||||||
|
|
||||||
if (class_exists('DBSearch'))
|
if (class_exists('MetaModel'))
|
||||||
{
|
{
|
||||||
DBSearch::RecordQueryTrace();
|
MetaModel::RecordQueryTrace();
|
||||||
}
|
}
|
||||||
if (class_exists('ExecutionKPI'))
|
if (class_exists('ExecutionKPI'))
|
||||||
{
|
{
|
||||||
@@ -697,16 +652,7 @@ class WebPage implements Page
|
|||||||
}
|
}
|
||||||
return $bResult;
|
return $bResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether the output must be printable (using print.css, for sure!)
|
|
||||||
* @return bool ...
|
|
||||||
*/
|
|
||||||
public function IsPrintableVersion()
|
|
||||||
{
|
|
||||||
return $this->bPrintable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the value of a named output option for the given format
|
* Retrieves the value of a named output option for the given format
|
||||||
* @param string $sFormat The format: html or pdf
|
* @param string $sFormat The format: html or pdf
|
||||||
@@ -743,34 +689,32 @@ class WebPage implements Page
|
|||||||
{
|
{
|
||||||
$sPrevUrl = '';
|
$sPrevUrl = '';
|
||||||
$sHtml = '';
|
$sHtml = '';
|
||||||
if (!$this->IsPrintableVersion())
|
foreach ($aActions as $aAction)
|
||||||
{
|
{
|
||||||
foreach ($aActions as $aAction)
|
$sClass = isset($aAction['class']) ? " class=\"{$aAction['class']}\"" : "";
|
||||||
|
$sOnClick = isset($aAction['onclick']) ? ' onclick="'.htmlspecialchars($aAction['onclick'], ENT_QUOTES, "UTF-8").'"' : '';
|
||||||
|
$sTarget = isset($aAction['target']) ? " target=\"{$aAction['target']}\"" : "";
|
||||||
|
if (empty($aAction['url']))
|
||||||
{
|
{
|
||||||
$sClass = isset($aAction['class']) ? " class=\"{$aAction['class']}\"" : "";
|
if ($sPrevUrl != '') // Don't output consecutively two separators...
|
||||||
$sOnClick = isset($aAction['onclick']) ? ' onclick="'.htmlspecialchars($aAction['onclick'], ENT_QUOTES, "UTF-8").'"' : '';
|
|
||||||
$sTarget = isset($aAction['target']) ? " target=\"{$aAction['target']}\"" : "";
|
|
||||||
if (empty($aAction['url']))
|
|
||||||
{
|
{
|
||||||
if ($sPrevUrl != '') // Don't output consecutively two separators...
|
$sHtml .= "<li>{$aAction['label']}</li>";
|
||||||
{
|
|
||||||
$sHtml .= "<li>{$aAction['label']}</li>";
|
|
||||||
}
|
|
||||||
$sPrevUrl = '';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sHtml .= "<li><a $sTarget href=\"{$aAction['url']}\"$sClass $sOnClick>{$aAction['label']}</a></li>";
|
|
||||||
$sPrevUrl = $aAction['url'];
|
|
||||||
}
|
}
|
||||||
|
$sPrevUrl = '';
|
||||||
}
|
}
|
||||||
$sHtml .= "</ul></li></ul></div>";
|
else
|
||||||
foreach(array_reverse($aFavoriteActions) as $aAction)
|
|
||||||
{
|
{
|
||||||
$sTarget = isset($aAction['target']) ? " target=\"{$aAction['target']}\"" : "";
|
$sHtml .= "<li><a $sTarget href=\"{$aAction['url']}\"$sClass $sOnClick>{$aAction['label']}</a></li>";
|
||||||
$sHtml .= "<div class=\"actions_button\"><a $sTarget href='{$aAction['url']}'>{$aAction['label']}</a></div>";
|
$sPrevUrl = $aAction['url'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$sHtml .= "</ul></li></ul></div>";
|
||||||
|
foreach(array_reverse($aFavoriteActions) as $aAction)
|
||||||
|
{
|
||||||
|
$sTarget = isset($aAction['target']) ? " target=\"{$aAction['target']}\"" : "";
|
||||||
|
$sHtml .= "<div class=\"actions_button\"><a $sTarget href='{$aAction['url']}'>{$aAction['label']}</a></div>";
|
||||||
|
}
|
||||||
|
|
||||||
return $sHtml;
|
return $sHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -994,11 +938,11 @@ class TabManager
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the tab whose title matches a given pattern
|
* Finds the tab whose title matches a given pattern
|
||||||
* @return mixed The actual name of the tab (as a string) or false if not found
|
* @return mixed The name of the tab as a string or false if not found
|
||||||
*/
|
*/
|
||||||
public function FindTab($sPattern, $sTabContainer = null)
|
public function FindTab($sPattern, $sTabContainer = null)
|
||||||
{
|
{
|
||||||
$result = false;
|
$return = false;
|
||||||
if ($sTabContainer == null)
|
if ($sTabContainer == null)
|
||||||
{
|
{
|
||||||
$sTabContainer = $this->m_sCurrentTabContainer;
|
$sTabContainer = $this->m_sCurrentTabContainer;
|
||||||
@@ -1044,7 +988,7 @@ class TabManager
|
|||||||
return "window.setTimeout(\"$('$sSelector').tabs('select', $tab_index);\", 100);"; // Let the time to the tabs widget to initialize
|
return "window.setTimeout(\"$('$sSelector').tabs('select', $tab_index);\", 100);"; // Let the time to the tabs widget to initialize
|
||||||
}
|
}
|
||||||
|
|
||||||
public function RenderIntoContent($sContent, WebPage $oPage)
|
public function RenderIntoContent($sContent)
|
||||||
{
|
{
|
||||||
// Render the tabs in the page (if any)
|
// Render the tabs in the page (if any)
|
||||||
foreach($this->m_aTabs as $sTabContainerName => $aTabs)
|
foreach($this->m_aTabs as $sTabContainerName => $aTabs)
|
||||||
@@ -1054,86 +998,42 @@ class TabManager
|
|||||||
$container_index = 0;
|
$container_index = 0;
|
||||||
if (count($aTabs['tabs']) > 0)
|
if (count($aTabs['tabs']) > 0)
|
||||||
{
|
{
|
||||||
if ($oPage->IsPrintableVersion())
|
$sTabs = "<!-- tabs -->\n<div id=\"tabbedContent_{$sPrefix}{$container_index}\" class=\"light\">\n";
|
||||||
|
$sTabs .= "<ul>\n";
|
||||||
|
// Display the unordered list that will be rendered as the tabs
|
||||||
|
$i = 0;
|
||||||
|
foreach($aTabs['tabs'] as $sTabName => $aTabData)
|
||||||
{
|
{
|
||||||
$oPage->add_ready_script(
|
switch($aTabData['type'])
|
||||||
<<< EOF
|
|
||||||
oHiddeableChapters = {};
|
|
||||||
EOF
|
|
||||||
);
|
|
||||||
$sTabs = "<!-- tabs -->\n<div id=\"tabbedContent_{$sPrefix}{$container_index}\" class=\"light\">\n";
|
|
||||||
$i = 0;
|
|
||||||
foreach($aTabs['tabs'] as $sTabName => $aTabData)
|
|
||||||
{
|
{
|
||||||
$sTabNameEsc = addslashes($sTabName);
|
case 'ajax':
|
||||||
$sTabId = "tab_{$sPrefix}{$container_index}$i";
|
$sTabs .= "<li data-cache=\"".($aTabData['cache'] ? 'true' : 'false')."\"><a href=\"{$aTabData['url']}\" class=\"tab\"><span>".htmlentities($sTabName, ENT_QUOTES, 'UTF-8')."</span></a></li>\n";
|
||||||
switch($aTabData['type'])
|
break;
|
||||||
{
|
|
||||||
case 'ajax':
|
case 'html':
|
||||||
$sTabHtml = '';
|
default:
|
||||||
$sUrl = $aTabData['url'];
|
$sTabs .= "<li><a href=\"#tab_{$sPrefix}{$container_index}$i\" class=\"tab\"><span>".htmlentities($sTabName, ENT_QUOTES, 'UTF-8')."</span></a></li>\n";
|
||||||
$oPage->add_ready_script(
|
|
||||||
<<< EOF
|
|
||||||
$.post('$sUrl', {printable: '1'}, function(data){
|
|
||||||
$('#$sTabId > .printable-tab-content').append(data);
|
|
||||||
});
|
|
||||||
EOF
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'html':
|
|
||||||
default:
|
|
||||||
$sTabHtml = $aTabData['html'];
|
|
||||||
}
|
|
||||||
$sTabs .= "<div class=\"printable-tab\" id=\"$sTabId\"><h2 class=\"printable-tab-title\">".htmlentities($sTabName, ENT_QUOTES, 'UTF-8')."</h2><div class=\"printable-tab-content\">".$sTabHtml."</div></div>\n";
|
|
||||||
$oPage->add_ready_script(
|
|
||||||
<<< EOF
|
|
||||||
oHiddeableChapters['$sTabId'] = '$sTabNameEsc';
|
|
||||||
EOF
|
|
||||||
);
|
|
||||||
$i++;
|
|
||||||
}
|
}
|
||||||
$sTabs .= "</div>\n<!-- end of tabs-->\n";
|
$i++;
|
||||||
}
|
}
|
||||||
else
|
$sTabs .= "</ul>\n";
|
||||||
|
// Now add the content of the tabs themselves
|
||||||
|
$i = 0;
|
||||||
|
foreach($aTabs['tabs'] as $sTabName => $aTabData)
|
||||||
{
|
{
|
||||||
$sTabs = "<!-- tabs -->\n<div id=\"tabbedContent_{$sPrefix}{$container_index}\" class=\"light\">\n";
|
switch($aTabData['type'])
|
||||||
$sTabs .= "<ul>\n";
|
|
||||||
// Display the unordered list that will be rendered as the tabs
|
|
||||||
$i = 0;
|
|
||||||
foreach($aTabs['tabs'] as $sTabName => $aTabData)
|
|
||||||
{
|
{
|
||||||
switch($aTabData['type'])
|
case 'ajax':
|
||||||
{
|
// Nothing to add
|
||||||
case 'ajax':
|
break;
|
||||||
$sTabs .= "<li data-cache=\"".($aTabData['cache'] ? 'true' : 'false')."\"><a href=\"{$aTabData['url']}\" class=\"tab\"><span>".htmlentities($sTabName, ENT_QUOTES, 'UTF-8')."</span></a></li>\n";
|
|
||||||
break;
|
case 'html':
|
||||||
|
default:
|
||||||
case 'html':
|
$sTabs .= "<div id=\"tab_{$sPrefix}{$container_index}$i\">".$aTabData['html']."</div>\n";
|
||||||
default:
|
|
||||||
$sTabs .= "<li><a href=\"#tab_{$sPrefix}{$container_index}$i\" class=\"tab\"><span>".htmlentities($sTabName, ENT_QUOTES, 'UTF-8')."</span></a></li>\n";
|
|
||||||
}
|
|
||||||
$i++;
|
|
||||||
}
|
}
|
||||||
$sTabs .= "</ul>\n";
|
$i++;
|
||||||
// Now add the content of the tabs themselves
|
|
||||||
$i = 0;
|
|
||||||
foreach($aTabs['tabs'] as $sTabName => $aTabData)
|
|
||||||
{
|
|
||||||
switch($aTabData['type'])
|
|
||||||
{
|
|
||||||
case 'ajax':
|
|
||||||
// Nothing to add
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'html':
|
|
||||||
default:
|
|
||||||
$sTabs .= "<div id=\"tab_{$sPrefix}{$container_index}$i\">".$aTabData['html']."</div>\n";
|
|
||||||
}
|
|
||||||
$i++;
|
|
||||||
}
|
|
||||||
$sTabs .= "</div>\n<!-- end of tabs-->\n";
|
|
||||||
}
|
}
|
||||||
|
$sTabs .= "</div>\n<!-- end of tabs-->\n";
|
||||||
}
|
}
|
||||||
$sContent = str_replace("\$Tabs:$sTabContainerName\$", $sTabs, $sContent);
|
$sContent = str_replace("\$Tabs:$sTabContainerName\$", $sTabs, $sContent);
|
||||||
$container_index++;
|
$container_index++;
|
||||||
|
|||||||
@@ -113,16 +113,8 @@ class WizardHelper
|
|||||||
{
|
{
|
||||||
// For external keys: load the target object so that external fields
|
// For external keys: load the target object so that external fields
|
||||||
// get filled too
|
// get filled too
|
||||||
$oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value, false);
|
$oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value);
|
||||||
if ($oTargetObj)
|
$oObj->Set($sAttCode, $oTargetObj);
|
||||||
{
|
|
||||||
$oObj->Set($sAttCode, $oTargetObj);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// May happen for security reasons (portal, see ticket #1074)
|
|
||||||
$oObj->Set($sAttCode, $value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ Class XLSXWriter
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function writeSheet(array $data, $sheet_name='', array $header_types=array(), array $header_row=array() )
|
public function writeSheet(array $data, $sheet_name='', array $header_types=array() )
|
||||||
{
|
{
|
||||||
$data = empty($data) ? array( array('') ) : $data;
|
$data = empty($data) ? array( array('') ) : $data;
|
||||||
|
|
||||||
@@ -95,10 +95,7 @@ Class XLSXWriter
|
|||||||
|
|
||||||
$tabselected = count($this->sheets_meta)==1 ? 'true' : 'false';//only first sheet is selected
|
$tabselected = count($this->sheets_meta)==1 ? 'true' : 'false';//only first sheet is selected
|
||||||
$cell_formats_arr = empty($header_types) ? array_fill(0, $column_count, 'string') : array_values($header_types);
|
$cell_formats_arr = empty($header_types) ? array_fill(0, $column_count, 'string') : array_values($header_types);
|
||||||
if (empty($header_row) && !empty($header_types))
|
$header_row = empty($header_types) ? array() : array_keys($header_types);
|
||||||
{
|
|
||||||
$header_row = empty($header_types) ? array() : array_keys($header_types);
|
|
||||||
}
|
|
||||||
|
|
||||||
$fd = fopen($sheet_filename, "w+");
|
$fd = fopen($sheet_filename, "w+");
|
||||||
if ($fd===false) { self::log("write failed in ".__CLASS__."::".__FUNCTION__."."); return; }
|
if ($fd===false) { self::log("write failed in ".__CLASS__."::".__FUNCTION__."."); return; }
|
||||||
@@ -154,7 +151,7 @@ Class XLSXWriter
|
|||||||
$cell = self::xlsCell($row_number, $column_number);
|
$cell = self::xlsCell($row_number, $column_number);
|
||||||
$s = isset($styles[$cell_format]) ? $styles[$cell_format] : '0';
|
$s = isset($styles[$cell_format]) ? $styles[$cell_format] : '0';
|
||||||
|
|
||||||
if (is_int($value) || is_float($value)) {
|
if (is_numeric($value)) {
|
||||||
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.($value*1).'</v></c>');//int,float, etc
|
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.($value*1).'</v></c>');//int,float, etc
|
||||||
} else if ($cell_format=='date') {
|
} else if ($cell_format=='date') {
|
||||||
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.intval(self::convert_date_time($value)).'</v></c>');
|
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.intval(self::convert_date_time($value)).'</v></c>');
|
||||||
@@ -220,7 +217,7 @@ Class XLSXWriter
|
|||||||
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="9"/>');
|
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="9"/>');
|
||||||
fwrite($fd, '</cellStyleXfs>');
|
fwrite($fd, '</cellStyleXfs>');
|
||||||
fwrite($fd, '<cellXfs count="4">');
|
fwrite($fd, '<cellXfs count="4">');
|
||||||
fwrite($fd, '<xf applyAlignment="1" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="164" xfId="0"><alignment wrapText="1"/></xf>');
|
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="164" xfId="0"/>');
|
||||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="165" xfId="0"/>');
|
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="165" xfId="0"/>');
|
||||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="166" xfId="0"/>');
|
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="166" xfId="0"/>');
|
||||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="167" xfId="0"/>');
|
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="167" xfId="0"/>');
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2014 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
/**
|
/**
|
||||||
* Class XMLPage
|
* Class XMLPage
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -62,9 +62,9 @@ class XMLPage extends WebPage
|
|||||||
}
|
}
|
||||||
echo $this->s_content;
|
echo $this->s_content;
|
||||||
}
|
}
|
||||||
if (class_exists('DBSearch'))
|
if (class_exists('MetaModel'))
|
||||||
{
|
{
|
||||||
DBSearch::RecordQueryTrace();
|
MetaModel::RecordQueryTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ class ActionEmail extends ActionNotification
|
|||||||
{
|
{
|
||||||
$aParams = array
|
$aParams = array
|
||||||
(
|
(
|
||||||
"category" => "core/cmdb,application",
|
"category" => "core/cmdb,bizmodel",
|
||||||
"key_type" => "autoincrement",
|
"key_type" => "autoincrement",
|
||||||
"name_attcode" => "name",
|
"name_attcode" => "name",
|
||||||
"state_attcode" => "",
|
"state_attcode" => "",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2013 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
/**
|
/**
|
||||||
* Bulk change facility (common to interactive and batch usages)
|
* Bulk change facility (common to interactive and batch usages)
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -295,7 +295,7 @@ class BulkChange
|
|||||||
protected function ResolveExternalKey($aRowData, $sAttCode, &$aResults)
|
protected function ResolveExternalKey($aRowData, $sAttCode, &$aResults)
|
||||||
{
|
{
|
||||||
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||||
$oReconFilter = new DBObjectSearch($oExtKey->GetTargetClass());
|
$oReconFilter = new CMDBSearchFilter($oExtKey->GetTargetClass());
|
||||||
foreach ($this->m_aExtKeys[$sAttCode] as $sForeignAttCode => $iCol)
|
foreach ($this->m_aExtKeys[$sAttCode] as $sForeignAttCode => $iCol)
|
||||||
{
|
{
|
||||||
if ($sForeignAttCode == 'id')
|
if ($sForeignAttCode == 'id')
|
||||||
@@ -366,7 +366,7 @@ class BulkChange
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$oReconFilter = new DBObjectSearch($oExtKey->GetTargetClass());
|
$oReconFilter = new CMDBSearchFilter($oExtKey->GetTargetClass());
|
||||||
$aCacheKeys = array();
|
$aCacheKeys = array();
|
||||||
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
|
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
|
||||||
{
|
{
|
||||||
@@ -839,7 +839,7 @@ class BulkChange
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$oReconciliationFilter = new DBObjectSearch($this->m_sClass);
|
$oReconciliationFilter = new CMDBSearchFilter($this->m_sClass);
|
||||||
$bSkipQuery = false;
|
$bSkipQuery = false;
|
||||||
foreach($this->m_aReconcilKeys as $sAttCode)
|
foreach($this->m_aReconcilKeys as $sAttCode)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,420 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Copyright (C) 2015 Combodo SARL
|
|
||||||
//
|
|
||||||
// This file is part of iTop.
|
|
||||||
//
|
|
||||||
// iTop is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// iTop is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
|
|
||||||
define('EXPORTER_DEFAULT_CHUNK_SIZE', 1000);
|
|
||||||
|
|
||||||
class BulkExportException extends Exception
|
|
||||||
{
|
|
||||||
protected $sLocalizedMessage;
|
|
||||||
public function __construct($message, $sLocalizedMessage, $code = null, $previous = null)
|
|
||||||
{
|
|
||||||
parent::__construct($message, $code, $previous);
|
|
||||||
$this->sLocalizedMessage = $sLocalizedMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetLocalizedMessage()
|
|
||||||
{
|
|
||||||
return $this->sLocalizedMessage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class BulkExportMissingParameterException extends BulkExportException
|
|
||||||
{
|
|
||||||
public function __construct($sFieldCode)
|
|
||||||
{
|
|
||||||
parent::__construct('Missing parameter: '.$sFieldCode, Dict::Format('Core:BulkExport:MissingParameter_Param', $sFieldCode));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class BulkExport
|
|
||||||
*
|
|
||||||
* @copyright Copyright (C) 2015 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
class BulkExportResult extends DBObject
|
|
||||||
{
|
|
||||||
public static function Init()
|
|
||||||
{
|
|
||||||
$aParams = array
|
|
||||||
(
|
|
||||||
"category" => 'core/cmdb',
|
|
||||||
"key_type" => 'autoincrement',
|
|
||||||
"name_attcode" => array('created'),
|
|
||||||
"state_attcode" => '',
|
|
||||||
"reconc_keys" => array(),
|
|
||||||
"db_table" => 'priv_bulk_export_result',
|
|
||||||
"db_key_field" => 'id',
|
|
||||||
"db_finalclass_field" => '',
|
|
||||||
"display_template" => '',
|
|
||||||
);
|
|
||||||
MetaModel::Init_Params($aParams);
|
|
||||||
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeInteger("user_id", array("allowed_values"=>null, "sql"=>"user_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeInteger("chunk_size", array("allowed_values"=>null, "sql"=>"chunk_size", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeString("format", array("allowed_values"=>null, "sql"=>"format", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeString("temp_file_path", array("allowed_values"=>null, "sql"=>"temp_file_path", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeLongText("search", array("allowed_values"=>null, "sql"=>"search", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeLongText("status_info", array("allowed_values"=>null, "sql"=>"status_info", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function ComputeValues()
|
|
||||||
{
|
|
||||||
$this->Set('user_id', UserRights::GetUserId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Garbage collector for cleaning "old" export results from the database and the disk.
|
|
||||||
* This background process runs once per day and deletes the results of all exports which
|
|
||||||
* are older than one day.
|
|
||||||
*/
|
|
||||||
class BulkExportResultGC implements iBackgroundProcess
|
|
||||||
{
|
|
||||||
public function GetPeriodicity()
|
|
||||||
{
|
|
||||||
return 24*3600; // seconds
|
|
||||||
}
|
|
||||||
|
|
||||||
public function Process($iTimeLimit)
|
|
||||||
{
|
|
||||||
$sDateLimit = date('Y-m-d H:i:s', time() - 24*3600); // Every BulkExportResult older than one day will be deleted
|
|
||||||
|
|
||||||
$sOQL = "SELECT BulkExportResult WHERE created < '$sDateLimit'";
|
|
||||||
$iProcessed = 0;
|
|
||||||
while (time() < $iTimeLimit)
|
|
||||||
{
|
|
||||||
// Next one ?
|
|
||||||
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('created' => true) /* order by*/, array(), null, 1 /* limit count */);
|
|
||||||
$oSet->OptimizeColumnLoad(array('temp_file_path'));
|
|
||||||
$oResult = $oSet->Fetch();
|
|
||||||
if (is_null($oResult))
|
|
||||||
{
|
|
||||||
// Nothing to be done
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$iProcessed++;
|
|
||||||
@unlink($oResult->Get('temp_file_path'));
|
|
||||||
$oResult->DBDelete();
|
|
||||||
}
|
|
||||||
return "Cleaned $iProcessed old export results(s).";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class BulkExport
|
|
||||||
*
|
|
||||||
* @copyright Copyright (C) 2015 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
abstract class BulkExport
|
|
||||||
{
|
|
||||||
protected $oSearch;
|
|
||||||
protected $iChunkSize;
|
|
||||||
protected $sFormatCode;
|
|
||||||
protected $aStatusInfo;
|
|
||||||
protected $oBulkExportResult;
|
|
||||||
protected $sTmpFile;
|
|
||||||
protected $bLocalizeOutput;
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->oSearch = null;
|
|
||||||
$this->iChunkSize = 0;
|
|
||||||
$this->sFormatCode = null;
|
|
||||||
$this->aStatusInfo = array();
|
|
||||||
$this->oBulkExportResult = null;
|
|
||||||
$this->sTmpFile = '';
|
|
||||||
$this->bLocalizeOutput = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the first class capable of exporting the data in the given format
|
|
||||||
* @param string $sFormat The lowercase format (e.g. html, csv, spreadsheet, xlsx, xml, json, pdf...)
|
|
||||||
* @param DBSearch $oSearch The search/filter defining the set of objects to export or null when listing the supported formats
|
|
||||||
* @return iBulkExport|NULL
|
|
||||||
*/
|
|
||||||
static public function FindExporter($sFormatCode, $oSearch = null)
|
|
||||||
{
|
|
||||||
foreach(get_declared_classes() as $sPHPClass)
|
|
||||||
{
|
|
||||||
$oRefClass = new ReflectionClass($sPHPClass);
|
|
||||||
if ($oRefClass->isSubclassOf('BulkExport') && !$oRefClass->isAbstract())
|
|
||||||
{
|
|
||||||
$oBulkExporter = new $sPHPClass();
|
|
||||||
if ($oBulkExporter->IsFormatSupported($sFormatCode, $oSearch))
|
|
||||||
{
|
|
||||||
if ($oSearch)
|
|
||||||
{
|
|
||||||
$oBulkExporter->SetObjectList($oSearch);
|
|
||||||
}
|
|
||||||
return $oBulkExporter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the exporter corresponding to the given persistent token
|
|
||||||
* @param int $iPersistentToken The identifier of the BulkExportResult object storing the information
|
|
||||||
* @return iBulkExport|NULL
|
|
||||||
*/
|
|
||||||
static public function FindExporterFromToken($iPersistentToken = null)
|
|
||||||
{
|
|
||||||
$oBulkExporter = null;
|
|
||||||
$oInfo = MetaModel::GetObject('BulkExportResult', $iPersistentToken, false);
|
|
||||||
if ($oInfo && ($oInfo->Get('user_id') == UserRights::GetUserId()))
|
|
||||||
{
|
|
||||||
$sFormatCode = $oInfo->Get('format');
|
|
||||||
$oSearch = DBObjectSearch::unserialize($oInfo->Get('search'));
|
|
||||||
|
|
||||||
$oBulkExporter = self::FindExporter($sFormatCode, $oSearch);
|
|
||||||
if ($oBulkExporter)
|
|
||||||
{
|
|
||||||
$oBulkExporter->SetFormat($sFormatCode);
|
|
||||||
$oBulkExporter->SetObjectList($oSearch);
|
|
||||||
$oBulkExporter->SetChunkSize($oInfo->Get('chunk_size'));
|
|
||||||
$oBulkExporter->SetStatusInfo(json_decode($oInfo->Get('status_info'), true));
|
|
||||||
$oBulkExporter->sTmpFile = $oInfo->Get('temp_file_path');
|
|
||||||
$oBulkExporter->oBulkExportResult = $oInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $oBulkExporter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function AppendToTmpFile($data)
|
|
||||||
{
|
|
||||||
if ($this->sTmpFile == '')
|
|
||||||
{
|
|
||||||
$this->sTmpFile = $this->MakeTmpFile($this->GetFileExtension());
|
|
||||||
}
|
|
||||||
$hFile = fopen($this->sTmpFile, 'ab');
|
|
||||||
if ($hFile !== false)
|
|
||||||
{
|
|
||||||
fwrite($hFile, $data);
|
|
||||||
fclose($hFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetTmpFilePath()
|
|
||||||
{
|
|
||||||
return $this->sTmpFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lists all possible export formats. The output is a hash array in the form: 'format_code' => 'localized format label'
|
|
||||||
* @return multitype:string
|
|
||||||
*/
|
|
||||||
static public function FindSupportedFormats()
|
|
||||||
{
|
|
||||||
$aSupportedFormats = array();
|
|
||||||
foreach(get_declared_classes() as $sPHPClass)
|
|
||||||
{
|
|
||||||
$oRefClass = new ReflectionClass($sPHPClass);
|
|
||||||
if ($oRefClass->isSubClassOf('BulkExport') && !$oRefClass->isAbstract())
|
|
||||||
{
|
|
||||||
$oBulkExporter = new $sPHPClass;
|
|
||||||
$aFormats = $oBulkExporter->GetSupportedFormats();
|
|
||||||
$aSupportedFormats = array_merge($aSupportedFormats, $aFormats);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $aSupportedFormats;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* (non-PHPdoc)
|
|
||||||
* @see iBulkExport::SetChunkSize()
|
|
||||||
*/
|
|
||||||
public function SetChunkSize($iChunkSize)
|
|
||||||
{
|
|
||||||
$this->iChunkSize = $iChunkSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* (non-PHPdoc)
|
|
||||||
* @see iBulkExport::SetObjectList()
|
|
||||||
*/
|
|
||||||
public function SetObjectList(DBSearch $oSearch)
|
|
||||||
{
|
|
||||||
$this->oSearch = $oSearch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function SetFormat($sFormatCode)
|
|
||||||
{
|
|
||||||
$this->sFormatCode = $sFormatCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* (non-PHPdoc)
|
|
||||||
* @see iBulkExport::IsFormatSupported()
|
|
||||||
*/
|
|
||||||
public function IsFormatSupported($sFormatCode, $oSearch = null)
|
|
||||||
{
|
|
||||||
return array_key_exists($sFormatCode, $this->GetSupportedFormats());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* (non-PHPdoc)
|
|
||||||
* @see iBulkExport::GetSupportedFormats()
|
|
||||||
*/
|
|
||||||
public function GetSupportedFormats()
|
|
||||||
{
|
|
||||||
return array(); // return array('csv' => Dict::S('UI:ExportFormatCSV'));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function SetHttpHeaders(WebPage $oPage)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetHeader()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
abstract public function GetNextChunk(&$aStatus);
|
|
||||||
public function GetFooter()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function SaveState()
|
|
||||||
{
|
|
||||||
if ($this->oBulkExportResult === null)
|
|
||||||
{
|
|
||||||
$this->oBulkExportResult = new BulkExportResult();
|
|
||||||
$this->oBulkExportResult->Set('format', $this->sFormatCode);
|
|
||||||
$this->oBulkExportResult->Set('search', $this->oSearch->serialize());
|
|
||||||
$this->oBulkExportResult->Set('chunk_size', $this->iChunkSize);
|
|
||||||
$this->oBulkExportResult->Set('temp_file_path', $this->sTmpFile);
|
|
||||||
}
|
|
||||||
$this->oBulkExportResult->Set('status_info', json_encode($this->GetStatusInfo()));
|
|
||||||
return $this->oBulkExportResult->DBWrite();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function Cleanup()
|
|
||||||
{
|
|
||||||
if (($this->oBulkExportResult && (!$this->oBulkExportResult->IsNew())))
|
|
||||||
{
|
|
||||||
$sFilename = $this->oBulkExportResult->Get('temp_file_path');
|
|
||||||
if ($sFilename != '')
|
|
||||||
{
|
|
||||||
@unlink($sFilename);
|
|
||||||
}
|
|
||||||
$this->oBulkExportResult->DBDelete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function EnumFormParts()
|
|
||||||
{
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function DisplayFormPart(WebPage $oP, $sPartId)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function DisplayUsage(Page $oP)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
public function ReadParameters()
|
|
||||||
{
|
|
||||||
$this->bLocalizeOutput = !((bool)utils::ReadParam('no_localize', 0, true, 'integer'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetResultAsHtml()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
public function GetRawResult()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
public function GetMimeType()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
public function GetFileExtension()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
public function GetCharacterSet()
|
|
||||||
{
|
|
||||||
return 'UTF-8';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetStatistics()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetDownloadFileName()
|
|
||||||
{
|
|
||||||
return Dict::Format('Core:BulkExportOf_Class', MetaModel::GetName($this->oSearch->GetClass())).'.'.$this->GetFileExtension();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function SetStatusInfo($aStatusInfo)
|
|
||||||
{
|
|
||||||
$this->aStatusInfo = $aStatusInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetStatusInfo()
|
|
||||||
{
|
|
||||||
return $this->aStatusInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function MakeTmpFile($sExtension)
|
|
||||||
{
|
|
||||||
if(!is_dir(APPROOT."data/bulk_export"))
|
|
||||||
{
|
|
||||||
@mkdir(APPROOT."data/bulk_export", 0777, true /* recursive */);
|
|
||||||
clearstatcache();
|
|
||||||
}
|
|
||||||
if (!is_writable(APPROOT."data/bulk_export"))
|
|
||||||
{
|
|
||||||
throw new Exception('Data directory "'.APPROOT.'data/bulk_export" could not be written.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$iNum = rand();
|
|
||||||
$sFileName = '';
|
|
||||||
do
|
|
||||||
{
|
|
||||||
$iNum++;
|
|
||||||
$sToken = sprintf("%08x", $iNum);
|
|
||||||
$sFileName = APPROOT."data/bulk_export/$sToken.".$sExtension;
|
|
||||||
$hFile = @fopen($sFileName, 'x');
|
|
||||||
}
|
|
||||||
while($hFile === false);
|
|
||||||
|
|
||||||
fclose($hFile);
|
|
||||||
return $sFileName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The built-in exports
|
|
||||||
require_once(APPROOT.'core/tabularbulkexport.class.inc.php');
|
|
||||||
require_once(APPROOT.'core/htmlbulkexport.class.inc.php');
|
|
||||||
require_once(APPROOT.'core/pdfbulkexport.class.inc.php');
|
|
||||||
require_once(APPROOT.'core/csvbulkexport.class.inc.php');
|
|
||||||
require_once(APPROOT.'core/excelbulkexport.class.inc.php');
|
|
||||||
require_once(APPROOT.'core/spreadsheetbulkexport.class.inc.php');
|
|
||||||
require_once(APPROOT.'core/xmlbulkexport.class.inc.php');
|
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ class CMDBChangeOp extends DBObject
|
|||||||
MetaModel::Init_AddAttribute(new AttributeExternalField("date", array("allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"date")));
|
MetaModel::Init_AddAttribute(new AttributeExternalField("date", array("allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"date")));
|
||||||
MetaModel::Init_AddAttribute(new AttributeExternalField("userinfo", array("allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"userinfo")));
|
MetaModel::Init_AddAttribute(new AttributeExternalField("userinfo", array("allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"userinfo")));
|
||||||
MetaModel::Init_AddAttribute(new AttributeString("objclass", array("allowed_values"=>null, "sql"=>"objclass", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeString("objclass", array("allowed_values"=>null, "sql"=>"objclass", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||||
MetaModel::Init_AddAttribute(new AttributeObjectKey("objkey", array("allowed_values"=>null, "class_attcode"=>"objclass", "sql"=>"objkey", "is_null_allowed"=>false, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeInteger("objkey", array("allowed_values"=>null, "sql"=>"objkey", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||||
|
|
||||||
MetaModel::Init_SetZListItems('details', array('change', 'date', 'userinfo')); // Attributes to be displayed for the complete details
|
MetaModel::Init_SetZListItems('details', array('change', 'date', 'userinfo')); // Attributes to be displayed for the complete details
|
||||||
MetaModel::Init_SetZListItems('list', array('change', 'date', 'userinfo')); // Attributes to be displayed for the complete details
|
MetaModel::Init_SetZListItems('list', array('change', 'date', 'userinfo')); // Attributes to be displayed for the complete details
|
||||||
@@ -619,40 +619,10 @@ class CMDBChangeOpSetAttributeCaseLog extends CMDBChangeOpSetAttribute
|
|||||||
// The attribute was renamed or removed from the object ?
|
// The attribute was renamed or removed from the object ?
|
||||||
$sAttName = $this->Get('attcode');
|
$sAttName = $this->Get('attcode');
|
||||||
}
|
}
|
||||||
$oObj = $oMonoObjectSet->Fetch();
|
$sResult = Dict::Format('Change:AttName_EntryAdded', $sAttName);
|
||||||
$oCaseLog = $oObj->Get($this->Get('attcode'));
|
|
||||||
$iMaxVisibleLength = MetaModel::getConfig()->Get('max_history_case_log_entry_length', 0);
|
|
||||||
$sTextEntry = $oCaseLog->GetEntryAt($this->Get('lastentry'));
|
|
||||||
if (($iMaxVisibleLength > 0) && (strlen($sTextEntry) > $iMaxVisibleLength))
|
|
||||||
{
|
|
||||||
if (function_exists('mb_strcut'))
|
|
||||||
{
|
|
||||||
// Safe with multi-byte strings
|
|
||||||
mb_internal_encoding('UTF-8'); // Do not use the form mb_strcut(str, start, null, encoding) which does not work if PHP < 5.4.9 (null => 0)
|
|
||||||
$sBefore = $this->ToHtml(mb_strcut($sTextEntry, 0, $iMaxVisibleLength));
|
|
||||||
$sAfter = $this->ToHtml(mb_strcut($sTextEntry, $iMaxVisibleLength));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Let's hope we have no multi-byte characters around the cuttting point...
|
|
||||||
$sBefore = $this->ToHtml(substr($sTextEntry, 0, $iMaxVisibleLength));
|
|
||||||
$sAfter = $this->ToHtml(substr($sTextEntry, $iMaxVisibleLength));
|
|
||||||
}
|
|
||||||
$sTextEntry = '<span class="case-log-history-entry">'.$sBefore.'<span class="case-log-history-entry-end">'.$sAfter.'<span class="case-log-history-entry-toggle ui-icon ui-icon-circle-minus"></span></span><span class="case-log-history-entry-more">...<span class="case-log-history-entry-toggle ui-icon ui-icon-circle-plus"></span></span></span>';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sTextEntry = $this->ToHtml($sTextEntry);
|
|
||||||
}
|
|
||||||
$sResult = Dict::Format('Change:AttName_EntryAdded', $sAttName, $sTextEntry);
|
|
||||||
}
|
}
|
||||||
return $sResult;
|
return $sResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function ToHtml($sRawText)
|
|
||||||
{
|
|
||||||
return str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sRawText, ENT_QUOTES, 'UTF-8'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2012 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
/**
|
/**
|
||||||
* Class cmdbObject
|
* Class cmdbObject
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -46,20 +46,18 @@ require_once('stimulus.class.inc.php');
|
|||||||
require_once('valuesetdef.class.inc.php');
|
require_once('valuesetdef.class.inc.php');
|
||||||
require_once('MyHelpers.class.inc.php');
|
require_once('MyHelpers.class.inc.php');
|
||||||
|
|
||||||
require_once('oql/expression.class.inc.php');
|
require_once('expression.class.inc.php');
|
||||||
|
|
||||||
|
require_once('cmdbsource.class.inc.php');
|
||||||
|
require_once('sqlquery.class.inc.php');
|
||||||
require_once('oql/oqlquery.class.inc.php');
|
require_once('oql/oqlquery.class.inc.php');
|
||||||
require_once('oql/oqlexception.class.inc.php');
|
require_once('oql/oqlexception.class.inc.php');
|
||||||
require_once('oql/oql-parser.php');
|
require_once('oql/oql-parser.php');
|
||||||
require_once('oql/oql-lexer.php');
|
require_once('oql/oql-lexer.php');
|
||||||
require_once('oql/oqlinterpreter.class.inc.php');
|
require_once('oql/oqlinterpreter.class.inc.php');
|
||||||
|
|
||||||
require_once('cmdbsource.class.inc.php');
|
|
||||||
require_once('sqlquery.class.inc.php');
|
|
||||||
require_once('sqlobjectquery.class.inc.php');
|
|
||||||
require_once('sqlunionquery.class.inc.php');
|
|
||||||
|
|
||||||
require_once('dbobject.class.php');
|
require_once('dbobject.class.php');
|
||||||
require_once('dbsearch.class.php');
|
require_once('dbobjectsearch.class.php');
|
||||||
require_once('dbobjectset.class.php');
|
require_once('dbobjectset.class.php');
|
||||||
|
|
||||||
require_once('backgroundprocess.inc.php');
|
require_once('backgroundprocess.inc.php');
|
||||||
@@ -524,18 +522,18 @@ abstract class CMDBObject extends DBObject
|
|||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function BulkUpdate(DBSearch $oFilter, array $aValues)
|
public static function BulkUpdate(DBObjectSearch $oFilter, array $aValues)
|
||||||
{
|
{
|
||||||
return $this->BulkUpdateTracked_Internal($oFilter, $aValues);
|
return $this->BulkUpdateTracked_Internal($oFilter, $aValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function BulkUpdateTracked(CMDBChange $oChange, DBSearch $oFilter, array $aValues)
|
public static function BulkUpdateTracked(CMDBChange $oChange, DBObjectSearch $oFilter, array $aValues)
|
||||||
{
|
{
|
||||||
self::SetCurrentChange($oChange);
|
self::SetCurrentChange($oChange);
|
||||||
$this->BulkUpdateTracked_Internal($oFilter, $aValues);
|
$this->BulkUpdateTracked_Internal($oFilter, $aValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function BulkUpdateTracked_Internal(DBSearch $oFilter, array $aValues)
|
protected static function BulkUpdateTracked_Internal(DBObjectSearch $oFilter, array $aValues)
|
||||||
{
|
{
|
||||||
// $aValues is an array of $sAttCode => $value
|
// $aValues is an array of $sAttCode => $value
|
||||||
|
|
||||||
@@ -582,7 +580,7 @@ class CMDBObjectSet extends DBObjectSet
|
|||||||
|
|
||||||
static public function FromScratch($sClass)
|
static public function FromScratch($sClass)
|
||||||
{
|
{
|
||||||
$oFilter = new DBObjectSearch($sClass);
|
$oFilter = new CMDBSearchFilter($sClass);
|
||||||
$oFilter->AddConditionExpression(new FalseExpression());
|
$oFilter->AddConditionExpression(new FalseExpression());
|
||||||
$oRetSet = new self($oFilter);
|
$oRetSet = new self($oFilter);
|
||||||
// NOTE: THIS DOES NOT WORK IF m_bLoaded is private in the base class (and you will not get any error message)
|
// NOTE: THIS DOES NOT WORK IF m_bLoaded is private in the base class (and you will not get any error message)
|
||||||
@@ -606,7 +604,7 @@ class CMDBObjectSet extends DBObjectSet
|
|||||||
// let's create one search definition
|
// let's create one search definition
|
||||||
$sClass = reset($aClasses);
|
$sClass = reset($aClasses);
|
||||||
$sAlias = key($aClasses);
|
$sAlias = key($aClasses);
|
||||||
$oFilter = new DBObjectSearch($sClass, $sAlias);
|
$oFilter = new CMDBSearchFilter($sClass, $sAlias);
|
||||||
|
|
||||||
$oRetSet = new CMDBObjectSet($oFilter);
|
$oRetSet = new CMDBObjectSet($oFilter);
|
||||||
$oRetSet->m_bLoaded = true; // no DB load
|
$oRetSet->m_bLoaded = true; // no DB load
|
||||||
@@ -618,3 +616,16 @@ class CMDBObjectSet extends DBObjectSet
|
|||||||
return $oRetSet;
|
return $oRetSet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: investigate how to get rid of this class that was made to workaround some language limitation... or a poor design!
|
||||||
|
*
|
||||||
|
* @package iTopORM
|
||||||
|
*/
|
||||||
|
class CMDBSearchFilter extends DBObjectSearch
|
||||||
|
{
|
||||||
|
// this is the public interface (?)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2014 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
/**
|
/**
|
||||||
* DB Server abstraction
|
* DB Server abstraction
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -336,7 +336,7 @@ class CMDBSource
|
|||||||
catch(mysqli_sql_exception $e)
|
catch(mysqli_sql_exception $e)
|
||||||
{
|
{
|
||||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||||
}
|
}
|
||||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||||
if ($oResult === false)
|
if ($oResult === false)
|
||||||
@@ -368,7 +368,7 @@ class CMDBSource
|
|||||||
catch(mysqli_sql_exception $e)
|
catch(mysqli_sql_exception $e)
|
||||||
{
|
{
|
||||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||||
}
|
}
|
||||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||||
if ($oResult === false)
|
if ($oResult === false)
|
||||||
@@ -404,7 +404,7 @@ class CMDBSource
|
|||||||
}
|
}
|
||||||
catch(mysqli_sql_exception $e)
|
catch(mysqli_sql_exception $e)
|
||||||
{
|
{
|
||||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||||
}
|
}
|
||||||
if ($oResult === false)
|
if ($oResult === false)
|
||||||
{
|
{
|
||||||
@@ -430,7 +430,7 @@ class CMDBSource
|
|||||||
}
|
}
|
||||||
catch(mysqli_sql_exception $e)
|
catch(mysqli_sql_exception $e)
|
||||||
{
|
{
|
||||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||||
}
|
}
|
||||||
if ($oResult === false)
|
if ($oResult === false)
|
||||||
{
|
{
|
||||||
@@ -541,37 +541,6 @@ class CMDBSource
|
|||||||
return ($aFieldData["Type"]);
|
return ($aFieldData["Type"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function GetFieldSpec($sTable, $sField)
|
|
||||||
{
|
|
||||||
$aTableInfo = self::GetTableInfo($sTable);
|
|
||||||
if (empty($aTableInfo)) return false;
|
|
||||||
if (!array_key_exists($sField, $aTableInfo["Fields"])) return false;
|
|
||||||
$aFieldData = $aTableInfo["Fields"][$sField];
|
|
||||||
$sRet = $aFieldData["Type"];
|
|
||||||
if ($aFieldData["Null"] == 'NO')
|
|
||||||
{
|
|
||||||
$sRet .= ' NOT NULL';
|
|
||||||
}
|
|
||||||
if (is_numeric($aFieldData["Default"]))
|
|
||||||
{
|
|
||||||
if (strtolower(substr($aFieldData["Type"], 0, 5)) == 'enum(')
|
|
||||||
{
|
|
||||||
// Force quotes to match the column declaration statement
|
|
||||||
$sRet .= ' DEFAULT '.self::Quote($aFieldData["Default"], true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$default = $aFieldData["Default"] + 0; // Coerce to a numeric variable
|
|
||||||
$sRet .= ' DEFAULT '.self::Quote($default);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elseif (is_string($aFieldData["Default"]) == 'string')
|
|
||||||
{
|
|
||||||
$sRet .= ' DEFAULT '.self::Quote($aFieldData["Default"]);
|
|
||||||
}
|
|
||||||
return $sRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function HasIndex($sTable, $sIndexId, $aFields = null)
|
public static function HasIndex($sTable, $sIndexId, $aFields = null)
|
||||||
{
|
{
|
||||||
$aTableInfo = self::GetTableInfo($sTable);
|
$aTableInfo = self::GetTableInfo($sTable);
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ class Config
|
|||||||
'default' => '/usr/bin/dot',
|
'default' => '/usr/bin/dot',
|
||||||
'value' => '',
|
'value' => '',
|
||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => true,
|
'show_in_conf_sample' => false,
|
||||||
),
|
),
|
||||||
'php_path' => array(
|
'php_path' => array(
|
||||||
'type' => 'string',
|
'type' => 'string',
|
||||||
@@ -277,14 +277,6 @@ class Config
|
|||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
),
|
),
|
||||||
'log_rest_service' => array(
|
|
||||||
'type' => 'bool',
|
|
||||||
'description' => 'Log the usage of the REST/JSON service',
|
|
||||||
'default' => false,
|
|
||||||
'value' => false,
|
|
||||||
'source_of_value' => '',
|
|
||||||
'show_in_conf_sample' => false,
|
|
||||||
),
|
|
||||||
'synchro_trace' => array(
|
'synchro_trace' => array(
|
||||||
'type' => 'string',
|
'type' => 'string',
|
||||||
'description' => 'Synchronization details: none, display, save (includes \'display\')',
|
'description' => 'Synchronization details: none, display, save (includes \'display\')',
|
||||||
@@ -736,15 +728,6 @@ class Config
|
|||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
),
|
),
|
||||||
'max_history_case_log_entry_length' => array(
|
|
||||||
'type' => 'integer',
|
|
||||||
'description' => 'The length (in number of characters) at which to truncate the (expandable) display (in the history) of a case log entry. If zero, the display in the history is not truncated.',
|
|
||||||
// examples... not used
|
|
||||||
'default' => 60,
|
|
||||||
'value' => 60,
|
|
||||||
'source_of_value' => '',
|
|
||||||
'show_in_conf_sample' => false,
|
|
||||||
),
|
|
||||||
'full_text_chunk_duration' => array(
|
'full_text_chunk_duration' => array(
|
||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
'description' => 'Delay after which the results are displayed.',
|
'description' => 'Delay after which the results are displayed.',
|
||||||
@@ -794,6 +777,14 @@ class Config
|
|||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
),
|
),
|
||||||
|
'xlsx_exporter_cleanup_old_files_delay' => array(
|
||||||
|
'type' => 'int',
|
||||||
|
'description' => 'Delay (in seconds) for which to let the exported XLSX files on the server so that the user who initiated the export can download the result',
|
||||||
|
'default' => 86400,
|
||||||
|
'value' => '',
|
||||||
|
'source_of_value' => '',
|
||||||
|
'show_in_conf_sample' => false,
|
||||||
|
),
|
||||||
'xlsx_exporter_memory_limit' => array(
|
'xlsx_exporter_memory_limit' => array(
|
||||||
'type' => 'string',
|
'type' => 'string',
|
||||||
'description' => 'Memory limit to use when (interactively) exporting data to Excel',
|
'description' => 'Memory limit to use when (interactively) exporting data to Excel',
|
||||||
@@ -818,54 +809,6 @@ class Config
|
|||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
),
|
),
|
||||||
'transaction_storage' => array(
|
|
||||||
'type' => 'string',
|
|
||||||
'description' => 'The type of mechanism to use for storing the unique identifiers for transactions (Session|File).',
|
|
||||||
'default' => 'Session',
|
|
||||||
'value' => '',
|
|
||||||
'source_of_value' => '',
|
|
||||||
'show_in_conf_sample' => false,
|
|
||||||
),
|
|
||||||
'transactions_enabled' => array(
|
|
||||||
'type' => 'bool',
|
|
||||||
'description' => 'Whether or not the whole mechanism to prevent multiple submissions of a page is enabled.',
|
|
||||||
'default' => true,
|
|
||||||
'value' => '',
|
|
||||||
'source_of_value' => '',
|
|
||||||
'show_in_conf_sample' => false,
|
|
||||||
),
|
|
||||||
'log_transactions' => array(
|
|
||||||
'type' => 'bool',
|
|
||||||
'description' => 'Whether or not to enable the debug log for the transactions.',
|
|
||||||
'default' => false,
|
|
||||||
'value' => '',
|
|
||||||
'source_of_value' => '',
|
|
||||||
'show_in_conf_sample' => false,
|
|
||||||
),
|
|
||||||
'concurrent_lock_enabled' => array(
|
|
||||||
'type' => 'bool',
|
|
||||||
'description' => 'Whether or not to activate the locking mechanism in order to prevent concurrent edition of the same object.',
|
|
||||||
'default' => false,
|
|
||||||
'value' => '',
|
|
||||||
'source_of_value' => '',
|
|
||||||
'show_in_conf_sample' => false,
|
|
||||||
),
|
|
||||||
'concurrent_lock_expiration_delay' => array(
|
|
||||||
'type' => 'integer',
|
|
||||||
'description' => 'Delay (in seconds) for a concurrent lock to expire',
|
|
||||||
'default' => 120,
|
|
||||||
'value' => '',
|
|
||||||
'source_of_value' => '',
|
|
||||||
'show_in_conf_sample' => false,
|
|
||||||
),
|
|
||||||
'concurrent_lock_override_profiles' => array(
|
|
||||||
'type' => 'array',
|
|
||||||
'description' => 'The list of profiles allowed to "kill" a lock',
|
|
||||||
'default' => array('Administrator'),
|
|
||||||
'value' => '',
|
|
||||||
'source_of_value' => '',
|
|
||||||
'show_in_conf_sample' => false,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
public function IsProperty($sPropCode)
|
public function IsProperty($sPropCode)
|
||||||
@@ -1006,8 +949,6 @@ class Config
|
|||||||
'core/event.class.inc.php',
|
'core/event.class.inc.php',
|
||||||
'core/action.class.inc.php',
|
'core/action.class.inc.php',
|
||||||
'core/trigger.class.inc.php',
|
'core/trigger.class.inc.php',
|
||||||
'core/bulkexport.class.inc.php',
|
|
||||||
'core/ownershiplock.class.inc.php',
|
|
||||||
'synchro/synchrodatasource.class.inc.php',
|
'synchro/synchrodatasource.class.inc.php',
|
||||||
'core/backgroundtask.class.inc.php',
|
'core/backgroundtask.class.inc.php',
|
||||||
);
|
);
|
||||||
@@ -1047,8 +988,6 @@ class Config
|
|||||||
$this->m_sExtAuthVariable = DEFAULT_EXT_AUTH_VARIABLE;
|
$this->m_sExtAuthVariable = DEFAULT_EXT_AUTH_VARIABLE;
|
||||||
$this->m_sEncryptionKey = DEFAULT_ENCRYPTION_KEY;
|
$this->m_sEncryptionKey = DEFAULT_ENCRYPTION_KEY;
|
||||||
$this->m_aCharsets = array();
|
$this->m_aCharsets = array();
|
||||||
$this->m_bLogQueries = DEFAULT_LOG_QUERIES;
|
|
||||||
$this->m_bQueryCacheEnabled = DEFAULT_QUERY_CACHE_ENABLED;
|
|
||||||
|
|
||||||
$this->m_aModuleSettings = array();
|
$this->m_aModuleSettings = array();
|
||||||
|
|
||||||
@@ -1211,24 +1150,9 @@ class Config
|
|||||||
{
|
{
|
||||||
return $this->m_aModuleSettings[$sModule][$sProperty];
|
return $this->m_aModuleSettings[$sModule][$sProperty];
|
||||||
}
|
}
|
||||||
// Fall back to the predefined XML parameter, if any
|
return $defaultvalue;
|
||||||
return $this->GetModuleParameter($sModule, $sProperty, $defaultvalue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetModuleParameter($sModule, $sProperty, $defaultvalue = null)
|
|
||||||
{
|
|
||||||
$ret = $defaultvalue;
|
|
||||||
if (class_exists('ModulesXMLParameters'))
|
|
||||||
{
|
|
||||||
$aAllParams = ModulesXMLParameters::GetData($sModule);
|
|
||||||
if(array_key_exists($sProperty, $aAllParams))
|
|
||||||
{
|
|
||||||
$ret = $aAllParams[$sProperty];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function SetModuleSetting($sModule, $sProperty, $value)
|
public function SetModuleSetting($sModule, $sProperty, $value)
|
||||||
{
|
{
|
||||||
$this->m_aModuleSettings[$sModule][$sProperty] = $value;
|
$this->m_aModuleSettings[$sModule][$sProperty] = $value;
|
||||||
@@ -1538,8 +1462,6 @@ class Config
|
|||||||
$aSettings['log_notification'] = $this->m_bLogNotification;
|
$aSettings['log_notification'] = $this->m_bLogNotification;
|
||||||
$aSettings['log_issue'] = $this->m_bLogIssue;
|
$aSettings['log_issue'] = $this->m_bLogIssue;
|
||||||
$aSettings['log_web_service'] = $this->m_bLogWebService;
|
$aSettings['log_web_service'] = $this->m_bLogWebService;
|
||||||
$aSettings['log_queries'] = $this->m_bLogQueries;
|
|
||||||
$aSettings['query_cache_enabled'] = $this->m_bQueryCacheEnabled;
|
|
||||||
$aSettings['min_display_limit'] = $this->m_iMinDisplayLimit;
|
$aSettings['min_display_limit'] = $this->m_iMinDisplayLimit;
|
||||||
$aSettings['max_display_limit'] = $this->m_iMaxDisplayLimit;
|
$aSettings['max_display_limit'] = $this->m_iMaxDisplayLimit;
|
||||||
$aSettings['standard_reload_interval'] = $this->m_iStandardReloadInterval;
|
$aSettings['standard_reload_interval'] = $this->m_iStandardReloadInterval;
|
||||||
@@ -1547,7 +1469,6 @@ class Config
|
|||||||
$aSettings['secure_connection_required'] = $this->m_bSecureConnectionRequired;
|
$aSettings['secure_connection_required'] = $this->m_bSecureConnectionRequired;
|
||||||
$aSettings['default_language'] = $this->m_sDefaultLanguage;
|
$aSettings['default_language'] = $this->m_sDefaultLanguage;
|
||||||
$aSettings['allowed_login_types'] = $this->m_sAllowedLoginTypes;
|
$aSettings['allowed_login_types'] = $this->m_sAllowedLoginTypes;
|
||||||
$aSettings['ext_auth_variable'] = $this->m_sExtAuthVariable;
|
|
||||||
$aSettings['encryption_key'] = $this->m_sEncryptionKey;
|
$aSettings['encryption_key'] = $this->m_sEncryptionKey;
|
||||||
$aSettings['csv_import_charsets'] = $this->m_aCharsets;
|
$aSettings['csv_import_charsets'] = $this->m_aCharsets;
|
||||||
|
|
||||||
@@ -1613,8 +1534,6 @@ class Config
|
|||||||
'log_notification' => $this->m_bLogNotification,
|
'log_notification' => $this->m_bLogNotification,
|
||||||
'log_issue' => $this->m_bLogIssue,
|
'log_issue' => $this->m_bLogIssue,
|
||||||
'log_web_service' => $this->m_bLogWebService,
|
'log_web_service' => $this->m_bLogWebService,
|
||||||
'log_queries' => $this->m_bLogQueries,
|
|
||||||
'query_cache_enabled' => $this->m_bQueryCacheEnabled,
|
|
||||||
'secure_connection_required' => $this->m_bSecureConnectionRequired,
|
'secure_connection_required' => $this->m_bSecureConnectionRequired,
|
||||||
);
|
);
|
||||||
foreach($aBoolValues as $sKey => $bValue)
|
foreach($aBoolValues as $sKey => $bValue)
|
||||||
@@ -1653,7 +1572,6 @@ class Config
|
|||||||
'db_collation' => $this->m_sDBCollation,
|
'db_collation' => $this->m_sDBCollation,
|
||||||
'default_language' => $this->m_sDefaultLanguage,
|
'default_language' => $this->m_sDefaultLanguage,
|
||||||
'allowed_login_types' => $this->m_sAllowedLoginTypes,
|
'allowed_login_types' => $this->m_sAllowedLoginTypes,
|
||||||
'ext_auth_variable' => $this->m_sExtAuthVariable,
|
|
||||||
'encryption_key' => $this->m_sEncryptionKey,
|
'encryption_key' => $this->m_sEncryptionKey,
|
||||||
'csv_import_charsets' => $this->m_aCharsets,
|
'csv_import_charsets' => $this->m_aCharsets,
|
||||||
);
|
);
|
||||||
@@ -1791,10 +1709,6 @@ class Config
|
|||||||
{
|
{
|
||||||
$this->Set('app_root_url', $aParamValues['application_path']);
|
$this->Set('app_root_url', $aParamValues['application_path']);
|
||||||
}
|
}
|
||||||
if (isset($aParamValues['graphviz_path']))
|
|
||||||
{
|
|
||||||
$this->Set('graphviz_path', $aParamValues['graphviz_path']);
|
|
||||||
}
|
|
||||||
if (isset($aParamValues['mode']) && isset($aParamValues['language']))
|
if (isset($aParamValues['mode']) && isset($aParamValues['language']))
|
||||||
{
|
{
|
||||||
if (($aParamValues['mode'] == 'install') || $this->GetDefaultLanguage() == '')
|
if (($aParamValues['mode'] == 'install') || $this->GetDefaultLanguage() == '')
|
||||||
@@ -1816,46 +1730,32 @@ class Config
|
|||||||
$this->SetDBName($sDBName);
|
$this->SetDBName($sDBName);
|
||||||
$this->SetDBSubname($aParamValues['db_prefix']);
|
$this->SetDBSubname($aParamValues['db_prefix']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aParamValues['selected_modules']))
|
|
||||||
{
|
|
||||||
$aSelectedModules = explode(',', $aParamValues['selected_modules']);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$aSelectedModules = null;
|
|
||||||
}
|
|
||||||
$this->UpdateIncludes($sModulesDir, $aSelectedModules);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to rebuild the default configuration and the list of includes from a directory and a list of selected modules
|
|
||||||
* @param string $sModulesDir The relative path to the directory to scan for modules (typically the 'env-xxx' directory resulting from the compilation)
|
|
||||||
* @param array $aSelectedModules An array of selected modules' identifiers. If null all modules found will be considered as installed
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function UpdateIncludes($sModulesDir, $aSelectedModules = null)
|
|
||||||
{
|
|
||||||
if (!is_null($sModulesDir))
|
if (!is_null($sModulesDir))
|
||||||
{
|
{
|
||||||
|
if (isset($aParamValues['selected_modules']))
|
||||||
|
{
|
||||||
|
$aSelectedModules = explode(',', $aParamValues['selected_modules']);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$aSelectedModules = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize the arrays below with default values for the application...
|
// Initialize the arrays below with default values for the application...
|
||||||
$oEmptyConfig = new Config('dummy_file', false); // Do NOT load any config file, just set the default values
|
$oEmptyConfig = new Config('dummy_file', false); // Do NOT load any config file, just set the default values
|
||||||
$aAddOns = $oEmptyConfig->GetAddOns();
|
$aAddOns = $oEmptyConfig->GetAddOns();
|
||||||
$aAppModules = $oEmptyConfig->GetAppModules();
|
$aAppModules = $oEmptyConfig->GetAppModules();
|
||||||
if (file_exists(APPROOT.$sModulesDir.'/core/main.php'))
|
|
||||||
{
|
|
||||||
$aAppModules[] = $sModulesDir.'/core/main.php';
|
|
||||||
}
|
|
||||||
$aDataModels = $oEmptyConfig->GetDataModels();
|
$aDataModels = $oEmptyConfig->GetDataModels();
|
||||||
$aWebServiceCategories = $oEmptyConfig->GetWebServiceCategories();
|
$aWebServiceCategories = $oEmptyConfig->GetWebServiceCategories();
|
||||||
$aDictionaries = $oEmptyConfig->GetDictionaries();
|
$aDictionaries = $oEmptyConfig->GetDictionaries();
|
||||||
// Merge the values with the ones provided by the modules
|
// Merge the values with the ones provided by the modules
|
||||||
// Make sure when don't load the same file twice...
|
// Make sure when don't load the same file twice...
|
||||||
|
|
||||||
$aModules = ModuleDiscovery::GetAvailableModules(array(APPROOT.$sModulesDir));
|
$aModules = ModuleDiscovery::GetAvailableModules(array(APPROOT.$sModulesDir));
|
||||||
foreach ($aModules as $sModuleId => $aModuleInfo)
|
foreach($aModules as $sModuleId => $aModuleInfo)
|
||||||
{
|
{
|
||||||
list ($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
|
list($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
|
||||||
if (is_null($aSelectedModules) || in_array($sModuleName, $aSelectedModules))
|
if (is_null($aSelectedModules) || in_array($sModuleName, $aSelectedModules))
|
||||||
{
|
{
|
||||||
if (isset($aModuleInfo['datamodel']))
|
if (isset($aModuleInfo['datamodel']))
|
||||||
@@ -1868,10 +1768,10 @@ class Config
|
|||||||
}
|
}
|
||||||
if (isset($aModuleInfo['settings']))
|
if (isset($aModuleInfo['settings']))
|
||||||
{
|
{
|
||||||
list ($sName, $sVersion) = ModuleDiscovery::GetModuleName($sModuleId);
|
list($sName, $sVersion) = ModuleDiscovery::GetModuleName($sModuleId);
|
||||||
foreach ($aModuleInfo['settings'] as $sProperty => $value)
|
foreach($aModuleInfo['settings'] as $sProperty => $value)
|
||||||
{
|
{
|
||||||
if (isset($this->m_aModuleSettings[$sName][$sProperty]))
|
if ($bPreserveModuleSettings && isset($this->m_aModuleSettings[$sName][$sProperty]))
|
||||||
{
|
{
|
||||||
// Do nothing keep the original value
|
// Do nothing keep the original value
|
||||||
}
|
}
|
||||||
@@ -1892,7 +1792,7 @@ class Config
|
|||||||
{
|
{
|
||||||
throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not derived from 'ModuleInstallerAPI' - Module: ".$aModuleInfo['label']);
|
throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not derived from 'ModuleInstallerAPI' - Module: ".$aModuleInfo['label']);
|
||||||
}
|
}
|
||||||
$aCallSpec = array($sModuleInstallerClass,'BeforeWritingConfig');
|
$aCallSpec = array($sModuleInstallerClass, 'BeforeWritingConfig');
|
||||||
call_user_func_array($aCallSpec, array($this));
|
call_user_func_array($aCallSpec, array($this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1901,13 +1801,16 @@ class Config
|
|||||||
$this->SetAppModules($aAppModules);
|
$this->SetAppModules($aAppModules);
|
||||||
$this->SetDataModels($aDataModels);
|
$this->SetDataModels($aDataModels);
|
||||||
$this->SetWebServiceCategories($aWebServiceCategories);
|
$this->SetWebServiceCategories($aWebServiceCategories);
|
||||||
|
|
||||||
// Scan dictionaries
|
// Scan dictionaries
|
||||||
//
|
//
|
||||||
foreach (glob(APPROOT.$sModulesDir.'/dictionaries/*.dict.php') as $sFilePath)
|
if (!is_null($sModulesDir))
|
||||||
{
|
{
|
||||||
$sFile = basename($sFilePath);
|
foreach(glob(APPROOT.$sModulesDir.'/dictionaries/*.dict.php') as $sFilePath)
|
||||||
$aDictionaries[] = $sModulesDir.'/dictionaries/'.$sFile;
|
{
|
||||||
|
$sFile = basename($sFilePath);
|
||||||
|
$aDictionaries[] = $sModulesDir.'/dictionaries/'.$sFile;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$this->SetDictionaries($aDictionaries);
|
$this->SetDictionaries($aDictionaries);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,310 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Copyright (C) 2015 Combodo SARL
|
|
||||||
//
|
|
||||||
// This file is part of iTop.
|
|
||||||
//
|
|
||||||
// iTop is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// iTop is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bulk export: CSV export
|
|
||||||
*
|
|
||||||
* @copyright Copyright (C) 2015 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
class CSVBulkExport extends TabularBulkExport
|
|
||||||
{
|
|
||||||
public function DisplayUsage(Page $oP)
|
|
||||||
{
|
|
||||||
$oP->p(" * csv format options:");
|
|
||||||
$oP->p(" *\tfields: (mandatory) the comma separated list of field codes to export (e.g: name,org_id,service_name...).");
|
|
||||||
$oP->p(" *\tseparator: (optional) character to be used as the separator (default is ',').");
|
|
||||||
$oP->p(" *\tcharset: (optional) character set for encoding the result (default is 'UTF-8').");
|
|
||||||
$oP->p(" *\ttext-qualifier: (optional) character to be used around text strings (default is '\"').");
|
|
||||||
$oP->p(" *\tno_localize: set to 1 to retrieve non-localized values (for instance for ENUM values). Default is 0 (= localized values)");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function ReadParameters()
|
|
||||||
{
|
|
||||||
parent::ReadParameters();
|
|
||||||
$this->aStatusInfo['separator'] = utils::ReadParam('separator', ',', true, 'raw_data');
|
|
||||||
if (strtolower($this->aStatusInfo['separator']) == 'tab')
|
|
||||||
{
|
|
||||||
$this->aStatusInfo['separator'] = "\t";
|
|
||||||
}
|
|
||||||
else if (strtolower($this->aStatusInfo['separator']) == 'other')
|
|
||||||
{
|
|
||||||
$this->aStatusInfo['separator'] = utils::ReadParam('other-separator', ',', true, 'raw_data');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->aStatusInfo['text_qualifier'] = utils::ReadParam('text-qualifier', '"', true, 'raw_data');
|
|
||||||
if (strtolower($this->aStatusInfo['text_qualifier']) == 'other')
|
|
||||||
{
|
|
||||||
$this->aStatusInfo['text_qualifier'] = utils::ReadParam('other-text-qualifier', '"', true, 'raw_data');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->aStatusInfo['charset'] = strtoupper(utils::ReadParam('charset', 'UTF-8', true, 'raw_data'));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected function SuggestField($sClass, $sAttCode)
|
|
||||||
{
|
|
||||||
switch($sAttCode)
|
|
||||||
{
|
|
||||||
case 'id': // replace 'id' by 'friendlyname'
|
|
||||||
$sAttCode = 'friendlyname';
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
|
||||||
if ($oAttDef instanceof AttributeExternalKey)
|
|
||||||
{
|
|
||||||
$sAttCode .= '_friendlyname';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::SuggestField($sClass, $sAttCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function EnumFormParts()
|
|
||||||
{
|
|
||||||
return array_merge(parent::EnumFormParts(), array('csv_options' => array('separator', 'charset', 'text-qualifier', 'no_localize') ,'interactive_fields_csv' => array('interactive_fields_csv')));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function DisplayFormPart(WebPage $oP, $sPartId)
|
|
||||||
{
|
|
||||||
switch($sPartId)
|
|
||||||
{
|
|
||||||
case 'interactive_fields_csv':
|
|
||||||
$this->GetInteractiveFieldsWidget($oP, 'interactive_fields_csv');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'csv_options':
|
|
||||||
$oP->add('<fieldset><legend>'.Dict::S('Core:BulkExport:CSVOptions').'</legend>');
|
|
||||||
$oP->add('<table class="export_parameters"><tr><td style="vertical-align:top">');
|
|
||||||
$oP->add('<h3>'.Dict::S('UI:CSVImport:SeparatorCharacter').'</h3>');
|
|
||||||
$sRawSeparator = utils::ReadParam('separator', ',', true, 'raw_data');
|
|
||||||
$aSep = array(
|
|
||||||
';' => Dict::S('UI:CSVImport:SeparatorSemicolon+'),
|
|
||||||
',' => Dict::S('UI:CSVImport:SeparatorComma+'),
|
|
||||||
'tab' => Dict::S('UI:CSVImport:SeparatorTab+'),
|
|
||||||
);
|
|
||||||
$sOtherSeparator = '';
|
|
||||||
if (!array_key_exists($sRawSeparator, $aSep))
|
|
||||||
{
|
|
||||||
$sOtherSeparator = $sRawSeparator;
|
|
||||||
$sRawSeparator = 'other';
|
|
||||||
}
|
|
||||||
$aSep['other'] = Dict::S('UI:CSVImport:SeparatorOther').' <input type="text" size="3" name="other-separator" value="'.htmlentities($sOtherSeparator, ENT_QUOTES, 'UTF-8').'"/>';
|
|
||||||
|
|
||||||
foreach($aSep as $sVal => $sLabel)
|
|
||||||
{
|
|
||||||
$sChecked = ($sVal == $sRawSeparator) ? 'checked' : '';
|
|
||||||
$oP->add('<input type="radio" name="separator" value="'.htmlentities($sVal, ENT_QUOTES, 'UTF-8').'" '.$sChecked.'/> '.$sLabel.'<br/>');
|
|
||||||
}
|
|
||||||
|
|
||||||
$oP->add('</td><td style="vertical-align:top">');
|
|
||||||
|
|
||||||
$oP->add('<h3>'.Dict::S('UI:CSVImport:TextQualifierCharacter').'</h3>');
|
|
||||||
|
|
||||||
$sRawQualifier = utils::ReadParam('text-qualifier', '"', true, 'raw_data');
|
|
||||||
$aQualifiers = array(
|
|
||||||
'"' => Dict::S('UI:CSVImport:QualifierDoubleQuote+'),
|
|
||||||
'\'' => Dict::S('UI:CSVImport:QualifierSimpleQuote+'),
|
|
||||||
);
|
|
||||||
$sOtherQualifier = '';
|
|
||||||
if (!array_key_exists($sRawQualifier, $aQualifiers))
|
|
||||||
{
|
|
||||||
$sOtherQualifier = $sRawQualifier;
|
|
||||||
$sRawQualifier = 'other';
|
|
||||||
}
|
|
||||||
$aQualifiers['other'] = Dict::S('UI:CSVImport:QualifierOther').' <input type="text" size="3" name="other-text-qualifier" value="'.htmlentities($sOtherQualifier, ENT_QUOTES, 'UTF-8').'"/>';
|
|
||||||
|
|
||||||
foreach($aQualifiers as $sVal => $sLabel)
|
|
||||||
{
|
|
||||||
$sChecked = ($sVal == $sRawQualifier) ? 'checked' : '';
|
|
||||||
$oP->add('<input type="radio" name="text-qualifier" value="'.htmlentities($sVal, ENT_QUOTES, 'UTF-8').'" '.$sChecked.'/> '.$sLabel.'<br/>');
|
|
||||||
}
|
|
||||||
|
|
||||||
$sChecked = (utils::ReadParam('no_localize', 0) == 1) ? ' checked ' : '';
|
|
||||||
$oP->add('</td><td style="vertical-align:top">');
|
|
||||||
$oP->add('<h3>'.Dict::S('Core:BulkExport:CSVLocalization').'</h3>');
|
|
||||||
$oP->add('<input type="checkbox" id="csv_no_localize" name="no_localize" value="1"'.$sChecked.'><label for="csv_no_localize"> '.Dict::S('Core:BulkExport:OptionNoLocalize').'</label>');
|
|
||||||
$oP->add('<br/>');
|
|
||||||
$oP->add('<br/>');
|
|
||||||
$oP->add(Dict::S('UI:CSVImport:Encoding').': <select name="charset" style="font-family:Arial,Helvetica,Sans-serif">'); // IE 8 has some troubles if the font is different
|
|
||||||
$aPossibleEncodings = utils::GetPossibleEncodings(MetaModel::GetConfig()->GetCSVImportCharsets());
|
|
||||||
$sDefaultEncoding = MetaModel::GetConfig()->Get('csv_file_default_charset');
|
|
||||||
foreach($aPossibleEncodings as $sIconvCode => $sDisplayName )
|
|
||||||
{
|
|
||||||
$sSelected = '';
|
|
||||||
if ($sIconvCode == $sDefaultEncoding)
|
|
||||||
{
|
|
||||||
$sSelected = ' selected';
|
|
||||||
}
|
|
||||||
$oP->add('<option value="'.$sIconvCode.'"'.$sSelected.'>'.$sDisplayName.'</option>');
|
|
||||||
}
|
|
||||||
$oP->add('</select>');
|
|
||||||
|
|
||||||
$oP->add('</td></tr></table>');
|
|
||||||
|
|
||||||
$oP->add('</fieldset>');
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
|
||||||
default:
|
|
||||||
return parent:: DisplayFormPart($oP, $sPartId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function GetSampleData($oObj, $sAttCode)
|
|
||||||
{
|
|
||||||
return '<div class="text-preview">'.htmlentities($this->GetValue($oObj, $sAttCode), ENT_QUOTES, 'UTF-8').'</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function GetValue($oObj, $sAttCode)
|
|
||||||
{
|
|
||||||
switch($sAttCode)
|
|
||||||
{
|
|
||||||
case 'id':
|
|
||||||
$sRet = $oObj->GetKey();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$sRet = trim($oObj->GetAsCSV($sAttCode), '"');
|
|
||||||
}
|
|
||||||
return $sRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetHeader()
|
|
||||||
{
|
|
||||||
$oSet = new DBObjectSet($this->oSearch);
|
|
||||||
$this->aStatusInfo['status'] = 'running';
|
|
||||||
$this->aStatusInfo['position'] = 0;
|
|
||||||
$this->aStatusInfo['total'] = $oSet->Count();
|
|
||||||
|
|
||||||
$aData = array();
|
|
||||||
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
|
|
||||||
{
|
|
||||||
$aData[] = $aFieldSpec['sColLabel'];
|
|
||||||
}
|
|
||||||
$sFrom = array("\r\n", $this->aStatusInfo['text_qualifier']);
|
|
||||||
$sTo = array("\n", $this->aStatusInfo['text_qualifier'].$this->aStatusInfo['text_qualifier']);
|
|
||||||
foreach($aData as $idx => $sData)
|
|
||||||
{
|
|
||||||
// Escape and encode (if needed) the headers
|
|
||||||
$sEscaped = str_replace($sFrom, $sTo, (string)$sData);
|
|
||||||
$aData[$idx] = $this->aStatusInfo['text_qualifier'].$sEscaped.$this->aStatusInfo['text_qualifier'];
|
|
||||||
if ($this->aStatusInfo['charset'] != 'UTF-8')
|
|
||||||
{
|
|
||||||
// Note: due to bugs in the glibc library it's safer to call iconv on the smallest possible string
|
|
||||||
// and thus to convert field by field and not the whole row or file at once (see ticket #991)
|
|
||||||
$aData[$idx] = iconv('UTF-8', $this->aStatusInfo['charset'].'//IGNORE//TRANSLIT', $aData[$idx]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$sData = implode($this->aStatusInfo['separator'], $aData)."\n";
|
|
||||||
|
|
||||||
return $sData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetNextChunk(&$aStatus)
|
|
||||||
{
|
|
||||||
$sRetCode = 'run';
|
|
||||||
$iPercentage = 0;
|
|
||||||
|
|
||||||
$oSet = new DBObjectSet($this->oSearch);
|
|
||||||
$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
|
|
||||||
$this->OptimizeColumnLoad($oSet);
|
|
||||||
|
|
||||||
$iCount = 0;
|
|
||||||
$sData = '';
|
|
||||||
$iPreviousTimeLimit = ini_get('max_execution_time');
|
|
||||||
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
|
|
||||||
while($aRow = $oSet->FetchAssoc())
|
|
||||||
{
|
|
||||||
set_time_limit($iLoopTimeLimit);
|
|
||||||
$aData = array();
|
|
||||||
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
|
|
||||||
{
|
|
||||||
$sAlias = $aFieldSpec['sAlias'];
|
|
||||||
$sAttCode = $aFieldSpec['sAttCode'];
|
|
||||||
|
|
||||||
$sField = '';
|
|
||||||
$oObj = $aRow[$sAlias];
|
|
||||||
if ($oObj != null)
|
|
||||||
{
|
|
||||||
switch($sAttCode)
|
|
||||||
{
|
|
||||||
case 'id':
|
|
||||||
$sField = $oObj->GetKey();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$sField = $oObj->GetAsCSV($sAttCode, $this->aStatusInfo['separator'], $this->aStatusInfo['text_qualifier'], $this->bLocalizeOutput);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($this->aStatusInfo['charset'] != 'UTF-8')
|
|
||||||
{
|
|
||||||
// Note: due to bugs in the glibc library it's safer to call iconv on the smallest possible string
|
|
||||||
// and thus to convert field by field and not the whole row or file at once (see ticket #991)
|
|
||||||
$aData[] = iconv('UTF-8', $this->aStatusInfo['charset'].'//IGNORE//TRANSLIT', $sField);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$aData[] = $sField;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$sData .= implode($this->aStatusInfo['separator'], $aData)."\n";
|
|
||||||
$iCount++;
|
|
||||||
}
|
|
||||||
set_time_limit($iPreviousTimeLimit);
|
|
||||||
$this->aStatusInfo['position'] += $this->iChunkSize;
|
|
||||||
if ($this->aStatusInfo['total'] == 0)
|
|
||||||
{
|
|
||||||
$iPercentage = 100;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$iPercentage = floor(min(100.0, 100.0*$this->aStatusInfo['position']/$this->aStatusInfo['total']));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($iCount < $this->iChunkSize)
|
|
||||||
{
|
|
||||||
$sRetCode = 'done';
|
|
||||||
}
|
|
||||||
|
|
||||||
$aStatus = array('code' => $sRetCode, 'message' => Dict::S('Core:BulkExport:RetrievingData'), 'percentage' => $iPercentage);
|
|
||||||
return $sData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetSupportedFormats()
|
|
||||||
{
|
|
||||||
return array('csv' => Dict::S('Core:BulkExport:CSVFormat'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetMimeType()
|
|
||||||
{
|
|
||||||
return 'text/csv';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetFileExtension()
|
|
||||||
{
|
|
||||||
return 'csv';
|
|
||||||
}
|
|
||||||
public function GetCharacterSet()
|
|
||||||
{
|
|
||||||
return $this->aStatusInfo['charset'];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2012 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
* data generator
|
* data generator
|
||||||
* helps the consultants in creating dummy data sets, for various test purposes (validation, usability, scalability)
|
* helps the consultants in creating dummy data sets, for various test purposes (validation, usability, scalability)
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -267,7 +267,7 @@ class cmdbDataGenerator
|
|||||||
function GenerateKey($sClass, $aFilterCriteria)
|
function GenerateKey($sClass, $aFilterCriteria)
|
||||||
{
|
{
|
||||||
$retKey = null;
|
$retKey = null;
|
||||||
$oFilter = new DBObjectSearch($sClass);
|
$oFilter = new CMDBSearchFilter($sClass);
|
||||||
foreach($aFilterCriteria as $sFilterCode => $filterValue)
|
foreach($aFilterCriteria as $sFilterCode => $filterValue)
|
||||||
{
|
{
|
||||||
$oFilter->AddCondition($sFilterCode, $filterValue, '=');
|
$oFilter->AddCondition($sFilterCode, $filterValue, '=');
|
||||||
@@ -346,7 +346,7 @@ class cmdbDataGenerator
|
|||||||
*/
|
*/
|
||||||
protected function OrganizationExists($sCode)
|
protected function OrganizationExists($sCode)
|
||||||
{
|
{
|
||||||
$oFilter = new DBObjectSearch('Organization');
|
$oFilter = new CMDBSearchFilter('bizOrganization');
|
||||||
$oFilter->AddCondition('code', $sCode, '=');
|
$oFilter->AddCondition('code', $sCode, '=');
|
||||||
$oSet = new CMDBObjectSet($oFilter);
|
$oSet = new CMDBObjectSet($oFilter);
|
||||||
return ($oSet->Count() > 0);
|
return ($oSet->Count() > 0);
|
||||||
@@ -361,7 +361,7 @@ class cmdbDataGenerator
|
|||||||
protected function GetOrganization($sId)
|
protected function GetOrganization($sId)
|
||||||
{
|
{
|
||||||
$oOrg = null;
|
$oOrg = null;
|
||||||
$oFilter = new DBObjectSearch('Organization');
|
$oFilter = new CMDBSearchFilter('bizOrganization');
|
||||||
$oFilter->AddCondition('id', $sId, '=');
|
$oFilter->AddCondition('id', $sId, '=');
|
||||||
$oSet = new CMDBObjectSet($oFilter);
|
$oSet = new CMDBObjectSet($oFilter);
|
||||||
if ($oSet->Count() > 0)
|
if ($oSet->Count() > 0)
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<itop_design>
|
|
||||||
</itop_design>
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2014 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -52,7 +52,7 @@ interface iDisplay
|
|||||||
/**
|
/**
|
||||||
* Class dbObject: the root of persistent classes
|
* Class dbObject: the root of persistent classes
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -89,10 +89,11 @@ abstract class DBObject implements iDisplay
|
|||||||
protected $m_aCheckIssues = null;
|
protected $m_aCheckIssues = null;
|
||||||
protected $m_aDeleteIssues = null;
|
protected $m_aDeleteIssues = null;
|
||||||
|
|
||||||
|
protected $m_aAsArgs = null; // The current object as a standard argument (cache)
|
||||||
|
|
||||||
private $m_bFullyLoaded = false; // Compound objects can be partially loaded
|
private $m_bFullyLoaded = false; // Compound objects can be partially loaded
|
||||||
private $m_aLoadedAtt = array(); // Compound objects can be partially loaded, array of sAttCode
|
private $m_aLoadedAtt = array(); // Compound objects can be partially loaded, array of sAttCode
|
||||||
protected $m_aTouchedAtt = array(); // list of (potentially) modified sAttCodes
|
protected $m_aModifiedAtt = array(); // list of (potentially) modified sAttCodes
|
||||||
protected $m_aModifiedAtt = array(); // real modification status: for each attCode can be: unset => don't know, true => modified, false => not modified (the same value as the original value was set)
|
|
||||||
protected $m_aSynchroData = null; // Set of Synch data related to this object
|
protected $m_aSynchroData = null; // Set of Synch data related to this object
|
||||||
protected $m_sHighlightCode = null;
|
protected $m_sHighlightCode = null;
|
||||||
protected $m_aCallbacks = array();
|
protected $m_aCallbacks = array();
|
||||||
@@ -104,7 +105,6 @@ abstract class DBObject implements iDisplay
|
|||||||
{
|
{
|
||||||
$this->FromRow($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec);
|
$this->FromRow($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec);
|
||||||
$this->m_bFullyLoaded = $this->IsFullyLoaded();
|
$this->m_bFullyLoaded = $this->IsFullyLoaded();
|
||||||
$this->m_aTouchedAtt = array();
|
|
||||||
$this->m_aModifiedAtt = array();
|
$this->m_aModifiedAtt = array();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -230,7 +230,6 @@ abstract class DBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->m_bFullyLoaded = true;
|
$this->m_bFullyLoaded = true;
|
||||||
$this->m_aTouchedAtt = array();
|
|
||||||
$this->m_aModifiedAtt = array();
|
$this->m_aModifiedAtt = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,11 +409,11 @@ abstract class DBObject implements iDisplay
|
|||||||
$realvalue = $oAttDef->MakeRealValue($value, $this);
|
$realvalue = $oAttDef->MakeRealValue($value, $this);
|
||||||
|
|
||||||
$this->m_aCurrValues[$sAttCode] = $realvalue;
|
$this->m_aCurrValues[$sAttCode] = $realvalue;
|
||||||
$this->m_aTouchedAtt[$sAttCode] = true;
|
$this->m_aModifiedAtt[$sAttCode] = true;
|
||||||
unset($this->m_aModifiedAtt[$sAttCode]);
|
|
||||||
|
|
||||||
// The object has changed, reset caches
|
// The object has changed, reset caches
|
||||||
$this->m_bCheckStatus = null;
|
$this->m_bCheckStatus = null;
|
||||||
|
$this->m_aAsArgs = null;
|
||||||
|
|
||||||
// Make sure we do not reload it anymore... before saving it
|
// Make sure we do not reload it anymore... before saving it
|
||||||
$this->RegisterAsDirty();
|
$this->RegisterAsDirty();
|
||||||
@@ -423,17 +422,6 @@ abstract class DBObject implements iDisplay
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SetTrim($sAttCode, $sValue)
|
|
||||||
{
|
|
||||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
|
||||||
$iMaxSize = $oAttDef->GetMaxSize();
|
|
||||||
if ($iMaxSize && (strlen($sValue) > $iMaxSize))
|
|
||||||
{
|
|
||||||
$sValue = substr($sValue, 0, $iMaxSize);
|
|
||||||
}
|
|
||||||
$this->Set($sAttCode, $sValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetLabel($sAttCode)
|
public function GetLabel($sAttCode)
|
||||||
{
|
{
|
||||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||||
@@ -1255,29 +1243,18 @@ abstract class DBObject implements iDisplay
|
|||||||
// The value was not set
|
// The value was not set
|
||||||
$aDelta[$sAtt] = $proposedValue;
|
$aDelta[$sAtt] = $proposedValue;
|
||||||
}
|
}
|
||||||
elseif(!array_key_exists($sAtt, $this->m_aTouchedAtt) || (array_key_exists($sAtt, $this->m_aModifiedAtt) && $this->m_aModifiedAtt[$sAtt] == false))
|
elseif(!array_key_exists($sAtt, $this->m_aModifiedAtt))
|
||||||
{
|
{
|
||||||
// This attCode was never set, cannot be modified
|
// This attCode was never set, canno tbe modified
|
||||||
// or the same value - as the original value - was set, and has been verified as equivalent to the original value
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (array_key_exists($sAtt, $this->m_aModifiedAtt) && $this->m_aModifiedAtt[$sAtt] == true)
|
|
||||||
{
|
|
||||||
// We already know that the value is really modified
|
|
||||||
$aDelta[$sAtt] = $proposedValue;
|
|
||||||
}
|
|
||||||
elseif(is_object($proposedValue))
|
elseif(is_object($proposedValue))
|
||||||
{
|
{
|
||||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAtt);
|
$oLinkAttDef = MetaModel::GetAttributeDef(get_class($this), $sAtt);
|
||||||
// The value is an object, the comparison is not strict
|
// The value is an object, the comparison is not strict
|
||||||
if (!$oAttDef->Equals($this->m_aOrigValues[$sAtt], $proposedValue))
|
if (!$oLinkAttDef->Equals($proposedValue, $this->m_aOrigValues[$sAtt]))
|
||||||
{
|
{
|
||||||
$aDelta[$sAtt] = $proposedValue;
|
$aDelta[$sAtt] = $proposedValue;
|
||||||
$this->m_aModifiedAtt[$sAtt] = true; // Really modified
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->m_aModifiedAtt[$sAtt] = false; // Not really modified
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1290,11 +1267,6 @@ abstract class DBObject implements iDisplay
|
|||||||
//var_dump($proposedValue);
|
//var_dump($proposedValue);
|
||||||
//echo "</pre>\n";
|
//echo "</pre>\n";
|
||||||
$aDelta[$sAtt] = $proposedValue;
|
$aDelta[$sAtt] = $proposedValue;
|
||||||
$this->m_aModifiedAtt[$sAtt] = true; // Really modified
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->m_aModifiedAtt[$sAtt] = false; // Not really modified
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1363,35 +1335,49 @@ abstract class DBObject implements iDisplay
|
|||||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||||
{
|
{
|
||||||
if (!$oAttDef->IsLinkSet()) continue;
|
if (!$oAttDef->IsLinkSet()) continue;
|
||||||
if (!array_key_exists($sAttCode, $this->m_aTouchedAtt)) continue;
|
|
||||||
if (array_key_exists($sAttCode, $this->m_aModifiedAtt) && ($this->m_aModifiedAtt[$sAttCode] == false)) continue;
|
$oOriginalSet = $this->m_aOrigValues[$sAttCode];
|
||||||
|
if ($oOriginalSet != null)
|
||||||
|
{
|
||||||
|
$aOriginalList = $oOriginalSet->ToArray();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$aOriginalList = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$oLinks = $this->Get($sAttCode);
|
||||||
|
$oLinks->Rewind();
|
||||||
|
while ($oLinkedObject = $oLinks->Fetch())
|
||||||
|
{
|
||||||
|
if (!array_key_exists($oLinkedObject->GetKey(), $aOriginalList))
|
||||||
|
{
|
||||||
|
// New object added to the set, make it point properly
|
||||||
|
$oLinkedObject->Set($oAttDef->GetExtKeyToMe(), $this->m_iKey);
|
||||||
|
}
|
||||||
|
if ($oLinkedObject->IsModified())
|
||||||
|
{
|
||||||
|
// Objects can be modified because:
|
||||||
|
// 1) They've just been added into the set, so their ExtKey is modified
|
||||||
|
// 2) They are about to be removed from the set BUT NOT deleted, their ExtKey has been reset
|
||||||
|
$oLinkedObject->DBWrite();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the objects that were initialy present and disappeared from the list
|
||||||
|
// (if any)
|
||||||
|
if (count($aOriginalList) > 0)
|
||||||
|
{
|
||||||
|
$aNewSet = $oLinks->ToArray();
|
||||||
|
|
||||||
// Note: any change to this algorithm must be reproduced into the implementation of AttributeLinkSet::Equals()
|
foreach($aOriginalList as $iId => $oObject)
|
||||||
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
|
{
|
||||||
$sAdditionalKey = null;
|
if (!array_key_exists($iId, $aNewSet))
|
||||||
if ($oAttDef->IsIndirect() && !$oAttDef->DuplicatesAllowed())
|
{
|
||||||
{
|
// It disappeared from the list
|
||||||
$sAdditionalKey = $oAttDef->GetExtKeyToRemote();
|
$oObject->DBDelete();
|
||||||
}
|
}
|
||||||
$oComparator = new DBObjectSetComparator($this->m_aOrigValues[$sAttCode], $this->Get($sAttCode), array($sExtKeyToMe), $sAdditionalKey);
|
}
|
||||||
$aChanges = $oComparator->GetDifferences();
|
|
||||||
|
|
||||||
foreach($aChanges['added'] as $oLink)
|
|
||||||
{
|
|
||||||
// Make sure that the objects in the set point to "this"
|
|
||||||
$oLink->Set($oAttDef->GetExtKeyToMe(), $this->m_iKey);
|
|
||||||
$id = $oLink->DBWrite();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($aChanges['modified'] as $oLink)
|
|
||||||
{
|
|
||||||
// Objects in the set either remain attached or have been detached -> leave the link as is
|
|
||||||
$oLink->DBWrite();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($aChanges['removed'] as $oLink)
|
|
||||||
{
|
|
||||||
$oLink->DBDelete();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1584,6 +1570,9 @@ abstract class DBObject implements iDisplay
|
|||||||
$this->DBWriteLinks();
|
$this->DBWriteLinks();
|
||||||
$this->m_bIsInDB = true;
|
$this->m_bIsInDB = true;
|
||||||
$this->m_bDirty = false;
|
$this->m_bDirty = false;
|
||||||
|
|
||||||
|
// Arg cache invalidated (in particular, it needs the object key -could be improved later)
|
||||||
|
$this->m_aAsArgs = null;
|
||||||
|
|
||||||
$this->AfterInsert();
|
$this->AfterInsert();
|
||||||
|
|
||||||
@@ -1863,7 +1852,7 @@ abstract class DBObject implements iDisplay
|
|||||||
$oFilter = new DBObjectSearch(get_class($this));
|
$oFilter = new DBObjectSearch(get_class($this));
|
||||||
$oFilter->AddCondition('id', $this->m_iKey, '=');
|
$oFilter->AddCondition('id', $this->m_iKey, '=');
|
||||||
|
|
||||||
$sSQL = $oFilter->MakeUpdateQuery($aChanges);
|
$sSQL = MetaModel::MakeUpdateQuery($oFilter, $aChanges);
|
||||||
CMDBSource::Query($sSQL);
|
CMDBSource::Query($sSQL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2099,12 +2088,12 @@ abstract class DBObject implements iDisplay
|
|||||||
{
|
{
|
||||||
if (is_string($actionHandler))
|
if (is_string($actionHandler))
|
||||||
{
|
{
|
||||||
// Old (pre-2.1.0 modules) action definition without any parameter
|
// Old (pre-2.1.0) action definition without any parameter
|
||||||
$aActionCallSpec = array($this, $actionHandler);
|
$aActionCallSpec = array($this, $sActionHandler);
|
||||||
|
|
||||||
if (!is_callable($aActionCallSpec))
|
if (!is_callable($aActionCallSpec))
|
||||||
{
|
{
|
||||||
throw new CoreException("Unable to call action: ".get_class($this)."::$actionHandler");
|
throw new CoreException("Unable to call action: ".get_class($this)."::$sActionHandler");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$bRet = call_user_func($aActionCallSpec, $sStimulusCode);
|
$bRet = call_user_func($aActionCallSpec, $sStimulusCode);
|
||||||
@@ -2205,8 +2194,7 @@ abstract class DBObject implements iDisplay
|
|||||||
$oSW = $this->Get($sAttCode);
|
$oSW = $this->Get($sAttCode);
|
||||||
$oSW->Reset($this, $oAttDef);
|
$oSW->Reset($this, $oAttDef);
|
||||||
$this->Set($sAttCode, $oSW);
|
$this->Set($sAttCode, $oSW);
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lifecycle action: Recover the default value (aka when an object is being created)
|
* Lifecycle action: Recover the default value (aka when an object is being created)
|
||||||
@@ -2241,56 +2229,7 @@ abstract class DBObject implements iDisplay
|
|||||||
*/
|
*/
|
||||||
public function SetCurrentUser($sAttCode)
|
public function SetCurrentUser($sAttCode)
|
||||||
{
|
{
|
||||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
$this->Set($sAttCode, UserRights::GetUserId());
|
||||||
if ($oAttDef instanceof AttributeString)
|
|
||||||
{
|
|
||||||
// Note: the user friendly name is the contact friendly name if a contact is attached to the logged in user
|
|
||||||
$this->Set($sAttCode, UserRights::GetUserFriendlyName());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ($oAttDef->IsExternalKey())
|
|
||||||
{
|
|
||||||
if ($oAttDef->GetTargetClass() != 'User')
|
|
||||||
{
|
|
||||||
throw new Exception("SetCurrentUser: the attribute $sAttCode must be an external key to 'User', found '".$oAttDef->GetTargetClass()."'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->Set($sAttCode, UserRights::GetUserId());
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lifecycle action: Set the current logged in CONTACT for the given attribute
|
|
||||||
*/
|
|
||||||
public function SetCurrentPerson($sAttCode)
|
|
||||||
{
|
|
||||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
|
||||||
if ($oAttDef instanceof AttributeString)
|
|
||||||
{
|
|
||||||
$iPerson = UserRights::GetContactId();
|
|
||||||
if ($iPerson == 0)
|
|
||||||
{
|
|
||||||
$this->Set($sAttCode, '');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$oPerson = MetaModel::GetObject('Person', $iPerson);
|
|
||||||
$this->Set($sAttCode, $oPerson->Get('friendlyname'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ($oAttDef->IsExternalKey())
|
|
||||||
{
|
|
||||||
if (!MetaModel::IsParentClass($oAttDef->GetTargetClass(), 'Person'))
|
|
||||||
{
|
|
||||||
throw new Exception("SetCurrentContact: the attribute $sAttCode must be an external key to 'Person' or any other class above 'Person', found '".$oAttDef->GetTargetClass()."'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->Set($sAttCode, UserRights::GetContactId());
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2331,97 +2270,84 @@ abstract class DBObject implements iDisplay
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Create query parameters (SELECT ... WHERE service = :this->service_id)
|
* Create query parameters (SELECT ... WHERE service = :this->service_id)
|
||||||
* to be used with the APIs DBObjectSearch/DBObjectSet
|
* to be used with the APIs DBObjectSearch/DBObjectSet
|
||||||
*
|
*
|
||||||
* Starting 2.0.2 the parameters are computed on demand, at the lowest level,
|
* Starting 2.0.2 the parameters are computed on demand, at the lowest level,
|
||||||
* in VariableExpression::Render()
|
* in VariableExpression::Render()
|
||||||
*/
|
*/
|
||||||
public function ToArgsForQuery($sArgName = 'this')
|
public function ToArgsForQuery($sArgName = 'this')
|
||||||
{
|
{
|
||||||
return array($sArgName.'->object()' => $this);
|
return array($sArgName.'->object()' => $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Create template placeholders: now equivalent to ToArgsForQuery since the actual
|
* Create template placeholders
|
||||||
* template placeholders are computed on demand.
|
* An improvement could be to compute the values on demand
|
||||||
*/
|
* (i.e. interpret the template to determine the placeholders)
|
||||||
|
*/
|
||||||
public function ToArgs($sArgName = 'this')
|
public function ToArgs($sArgName = 'this')
|
||||||
{
|
{
|
||||||
return $this->ToArgsForQuery($sArgName);
|
if (is_null($this->m_aAsArgs))
|
||||||
}
|
|
||||||
|
|
||||||
public function GetForTemplate($sPlaceholderAttCode)
|
|
||||||
{
|
|
||||||
$ret = null;
|
|
||||||
if (($iPos = strpos($sPlaceholderAttCode, '->')) !== false)
|
|
||||||
{
|
{
|
||||||
$sExtKeyAttCode = substr($sPlaceholderAttCode, 0, $iPos);
|
$oKPI = new ExecutionKPI();
|
||||||
$sRemoteAttCode = substr($sPlaceholderAttCode, $iPos + 2);
|
$aScalarArgs = $this->ToArgsForQuery($sArgName);
|
||||||
if (!MetaModel::IsValidAttCode(get_class($this), $sExtKeyAttCode))
|
$aScalarArgs[$sArgName] = $this->GetKey();
|
||||||
{
|
$aScalarArgs[$sArgName.'->id'] = $this->GetKey();
|
||||||
throw new CoreException("Unknown attribute '$sExtKeyAttCode' for the class ".get_class($this));
|
$aScalarArgs[$sArgName.'->hyperlink()'] = $this->GetHyperlink('iTopStandardURLMaker', false);
|
||||||
}
|
$aScalarArgs[$sArgName.'->hyperlink(portal)'] = $this->GetHyperlink('PortalURLMaker', false);
|
||||||
|
$aScalarArgs[$sArgName.'->name()'] = $this->GetName();
|
||||||
$oKeyAttDef = MetaModel::GetAttributeDef(get_class($this), $sExtKeyAttCode);
|
|
||||||
if (!$oKeyAttDef instanceof AttributeExternalKey)
|
|
||||||
{
|
|
||||||
throw new CoreException("'$sExtKeyAttCode' is not an external key of the class ".get_class($this));
|
|
||||||
}
|
|
||||||
$sRemoteClass = $oKeyAttDef->GetTargetClass();
|
|
||||||
$oRemoteObj = MetaModel::GetObject($sRemoteClass, $this->GetStrict($sExtKeyAttCode), false);
|
|
||||||
if (is_null($oRemoteObj))
|
|
||||||
{
|
|
||||||
$ret = Dict::S('UI:UndefinedObject');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Recurse
|
|
||||||
$ret = $oRemoteObj->GetForTemplate($sRemoteAttCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch($sPlaceholderAttCode)
|
|
||||||
{
|
|
||||||
case 'id':
|
|
||||||
$ret = $this->GetKey();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'hyperlink()':
|
|
||||||
$ret = $this->GetHyperlink('iTopStandardURLMaker', false);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'hyperlink(portal)':
|
$sClass = get_class($this);
|
||||||
$ret = $this->GetHyperlink('PortalURLMaker', false);
|
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||||
break;
|
|
||||||
|
|
||||||
case 'name()':
|
|
||||||
$ret = $this->GetName();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (preg_match('/^([^(]+)\\((.+)\\)$/', $sPlaceholderAttCode, $aMatches))
|
|
||||||
{
|
|
||||||
$sVerb = $aMatches[1];
|
|
||||||
$sAttCode = $aMatches[2];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sVerb = '';
|
|
||||||
$sAttCode = $sPlaceholderAttCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
|
||||||
$ret = $oAttDef->GetForTemplate($this->Get($sAttCode), $sVerb, $this);
|
|
||||||
}
|
|
||||||
if ($ret === null)
|
|
||||||
{
|
{
|
||||||
$ret = '';
|
if ($oAttDef instanceof AttributeCaseLog)
|
||||||
|
{
|
||||||
|
$oCaseLog = $this->Get($sAttCode);
|
||||||
|
$aScalarArgs[$sArgName.'->'.$sAttCode] = $oCaseLog->GetText();
|
||||||
|
$sHead = $oCaseLog->GetLatestEntry();
|
||||||
|
$aScalarArgs[$sArgName.'->head('.$sAttCode.')'] = $sHead;
|
||||||
|
$aScalarArgs[$sArgName.'->head_html('.$sAttCode.')'] = '<div class="caselog_entry">'.str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sHead, ENT_QUOTES, 'UTF-8')).'</div>';
|
||||||
|
$aScalarArgs[$sArgName.'->html('.$sAttCode.')'] = $oCaseLog->GetAsEmailHtml();
|
||||||
|
}
|
||||||
|
elseif ($oAttDef->IsScalar())
|
||||||
|
{
|
||||||
|
$aScalarArgs[$sArgName.'->'.$sAttCode] = $this->Get($sAttCode);
|
||||||
|
// #@# Note: This has been proven to be quite slow, this can slow down bulk load
|
||||||
|
$sAsHtml = $this->GetAsHtml($sAttCode);
|
||||||
|
$aScalarArgs[$sArgName.'->html('.$sAttCode.')'] = $sAsHtml;
|
||||||
|
$aScalarArgs[$sArgName.'->label('.$sAttCode.')'] = $this->GetEditValue($sAttCode); // "Nice" display value, but without HTML tags and entities
|
||||||
|
}
|
||||||
|
elseif ($oAttDef->IsLinkSet())
|
||||||
|
{
|
||||||
|
$sRemoteName = $oAttDef->IsIndirect() ? $oAttDef->GetExtKeyToRemote().'_friendlyname' : 'friendlyname';
|
||||||
|
|
||||||
|
$oLinkSet = clone $this->Get($sAttCode); // Workaround/Safety net for Trac #887
|
||||||
|
$iLimit = MetaModel::GetConfig()->Get('max_linkset_output');
|
||||||
|
if ($iLimit > 0)
|
||||||
|
{
|
||||||
|
$oLinkSet->SetLimit($iLimit);
|
||||||
|
}
|
||||||
|
$aNames = $oLinkSet->GetColumnAsArray($sRemoteName);
|
||||||
|
if ($iLimit > 0)
|
||||||
|
{
|
||||||
|
$iTotal = $oLinkSet->Count();
|
||||||
|
if ($iTotal > count($aNames))
|
||||||
|
{
|
||||||
|
$aNames[] = '... '.Dict::Format('UI:TruncatedResults', count($aNames), $iTotal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$sNames = implode("\n", $aNames);
|
||||||
|
$aScalarArgs[$sArgName.'->'.$sAttCode] = $sNames;
|
||||||
|
$aScalarArgs[$sArgName.'->html('.$sAttCode.')'] = '<ul><li>'.implode("</li><li>", $aNames).'</li></ul>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->m_aAsArgs = $aScalarArgs;
|
||||||
|
$oKPI->ComputeStats('ToArgs', get_class($this));
|
||||||
}
|
}
|
||||||
return $ret;
|
return $this->m_aAsArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// To be optionaly overloaded
|
// To be optionaly overloaded
|
||||||
@@ -2601,119 +2527,46 @@ abstract class DBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return an empty set for the parent of all
|
// Return an empty set for the parent of all
|
||||||
// May be overloaded.
|
|
||||||
// Anyhow, this way of implementing the relations suffers limitations (not handling the redundancy)
|
|
||||||
// and you should consider defining those things in XML.
|
|
||||||
public static function GetRelationQueries($sRelCode)
|
public static function GetRelationQueries($sRelCode)
|
||||||
{
|
{
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reserved: do not overload
|
|
||||||
public static function GetRelationQueriesEx($sRelCode)
|
|
||||||
{
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will be deprecated soon - use GetRelatedObjectsDown/Up instead to take redundancy into account
|
|
||||||
*/
|
|
||||||
public function GetRelatedObjects($sRelCode, $iMaxDepth = 99, &$aResults = array())
|
public function GetRelatedObjects($sRelCode, $iMaxDepth = 99, &$aResults = array())
|
||||||
{
|
{
|
||||||
// Temporary patch: until the impact analysis GUI gets rewritten,
|
foreach (MetaModel::EnumRelationQueries(get_class($this), $sRelCode) as $sDummy => $aQueryInfo)
|
||||||
// let's consider that "depends on" is equivalent to "impacts/up"
|
|
||||||
// The current patch has been implemented in DBObject and MetaModel
|
|
||||||
$sHackedRelCode = $sRelCode;
|
|
||||||
$bDown = true;
|
|
||||||
if ($sRelCode == 'depends on')
|
|
||||||
{
|
{
|
||||||
$sHackedRelCode = 'impacts';
|
MetaModel::DbgTrace("object=".$this->GetKey().", depth=$iMaxDepth, rel=".$aQueryInfo["sQuery"]);
|
||||||
$bDown = false;
|
$sQuery = $aQueryInfo["sQuery"];
|
||||||
}
|
$bPropagate = $aQueryInfo["bPropagate"];
|
||||||
foreach (MetaModel::EnumRelationQueries(get_class($this), $sHackedRelCode, $bDown) as $sDummy => $aQueryInfo)
|
$iDistance = $aQueryInfo["iDistance"];
|
||||||
{
|
|
||||||
$sQuery = $bDown ? $aQueryInfo['sQueryDown'] : $aQueryInfo['sQueryUp'];
|
|
||||||
//$bPropagate = $aQueryInfo["bPropagate"];
|
|
||||||
//$iDepth = $bPropagate ? $iMaxDepth - 1 : 0;
|
|
||||||
$iDepth = $iMaxDepth - 1;
|
|
||||||
|
|
||||||
// Note: the loop over the result set has been written in an unusual way for error reporting purposes
|
$iDepth = $bPropagate ? $iMaxDepth - 1 : 0;
|
||||||
// In the case of a wrong query parameter name, the error occurs on the first call to Fetch,
|
|
||||||
// thus we need to have this first call into the try/catch, but
|
$oFlt = DBObjectSearch::FromOQL($sQuery);
|
||||||
// we do NOT want to nest the try/catch for the error message to be clear
|
$oObjSet = new DBObjectSet($oFlt, array(), $this->ToArgsForQuery());
|
||||||
try
|
while ($oObj = $oObjSet->Fetch())
|
||||||
{
|
{
|
||||||
$oFlt = DBObjectSearch::FromOQL($sQuery);
|
$sRootClass = MetaModel::GetRootClass(get_class($oObj));
|
||||||
$oObjSet = new DBObjectSet($oFlt, array(), $this->ToArgsForQuery());
|
$sObjKey = $oObj->GetKey();
|
||||||
$oObj = $oObjSet->Fetch();
|
if (array_key_exists($sRootClass, $aResults))
|
||||||
}
|
|
||||||
catch (Exception $e)
|
|
||||||
{
|
|
||||||
$sClassOfDefinition = $aQueryInfo['_legacy_'] ? get_class($this).'(or a parent)::GetRelationQueries()' : $aQueryInfo['sDefinedInClass'];
|
|
||||||
throw new Exception("Wrong query for the relation $sRelCode/$sClassOfDefinition/{$aQueryInfo['sNeighbour']}: ".$e->getMessage());
|
|
||||||
}
|
|
||||||
if ($oObj)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
$sRootClass = MetaModel::GetRootClass(get_class($oObj));
|
if (array_key_exists($sObjKey, $aResults[$sRootClass]))
|
||||||
$sObjKey = $oObj->GetKey();
|
|
||||||
if (array_key_exists($sRootClass, $aResults))
|
|
||||||
{
|
{
|
||||||
if (array_key_exists($sObjKey, $aResults[$sRootClass]))
|
continue; // already visited, skip
|
||||||
{
|
|
||||||
continue; // already visited, skip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$aResults[$sRootClass][$sObjKey] = $oObj;
|
|
||||||
if ($iDepth > 0)
|
|
||||||
{
|
|
||||||
$oObj->GetRelatedObjects($sRelCode, $iDepth, $aResults);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while ($oObj = $oObjSet->Fetch());
|
|
||||||
|
$aResults[$sRootClass][$sObjKey] = $oObj;
|
||||||
|
if ($iDepth > 0)
|
||||||
|
{
|
||||||
|
$oObj->GetRelatedObjects($sRelCode, $iDepth, $aResults);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $aResults;
|
return $aResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute the "RelatedObjects" (forward or "down" direction) for the object
|
|
||||||
* for the specified relation
|
|
||||||
*
|
|
||||||
* @param string $sRelCode The code of the relation to use for the computation
|
|
||||||
* @param int $iMaxDepth Maximum recursion depth
|
|
||||||
* @param boolean $bEnableReduncancy Whether or not to take into account the redundancy
|
|
||||||
*
|
|
||||||
* @return RelationGraph The graph of all the related objects
|
|
||||||
*/
|
|
||||||
public function GetRelatedObjectsDown($sRelCode, $iMaxDepth = 99, $bEnableRedundancy = true)
|
|
||||||
{
|
|
||||||
$oGraph = new RelationGraph();
|
|
||||||
$oGraph->AddSourceObject($this);
|
|
||||||
$oGraph->ComputeRelatedObjectsDown($sRelCode, $iMaxDepth, $bEnableRedundancy);
|
|
||||||
return $oGraph;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute the "RelatedObjects" (reverse or "up" direction) for the object
|
|
||||||
* for the specified relation
|
|
||||||
*
|
|
||||||
* @param string $sRelCode The code of the relation to use for the computation
|
|
||||||
* @param int $iMaxDepth Maximum recursion depth
|
|
||||||
* @param boolean $bEnableReduncancy Whether or not to take into account the redundancy
|
|
||||||
*
|
|
||||||
* @return RelationGraph The graph of all the related objects
|
|
||||||
*/
|
|
||||||
public function GetRelatedObjectsUp($sRelCode, $iMaxDepth = 99, $bEnableRedundancy = true)
|
|
||||||
{
|
|
||||||
$oGraph = new RelationGraph();
|
|
||||||
$oGraph->AddSourceObject($this);
|
|
||||||
$oGraph->ComputeRelatedObjectsUp($sRelCode, $iMaxDepth, $bEnableRedundancy);
|
|
||||||
return $oGraph;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetReferencingObjects($bAllowAllData = false)
|
public function GetReferencingObjects($bAllowAllData = false)
|
||||||
{
|
{
|
||||||
$aDependentObjects = array();
|
$aDependentObjects = array();
|
||||||
@@ -2844,7 +2697,7 @@ abstract class DBObject implements iDisplay
|
|||||||
*/
|
*/
|
||||||
public function GetSynchroData()
|
public function GetSynchroData()
|
||||||
{
|
{
|
||||||
if (is_null($this->m_aSynchroData))
|
if ($this->m_aSynchroData == null)
|
||||||
{
|
{
|
||||||
$sOQL = "SELECT replica,datasource FROM SynchroReplica AS replica JOIN SynchroDataSource AS datasource ON replica.sync_source_id=datasource.id WHERE replica.dest_class = :dest_class AND replica.dest_id = :dest_id";
|
$sOQL = "SELECT replica,datasource FROM SynchroReplica AS replica JOIN SynchroDataSource AS datasource ON replica.sync_source_id=datasource.id WHERE replica.dest_class = :dest_class AND replica.dest_id = :dest_id";
|
||||||
$oReplicaSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array() /* order by*/, array('dest_class' => get_class($this), 'dest_id' => $this->GetKey()));
|
$oReplicaSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array() /* order by*/, array('dest_class' => get_class($this), 'dest_id' => $this->GetKey()));
|
||||||
@@ -2990,27 +2843,5 @@ abstract class DBObject implements iDisplay
|
|||||||
'params' => $aParameters
|
'params' => $aParameters
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes a text-like fingerprint identifying the content of the object
|
|
||||||
* but excluding the specified columns
|
|
||||||
* @param $aExcludedColumns array The list of columns to exclude
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function Fingerprint($aExcludedColumns = array())
|
|
||||||
{
|
|
||||||
$sFingerprint = '';
|
|
||||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
|
|
||||||
{
|
|
||||||
if (!in_array($sAttCode, $aExcludedColumns))
|
|
||||||
{
|
|
||||||
if ($oAttDef->IsPartOfFingerprint())
|
|
||||||
{
|
|
||||||
$sFingerprint .= chr(0).$oAttDef->Fingerprint($this->Get($sAttCode));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $sFingerprint;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2014 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
/**
|
/**
|
||||||
* Object set management
|
* Object set management
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -47,14 +47,14 @@ class DBObjectSet
|
|||||||
/**
|
/**
|
||||||
* Create a new set based on a Search definition.
|
* Create a new set based on a Search definition.
|
||||||
*
|
*
|
||||||
* @param DBSearch $oFilter The search filter defining the objects which are part of the set (multiple columns/objects per row are supported)
|
* @param DBObjectSearch $oFilter The search filter defining the objects which are part of the set (multiple columns/objects per row are supported)
|
||||||
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
||||||
* @param hash $aArgs Values to substitute for the search/query parameters (if any). Format: param_name => value
|
* @param hash $aArgs Values to substitute for the search/query parameters (if any). Format: param_name => value
|
||||||
* @param hash $aExtendedDataSpec
|
* @param hash $aExtendedDataSpec
|
||||||
* @param int $iLimitCount Maximum number of rows to load (i.e. equivalent to MySQL's LIMIT start, count)
|
* @param int $iLimitCount Maximum number of rows to load (i.e. equivalent to MySQL's LIMIT start, count)
|
||||||
* @param int $iLimitStart Index of the first row to load (i.e. equivalent to MySQL's LIMIT start, count)
|
* @param int $iLimitStart Index of the first row to load (i.e. equivalent to MySQL's LIMIT start, count)
|
||||||
*/
|
*/
|
||||||
public function __construct(DBSearch $oFilter, $aOrderBy = array(), $aArgs = array(), $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0)
|
public function __construct(DBObjectSearch $oFilter, $aOrderBy = array(), $aArgs = array(), $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0)
|
||||||
{
|
{
|
||||||
$this->m_oFilter = $oFilter->DeepClone();
|
$this->m_oFilter = $oFilter->DeepClone();
|
||||||
$this->m_aAddedIds = array();
|
$this->m_aAddedIds = array();
|
||||||
@@ -86,7 +86,7 @@ class DBObjectSet
|
|||||||
$sRet = '';
|
$sRet = '';
|
||||||
$this->Rewind();
|
$this->Rewind();
|
||||||
$sRet .= "Set (".$this->m_oFilter->ToOQL().")<br/>\n";
|
$sRet .= "Set (".$this->m_oFilter->ToOQL().")<br/>\n";
|
||||||
$sRet .= "Query: <pre style=\"font-size: smaller; display:inline;\">".$this->m_oFilter->MakeSelectQuery().")</pre>\n";
|
$sRet .= "Query: <pre style=\"font-size: smaller; display:inline;\">".MetaModel::MakeSelectQuery($this->m_oFilter, array()).")</pre>\n";
|
||||||
|
|
||||||
$sRet .= $this->Count()." records<br/>\n";
|
$sRet .= $this->Count()." records<br/>\n";
|
||||||
if ($this->Count() > 0)
|
if ($this->Count() > 0)
|
||||||
@@ -237,7 +237,7 @@ class DBObjectSet
|
|||||||
// let's create one search definition corresponding only to the first column
|
// let's create one search definition corresponding only to the first column
|
||||||
$sClass = reset($aClasses);
|
$sClass = reset($aClasses);
|
||||||
$sAlias = key($aClasses);
|
$sAlias = key($aClasses);
|
||||||
$oFilter = new DBObjectSearch($sClass, $sAlias);
|
$oFilter = new CMDBSearchFilter($sClass, $sAlias);
|
||||||
|
|
||||||
$oRetSet = new self($oFilter);
|
$oRetSet = new self($oFilter);
|
||||||
$oRetSet->m_bLoaded = true; // no DB load
|
$oRetSet->m_bLoaded = true; // no DB load
|
||||||
@@ -353,7 +353,7 @@ class DBObjectSet
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the DBSearch corresponding to the objects present in this set
|
* Retrieve the DBObjectSearch corresponding to the objects present in this set
|
||||||
*
|
*
|
||||||
* Limitation:
|
* Limitation:
|
||||||
* This method will NOT work for sets with several columns (i.e. several objects per row)
|
* This method will NOT work for sets with several columns (i.e. several objects per row)
|
||||||
@@ -513,11 +513,11 @@ class DBObjectSet
|
|||||||
|
|
||||||
if ($this->m_iLimitCount > 0)
|
if ($this->m_iLimitCount > 0)
|
||||||
{
|
{
|
||||||
$sSQL = $this->m_oFilter->MakeSelectQuery($this->GetRealSortOrder(), $this->m_aArgs, $this->m_aAttToLoad, $this->m_aExtendedDataSpec, $this->m_iLimitCount, $this->m_iLimitStart);
|
$sSQL = MetaModel::MakeSelectQuery($this->m_oFilter, $this->GetRealSortOrder(), $this->m_aArgs, $this->m_aAttToLoad, $this->m_aExtendedDataSpec, $this->m_iLimitCount, $this->m_iLimitStart);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$sSQL = $this->m_oFilter->MakeSelectQuery($this->GetRealSortOrder(), $this->m_aArgs, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
|
$sSQL = MetaModel::MakeSelectQuery($this->m_oFilter, $this->GetRealSortOrder(), $this->m_aArgs, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_object($this->m_oSQLResult))
|
if (is_object($this->m_oSQLResult))
|
||||||
@@ -549,7 +549,7 @@ class DBObjectSet
|
|||||||
{
|
{
|
||||||
if (is_null($this->m_iNumTotalDBRows))
|
if (is_null($this->m_iNumTotalDBRows))
|
||||||
{
|
{
|
||||||
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, 0, 0, true);
|
$sSQL = MetaModel::MakeSelectQuery($this->m_oFilter, array(), $this->m_aArgs, null, null, 0, 0, true);
|
||||||
$resQuery = CMDBSource::Query($sSQL);
|
$resQuery = CMDBSource::Query($sSQL);
|
||||||
if (!$resQuery) return 0;
|
if (!$resQuery) return 0;
|
||||||
|
|
||||||
@@ -837,16 +837,74 @@ class DBObjectSet
|
|||||||
* Compare two sets of objects to determine if their content is identical or not.
|
* Compare two sets of objects to determine if their content is identical or not.
|
||||||
*
|
*
|
||||||
* Limitation:
|
* Limitation:
|
||||||
* Works only for sets of 1 column (i.e. one class of object selected)
|
* Works only on objects written to the DB, since we rely on their identifiers
|
||||||
*
|
*
|
||||||
* @param DBObjectSet $oObjectSet
|
* @param DBObjectSet $oObjectSet
|
||||||
* @param array $aExcludeColumns The list of columns to exclude frop the comparison
|
|
||||||
* @return boolean True if the sets are identical, false otherwise
|
* @return boolean True if the sets are identical, false otherwise
|
||||||
*/
|
*/
|
||||||
public function HasSameContents(DBObjectSet $oObjectSet, $aExcludeColumns = array())
|
public function HasSameContents(DBObjectSet $oObjectSet)
|
||||||
{
|
{
|
||||||
$oComparator = new DBObjectSetComparator($this, $oObjectSet, $aExcludeColumns);
|
if ($this->GetRootClass() != $oObjectSet->GetRootClass())
|
||||||
return $oComparator->SetsAreEquivalent();
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($this->Count() != $oObjectSet->Count())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$aId2Row = array();
|
||||||
|
$bRet = true;
|
||||||
|
$iCurrPos = $this->m_iCurrRow; // Save the cursor
|
||||||
|
$idx = 0;
|
||||||
|
|
||||||
|
// Optimization: we retain the first $iMaxObjects objects in memory
|
||||||
|
// to speed up the comparison of small sets (see below: $oObject->Equals($oSibling))
|
||||||
|
$iMaxObjects = 20;
|
||||||
|
$aCachedObjects = array();
|
||||||
|
while($oObj = $this->Fetch())
|
||||||
|
{
|
||||||
|
$aId2Row[$oObj->GetKey()] = $idx;
|
||||||
|
if ($idx <= $iMaxObjects)
|
||||||
|
{
|
||||||
|
$aCachedObjects[$idx] = $oObj;
|
||||||
|
}
|
||||||
|
$idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$oObjectSet->Rewind();
|
||||||
|
while ($oObject = $oObjectSet->Fetch())
|
||||||
|
{
|
||||||
|
$iObjectKey = $oObject->GetKey();
|
||||||
|
if ($iObjectKey < 0)
|
||||||
|
{
|
||||||
|
$bRet = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!array_key_exists($iObjectKey, $aId2Row))
|
||||||
|
{
|
||||||
|
$bRet = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$iRow = $aId2Row[$iObjectKey];
|
||||||
|
if (array_key_exists($iRow, $aCachedObjects))
|
||||||
|
{
|
||||||
|
// Cache hit
|
||||||
|
$oSibling = $aCachedObjects[$iRow];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Go fetch it from the DB, unless it's an object added in-memory
|
||||||
|
$oSibling = $this->GetObjectAt($iRow);
|
||||||
|
}
|
||||||
|
if (!$oObject->Equals($oSibling))
|
||||||
|
{
|
||||||
|
$bRet = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->Seek($iCurrPos); // Restore the cursor
|
||||||
|
return $bRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetObjectAt($iIndex)
|
protected function GetObjectAt($iIndex)
|
||||||
@@ -909,7 +967,12 @@ class DBObjectSet
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will be deprecated soon - use MetaModel::GetRelatedObjectsDown/Up instead to take redundancy into account
|
* Compute the "RelatedObjects" (for the given relation, as defined by MetaModel::GetRelatedObjects) for a whole set of DBObjects
|
||||||
|
*
|
||||||
|
* @param string $sRelCode The code of the relation to use for the computation
|
||||||
|
* @param int $iMaxDepth Teh maximum recursion depth
|
||||||
|
*
|
||||||
|
* @return Array An array containg all the "related" objects
|
||||||
*/
|
*/
|
||||||
public function GetRelatedObjects($sRelCode, $iMaxDepth = 99)
|
public function GetRelatedObjects($sRelCode, $iMaxDepth = 99)
|
||||||
{
|
{
|
||||||
@@ -933,50 +996,6 @@ class DBObjectSet
|
|||||||
}
|
}
|
||||||
return $aRelatedObjs;
|
return $aRelatedObjs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute the "RelatedObjects" (forward or "down" direction) for the set
|
|
||||||
* for the specified relation
|
|
||||||
*
|
|
||||||
* @param string $sRelCode The code of the relation to use for the computation
|
|
||||||
* @param int $iMaxDepth Maximum recursion depth
|
|
||||||
* @param boolean $bEnableReduncancy Whether or not to take into account the redundancy
|
|
||||||
*
|
|
||||||
* @return RelationGraph The graph of all the related objects
|
|
||||||
*/
|
|
||||||
public function GetRelatedObjectsDown($sRelCode, $iMaxDepth = 99, $bEnableRedundancy = true)
|
|
||||||
{
|
|
||||||
$oGraph = new RelationGraph();
|
|
||||||
$this->Rewind();
|
|
||||||
while($oObj = $this->Fetch())
|
|
||||||
{
|
|
||||||
$oGraph->AddSourceObject($oObj);
|
|
||||||
}
|
|
||||||
$oGraph->ComputeRelatedObjectsDown($sRelCode, $iMaxDepth, $bEnableRedundancy);
|
|
||||||
return $oGraph;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute the "RelatedObjects" (reverse or "up" direction) for the set
|
|
||||||
* for the specified relation
|
|
||||||
*
|
|
||||||
* @param string $sRelCode The code of the relation to use for the computation
|
|
||||||
* @param int $iMaxDepth Maximum recursion depth
|
|
||||||
* @param boolean $bEnableReduncancy Whether or not to take into account the redundancy
|
|
||||||
*
|
|
||||||
* @return RelationGraph The graph of all the related objects
|
|
||||||
*/
|
|
||||||
public function GetRelatedObjectsUp($sRelCode, $iMaxDepth = 99, $bEnableRedundancy = true)
|
|
||||||
{
|
|
||||||
$oGraph = new RelationGraph();
|
|
||||||
$this->Rewind();
|
|
||||||
while($oObj = $this->Fetch())
|
|
||||||
{
|
|
||||||
$oGraph->AddSinkObject($oObj);
|
|
||||||
}
|
|
||||||
$oGraph->ComputeRelatedObjectsUp($sRelCode, $iMaxDepth, $bEnableRedundancy);
|
|
||||||
return $oGraph;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds an object that contains the values that are common to all the objects
|
* Builds an object that contains the values that are common to all the objects
|
||||||
@@ -1137,217 +1156,3 @@ function HashCountComparison($a, $b) // Sort descending on 'count'
|
|||||||
}
|
}
|
||||||
return ($a['count'] > $b['count']) ? -1 : 1;
|
return ($a['count'] > $b['count']) ? -1 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper class to compare the content of two DBObjectSets based on the fingerprints of the contained objects
|
|
||||||
* The FIRST SET MUST BE LOADED FROM THE DATABASE, the second one can be a set of objects in memory
|
|
||||||
* When computing the actual differences, the algorithm tries to preserve as much as possible the EXISTING
|
|
||||||
* objects (i.e. prefers 'modified' to 'removed' + 'added')
|
|
||||||
*
|
|
||||||
* LIMITATIONS:
|
|
||||||
* - only DBObjectSets with one column (i.e. one class of object selected) are supported
|
|
||||||
* - the first set must be the one loaded from the database
|
|
||||||
*/
|
|
||||||
class DBObjectSetComparator
|
|
||||||
{
|
|
||||||
protected $aFingerprints1;
|
|
||||||
protected $aFingerprints2;
|
|
||||||
protected $aIDs1;
|
|
||||||
protected $aIDs2;
|
|
||||||
protected $aExcludedColumns;
|
|
||||||
protected $oSet1;
|
|
||||||
protected $oSet2;
|
|
||||||
protected $sAdditionalKeyColumn;
|
|
||||||
protected $aAdditionalKeys;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the comparator
|
|
||||||
* @param DBObjectSet $oSet1 The first set of objects to compare, or null
|
|
||||||
* @param DBObjectSet $oSet2 The second set of objects to compare, or null
|
|
||||||
* @param array $aExcludedColumns The list of columns (= attribute codes) to exclude from the comparison
|
|
||||||
* @param string $sAdditionalKeyColumn The attribute code of an additional column to be considered as a key indentifying the object (useful for n:n links)
|
|
||||||
*/
|
|
||||||
public function __construct($oSet1, $oSet2, $aExcludedColumns = array(), $sAdditionalKeyColumn = null)
|
|
||||||
{
|
|
||||||
$this->aFingerprints1 = null;
|
|
||||||
$this->aFingerprints2 = null;
|
|
||||||
$this->aIDs1 = array();
|
|
||||||
$this->aIDs2 = array();
|
|
||||||
$this->aExcludedColumns = $aExcludedColumns;
|
|
||||||
$this->sAdditionalKeyColumn = $sAdditionalKeyColumn;
|
|
||||||
$this->aAdditionalKeys = null;
|
|
||||||
$this->oSet1 = $oSet1;
|
|
||||||
$this->oSet2 = $oSet2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds the lists of fingerprints and initializes internal structures, if it was not already done
|
|
||||||
*/
|
|
||||||
protected function ComputeFingerprints()
|
|
||||||
{
|
|
||||||
if ($this->aFingerprints1 === null)
|
|
||||||
{
|
|
||||||
$this->aFingerprints1 = array();
|
|
||||||
$this->aFingerprints2 = array();
|
|
||||||
$this->aAdditionalKeys = array();
|
|
||||||
|
|
||||||
if ($this->oSet1 !== null)
|
|
||||||
{
|
|
||||||
$aAliases = $this->oSet1->GetSelectedClasses();
|
|
||||||
if (count($aAliases) > 1) throw new Exception('DBObjectSetComparator does not support Sets with more than one column. $oSet1: ('.print_r($aAliases, true).')');
|
|
||||||
|
|
||||||
$this->oSet1->Rewind();
|
|
||||||
while($oObj = $this->oSet1->Fetch())
|
|
||||||
{
|
|
||||||
$sFingerprint = $oObj->Fingerprint($this->aExcludedColumns);
|
|
||||||
$this->aFingerprints1[$sFingerprint] = $oObj;
|
|
||||||
if (!$oObj->IsNew())
|
|
||||||
{
|
|
||||||
$this->aIDs1[$oObj->GetKey()] = $oObj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->oSet1->Rewind();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->oSet2 !== null)
|
|
||||||
{
|
|
||||||
$aAliases = $this->oSet2->GetSelectedClasses();
|
|
||||||
if (count($aAliases) > 1) throw new Exception('DBObjectSetComparator does not support Sets with more than one column. $oSet2: ('.print_r($aAliases, true).')');
|
|
||||||
|
|
||||||
$this->oSet2->Rewind();
|
|
||||||
while($oObj = $this->oSet2->Fetch())
|
|
||||||
{
|
|
||||||
$sFingerprint = $oObj->Fingerprint($this->aExcludedColumns);
|
|
||||||
$this->aFingerprints2[$sFingerprint] = $oObj;
|
|
||||||
if (!$oObj->IsNew())
|
|
||||||
{
|
|
||||||
$this->aIDs2[$oObj->GetKey()] = $oObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->sAdditionalKeyColumn !== null)
|
|
||||||
{
|
|
||||||
$this->aAdditionalKeys[$oObj->Get($this->sAdditionalKeyColumn)] = $oObj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->oSet2->Rewind();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells if the sets are equivalent or not. Returns as soon as the first difference is found.
|
|
||||||
* @return boolean true if the set have an equivalent content, false otherwise
|
|
||||||
*/
|
|
||||||
public function SetsAreEquivalent()
|
|
||||||
{
|
|
||||||
if (($this->oSet1 === null) && ($this->oSet2 === null))
|
|
||||||
{
|
|
||||||
// Both sets are empty, they are equal
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (($this->oSet1 === null) || ($this->oSet2 === null))
|
|
||||||
{
|
|
||||||
// one of them is empty, they are different
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (($this->oSet1->GetRootClass() != $this->oSet2->GetRootClass()) || ($this->oSet1->Count() != $this->oSet2->Count())) return false;
|
|
||||||
|
|
||||||
$this->ComputeFingerprints();
|
|
||||||
|
|
||||||
// Check that all objects in Set1 are also in Set2
|
|
||||||
foreach($this->aFingerprints1 as $sFingerprint => $oObj)
|
|
||||||
{
|
|
||||||
if (!array_key_exists($sFingerprint, $this->aFingerprints2))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vice versa
|
|
||||||
// Check that all objects in Set2 are also in Set1
|
|
||||||
foreach($this->aFingerprints2 as $sFingerprint => $oObj)
|
|
||||||
{
|
|
||||||
if (!array_key_exists($sFingerprint, $this->aFingerprints1))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the list of differences between the two sets. In ordeer to write back into the database only the minimum changes
|
|
||||||
* THE FIRST SET MUST BE THE ONE LOADED FROM THE DATABASE
|
|
||||||
* Returns a hash: 'added' => DBObject(s), 'removed' => DBObject(s), 'modified' => DBObjects(s)
|
|
||||||
* @return Ambigous <int:DBObject: , unknown>
|
|
||||||
*/
|
|
||||||
public function GetDifferences()
|
|
||||||
{
|
|
||||||
$aResult = array('added' => array(), 'removed' => array(), 'modified' => array());
|
|
||||||
$this->ComputeFingerprints();
|
|
||||||
|
|
||||||
// Check that all objects in Set1 are also in Set2
|
|
||||||
foreach($this->aFingerprints1 as $sFingerprint => $oObj)
|
|
||||||
{
|
|
||||||
// Beware: the elements from the first set MUST come from the database, otherwise the result will be irrelevant
|
|
||||||
if ($oObj->IsNew()) throw new Exception('Cannot compute differences when elements from the first set are NOT in the database');
|
|
||||||
if (array_key_exists($oObj->GetKey(), $this->aIDs2) && ($this->aIDs2[$oObj->GetKey()]->IsModified()))
|
|
||||||
{
|
|
||||||
// The very same object exists in both set, but was modified since its load
|
|
||||||
$aResult['modified'][$oObj->GetKey()] = $this->aIDs2[$oObj->GetKey()];
|
|
||||||
}
|
|
||||||
else if (($this->sAdditionalKeyColumn !== null) && array_key_exists($oObj->Get($this->sAdditionalKeyColumn), $this->aAdditionalKeys))
|
|
||||||
{
|
|
||||||
// Special case for n:n links where the link is recreated between the very same 2 objects, but some of its attributes are modified
|
|
||||||
// Let's consider this as a "modification" instead of "deletion" + "creation" in order to have a "clean" history for the objects
|
|
||||||
$oDestObj = $this->aAdditionalKeys[$oObj->Get($this->sAdditionalKeyColumn)];
|
|
||||||
$oCloneObj = $this->CopyFrom($oObj, $oDestObj);
|
|
||||||
$aResult['modified'][$oObj->GetKey()] = $oCloneObj;
|
|
||||||
// Mark this as processed, so that the pass on aFingerprints2 below ignores this object
|
|
||||||
$sNewFingerprint = $oDestObj->Fingerprint($this->aExcludedColumns);
|
|
||||||
$this->aFingerprints2[$sNewFingerprint] = $oCloneObj;
|
|
||||||
}
|
|
||||||
else if (!array_key_exists($sFingerprint, $this->aFingerprints2))
|
|
||||||
{
|
|
||||||
$aResult['removed'][] = $oObj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vice versa
|
|
||||||
// Check that all objects in Set2 are also in Set1
|
|
||||||
foreach($this->aFingerprints2 as $sFingerprint => $oObj)
|
|
||||||
{
|
|
||||||
if (array_key_exists($oObj->GetKey(), $this->aIDs1) && ($oObj->IsModified()))
|
|
||||||
{
|
|
||||||
// Already marked as modified above
|
|
||||||
//$aResult['modified'][$oObj->GetKey()] = $oObj;
|
|
||||||
}
|
|
||||||
else if (!array_key_exists($sFingerprint, $this->aFingerprints1))
|
|
||||||
{
|
|
||||||
$aResult['added'][] = $oObj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $aResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helpr to clone (in memory) an object and to apply to it the values taken from a second object
|
|
||||||
* @param DBObject $oObjToClone
|
|
||||||
* @param DBObject $oObjWithValues
|
|
||||||
* @return DBObject The modified clone
|
|
||||||
*/
|
|
||||||
protected function CopyFrom($oObjToClone, $oObjWithValues)
|
|
||||||
{
|
|
||||||
$oObj = MetaModel::GetObject(get_class($oObjToClone), $oObjToClone->GetKey());
|
|
||||||
foreach(MetaModel::ListAttributeDefs(get_class($oObj)) as $sAttCode => $oAttDef)
|
|
||||||
{
|
|
||||||
if (!in_array($sAttCode, $this->aExcludedColumns) && $oAttDef->IsWritable())
|
|
||||||
{
|
|
||||||
$oObj->Set($sAttCode, $oObjWithValues->Get($sAttCode));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $oObj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,740 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Copyright (C) 2015 Combodo SARL
|
|
||||||
//
|
|
||||||
// This file is part of iTop.
|
|
||||||
//
|
|
||||||
// iTop is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// iTop is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
|
|
||||||
|
|
||||||
require_once('dbobjectsearch.class.php');
|
|
||||||
require_once('dbunionsearch.class.php');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An object search
|
|
||||||
*
|
|
||||||
* Note: in the ancient times of iTop, a search was named after DBObjectSearch.
|
|
||||||
* When the UNION has been introduced, it has been decided to:
|
|
||||||
* - declare a hierarchy of search classes, with two leafs :
|
|
||||||
* - one class to cope with a single query (A JOIN B... WHERE...)
|
|
||||||
* - and the other to cope with several queries (query1 UNION query2)
|
|
||||||
* - in order to preserve forward/backward compatibility of the existing modules
|
|
||||||
* - keep the name of DBObjectSearch even if it a little bit confusing
|
|
||||||
* - do not provide a type-hint for function parameters defined in the modules
|
|
||||||
* - leave the statements DBObjectSearch::FromOQL in the modules, though DBSearch is more relevant
|
|
||||||
*
|
|
||||||
* @copyright Copyright (C) 2015 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
abstract class DBSearch
|
|
||||||
{
|
|
||||||
protected $m_bDataFiltered = false;
|
|
||||||
protected $m_aModifierProperties = array();
|
|
||||||
|
|
||||||
// By default, some information may be hidden to the current user
|
|
||||||
// But it may happen that we need to disable that feature
|
|
||||||
protected $m_bAllowAllData = false;
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform a deep clone (as opposed to "clone" which does copy a reference to the underlying objects)
|
|
||||||
**/
|
|
||||||
public function DeepClone()
|
|
||||||
{
|
|
||||||
return unserialize(serialize($this)); // Beware this serializes/unserializes the search and its parameters as well
|
|
||||||
}
|
|
||||||
|
|
||||||
public function AllowAllData() {$this->m_bAllowAllData = true;}
|
|
||||||
public function IsAllDataAllowed() {return $this->m_bAllowAllData;}
|
|
||||||
public function IsDataFiltered() {return $this->m_bDataFiltered; }
|
|
||||||
public function SetDataFiltered() {$this->m_bDataFiltered = true;}
|
|
||||||
|
|
||||||
public function SetModifierProperty($sPluginClass, $sProperty, $value)
|
|
||||||
{
|
|
||||||
$this->m_aModifierProperties[$sPluginClass][$sProperty] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetModifierProperties($sPluginClass)
|
|
||||||
{
|
|
||||||
if (array_key_exists($sPluginClass, $this->m_aModifierProperties))
|
|
||||||
{
|
|
||||||
return $this->m_aModifierProperties[$sPluginClass];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract public function GetClassName($sAlias);
|
|
||||||
abstract public function GetClass();
|
|
||||||
abstract public function GetClassAlias();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change the class (only subclasses are supported as of now, because the conditions must fit the new class)
|
|
||||||
* Defaults to the first selected class (most of the time it is also the first joined class
|
|
||||||
*/
|
|
||||||
abstract public function ChangeClass($sNewClass, $sAlias = null);
|
|
||||||
abstract public function GetSelectedClasses();
|
|
||||||
|
|
||||||
abstract public function IsAny();
|
|
||||||
|
|
||||||
public function Describe(){return 'deprecated - use ToOQL() instead';}
|
|
||||||
public function DescribeConditionPointTo($sExtKeyAttCode, $aPointingTo){return 'deprecated - use ToOQL() instead';}
|
|
||||||
public function DescribeConditionRefBy($sForeignClass, $sForeignExtKeyAttCode){return 'deprecated - use ToOQL() instead';}
|
|
||||||
public function DescribeConditionRelTo($aRelInfo){return 'deprecated - use ToOQL() instead';}
|
|
||||||
public function DescribeConditions(){return 'deprecated - use ToOQL() instead';}
|
|
||||||
public function __DescribeHTML(){return 'deprecated - use ToOQL() instead';}
|
|
||||||
|
|
||||||
abstract public function ResetCondition();
|
|
||||||
abstract public function MergeConditionExpression($oExpression);
|
|
||||||
abstract public function AddConditionExpression($oExpression);
|
|
||||||
abstract public function AddNameCondition($sName);
|
|
||||||
abstract public function AddCondition($sFilterCode, $value, $sOpCode = null);
|
|
||||||
/**
|
|
||||||
* Specify a condition on external keys or link sets
|
|
||||||
* @param sAttSpec Can be either an attribute code or extkey->[sAttSpec] or linkset->[sAttSpec] and so on, recursively
|
|
||||||
* Example: infra_list->ci_id->location_id->country
|
|
||||||
* @param value The value to match (can be an array => IN(val1, val2...)
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
abstract public function AddConditionAdvanced($sAttSpec, $value);
|
|
||||||
abstract public function AddCondition_FullText($sFullText);
|
|
||||||
|
|
||||||
abstract public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS);
|
|
||||||
abstract public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode);
|
|
||||||
abstract public function Intersect(DBSearch $oFilter);
|
|
||||||
|
|
||||||
abstract public function SetInternalParams($aParams);
|
|
||||||
abstract public function GetInternalParams();
|
|
||||||
abstract public function GetQueryParams();
|
|
||||||
abstract public function ListConstantFields();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turn the parameters (:xxx) into scalar values in order to easily
|
|
||||||
* serialize a search
|
|
||||||
*/
|
|
||||||
abstract public function ApplyParameters($aArgs);
|
|
||||||
|
|
||||||
public function serialize($bDevelopParams = false, $aContextParams = null)
|
|
||||||
{
|
|
||||||
$sOql = $this->ToOql($bDevelopParams, $aContextParams);
|
|
||||||
return base64_encode(serialize(array($sOql, $this->GetInternalParams(), $this->m_aModifierProperties)));
|
|
||||||
}
|
|
||||||
|
|
||||||
static public function unserialize($sValue)
|
|
||||||
{
|
|
||||||
$aData = unserialize(base64_decode($sValue));
|
|
||||||
$sOql = $aData[0];
|
|
||||||
$aParams = $aData[1];
|
|
||||||
// We've tried to use gzcompress/gzuncompress, but for some specific queries
|
|
||||||
// it was not working at all (See Trac #193)
|
|
||||||
// gzuncompress was issuing a warning "data error" and the return object was null
|
|
||||||
$oRetFilter = self::FromOQL($sOql, $aParams);
|
|
||||||
$oRetFilter->m_aModifierProperties = $aData[2];
|
|
||||||
return $oRetFilter;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract public function ToOQL($bDevelopParams = false, $aContextParams = null);
|
|
||||||
|
|
||||||
static protected $m_aOQLQueries = array();
|
|
||||||
|
|
||||||
// Do not filter out depending on user rights
|
|
||||||
// In particular when we are currently in the process of evaluating the user rights...
|
|
||||||
static public function FromOQL_AllData($sQuery, $aParams = null)
|
|
||||||
{
|
|
||||||
$oRes = self::FromOQL($sQuery, $aParams);
|
|
||||||
$oRes->AllowAllData();
|
|
||||||
return $oRes;
|
|
||||||
}
|
|
||||||
|
|
||||||
static public function FromOQL($sQuery, $aParams = null)
|
|
||||||
{
|
|
||||||
if (empty($sQuery)) return null;
|
|
||||||
|
|
||||||
// Query caching
|
|
||||||
$sQueryId = md5($sQuery);
|
|
||||||
$bOQLCacheEnabled = true;
|
|
||||||
if ($bOQLCacheEnabled)
|
|
||||||
{
|
|
||||||
if (array_key_exists($sQueryId, self::$m_aOQLQueries))
|
|
||||||
{
|
|
||||||
// hit!
|
|
||||||
$oResultFilter = self::$m_aOQLQueries[$sQueryId]->DeepClone();
|
|
||||||
}
|
|
||||||
elseif (self::$m_bUseAPCCache)
|
|
||||||
{
|
|
||||||
// Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter
|
|
||||||
//
|
|
||||||
$sAPCCacheId = 'itop-'.MetaModel::GetEnvironmentId().'-dbsearch-cache-'.$sQueryId;
|
|
||||||
$oKPI = new ExecutionKPI();
|
|
||||||
$result = apc_fetch($sAPCCacheId);
|
|
||||||
$oKPI->ComputeStats('Search APC (fetch)', $sQuery);
|
|
||||||
|
|
||||||
if (is_object($result))
|
|
||||||
{
|
|
||||||
$oResultFilter = $result;
|
|
||||||
self::$m_aOQLQueries[$sQueryId] = $oResultFilter->DeepClone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($oResultFilter))
|
|
||||||
{
|
|
||||||
$oKPI = new ExecutionKPI();
|
|
||||||
|
|
||||||
$oOql = new OqlInterpreter($sQuery);
|
|
||||||
$oOqlQuery = $oOql->ParseQuery();
|
|
||||||
|
|
||||||
$oMetaModel = new ModelReflectionRuntime();
|
|
||||||
$oOqlQuery->Check($oMetaModel, $sQuery); // Exceptions thrown in case of issue
|
|
||||||
|
|
||||||
$oResultFilter = $oOqlQuery->ToDBSearch($sQuery);
|
|
||||||
|
|
||||||
$oKPI->ComputeStats('Parse OQL', $sQuery);
|
|
||||||
|
|
||||||
if ($bOQLCacheEnabled)
|
|
||||||
{
|
|
||||||
self::$m_aOQLQueries[$sQueryId] = $oResultFilter->DeepClone();
|
|
||||||
|
|
||||||
if (self::$m_bUseAPCCache)
|
|
||||||
{
|
|
||||||
$oKPI = new ExecutionKPI();
|
|
||||||
apc_store($sAPCCacheId, $oResultFilter, self::$m_iQueryCacheTTL);
|
|
||||||
$oKPI->ComputeStats('Search APC (store)', $sQueryId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_null($aParams))
|
|
||||||
{
|
|
||||||
$oResultFilter->SetInternalParams($aParams);
|
|
||||||
}
|
|
||||||
return $oResultFilter;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alternative to object mapping: the data are transfered directly into an array
|
|
||||||
// This is 10 times faster than creating a set of objects, and makes sense when optimization is required
|
|
||||||
/**
|
|
||||||
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
|
||||||
*/
|
|
||||||
public function ToDataArray($aColumns = array(), $aOrderBy = array(), $aArgs = array())
|
|
||||||
{
|
|
||||||
$sSQL = $this->MakeSelectQuery($aOrderBy, $aArgs);
|
|
||||||
$resQuery = CMDBSource::Query($sSQL);
|
|
||||||
if (!$resQuery) return;
|
|
||||||
|
|
||||||
if (count($aColumns) == 0)
|
|
||||||
{
|
|
||||||
$aColumns = array_keys(MetaModel::ListAttributeDefs($this->GetClass()));
|
|
||||||
// Add the standard id (as first column)
|
|
||||||
array_unshift($aColumns, 'id');
|
|
||||||
}
|
|
||||||
|
|
||||||
$aQueryCols = CMDBSource::GetColumns($resQuery);
|
|
||||||
|
|
||||||
$sClassAlias = $this->GetClassAlias();
|
|
||||||
$aColMap = array();
|
|
||||||
foreach ($aColumns as $sAttCode)
|
|
||||||
{
|
|
||||||
$sColName = $sClassAlias.$sAttCode;
|
|
||||||
if (in_array($sColName, $aQueryCols))
|
|
||||||
{
|
|
||||||
$aColMap[$sAttCode] = $sColName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$aRes = array();
|
|
||||||
while ($aRow = CMDBSource::FetchArray($resQuery))
|
|
||||||
{
|
|
||||||
$aMappedRow = array();
|
|
||||||
foreach ($aColMap as $sAttCode => $sColName)
|
|
||||||
{
|
|
||||||
$aMappedRow[$sAttCode] = $aRow[$sColName];
|
|
||||||
}
|
|
||||||
$aRes[] = $aMappedRow;
|
|
||||||
}
|
|
||||||
CMDBSource::FreeResult($resQuery);
|
|
||||||
return $aRes;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Construction of the SQL queries
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
protected static $m_aQueryStructCache = array();
|
|
||||||
|
|
||||||
|
|
||||||
public function MakeGroupByQuery($aArgs, $aGroupByExpr, $bExcludeNullValues = false)
|
|
||||||
{
|
|
||||||
if ($bExcludeNullValues)
|
|
||||||
{
|
|
||||||
// Null values are not handled (though external keys set to 0 are allowed)
|
|
||||||
$oQueryFilter = $this->DeepClone();
|
|
||||||
foreach ($aGroupByExpr as $oGroupByExp)
|
|
||||||
{
|
|
||||||
$oNull = new FunctionExpression('ISNULL', array($oGroupByExp));
|
|
||||||
$oNotNull = new BinaryExpression($oNull, '!=', new TrueExpression());
|
|
||||||
$oQueryFilter->AddConditionExpression($oNotNull);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$oQueryFilter = $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
$aAttToLoad = array();
|
|
||||||
$oSQLQuery = $oQueryFilter->GetSQLQuery(array(), $aArgs, $aAttToLoad, null, 0, 0, false, $aGroupByExpr);
|
|
||||||
|
|
||||||
$aScalarArgs = array_merge(MetaModel::PrepareQueryArguments($aArgs), $this->GetInternalParams());
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$bBeautifulSQL = self::$m_bTraceQueries || self::$m_bDebugQuery || self::$m_bIndentQueries;
|
|
||||||
$sRes = $oSQLQuery->RenderGroupBy($aScalarArgs, $bBeautifulSQL);
|
|
||||||
}
|
|
||||||
catch (MissingQueryArgument $e)
|
|
||||||
{
|
|
||||||
// Add some information...
|
|
||||||
$e->addInfo('OQL', $this->ToOQL());
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
$this->AddQueryTraceGroupBy($aArgs, $aGroupByExpr, $sRes);
|
|
||||||
return $sRes;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
|
||||||
*/
|
|
||||||
public function MakeSelectQuery($aOrderBy = array(), $aArgs = array(), $aAttToLoad = null, $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false)
|
|
||||||
{
|
|
||||||
// Check the order by specification, and prefix with the class alias
|
|
||||||
// and make sure that the ordering columns are going to be selected
|
|
||||||
//
|
|
||||||
$sClass = $this->GetClass();
|
|
||||||
$sClassAlias = $this->GetClassAlias();
|
|
||||||
$aOrderSpec = array();
|
|
||||||
foreach ($aOrderBy as $sFieldAlias => $bAscending)
|
|
||||||
{
|
|
||||||
if (!is_bool($bAscending))
|
|
||||||
{
|
|
||||||
throw new CoreException("Wrong direction in ORDER BY spec, found '$bAscending' and expecting a boolean value");
|
|
||||||
}
|
|
||||||
|
|
||||||
$iDotPos = strpos($sFieldAlias, '.');
|
|
||||||
if ($iDotPos === false)
|
|
||||||
{
|
|
||||||
$sAttClass = $sClass;
|
|
||||||
$sAttClassAlias = $sClassAlias;
|
|
||||||
$sAttCode = $sFieldAlias;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sAttClassAlias = substr($sFieldAlias, 0, $iDotPos);
|
|
||||||
$sAttClass = $this->GetClassName($sAttClassAlias);
|
|
||||||
$sAttCode = substr($sFieldAlias, $iDotPos + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($sAttCode != 'id')
|
|
||||||
{
|
|
||||||
MyHelpers::CheckValueInArray('field name in ORDER BY spec', $sAttCode, MetaModel::GetAttributesList($sAttClass));
|
|
||||||
|
|
||||||
$oAttDef = MetaModel::GetAttributeDef($sAttClass, $sAttCode);
|
|
||||||
foreach($oAttDef->GetOrderBySQLExpressions($sAttClassAlias) as $sSQLExpression)
|
|
||||||
{
|
|
||||||
$aOrderSpec[$sSQLExpression] = $bAscending;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$aOrderSpec['`'.$sAttClassAlias.$sAttCode.'`'] = $bAscending;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that the columns used for sorting are present in the loaded columns
|
|
||||||
if (!is_null($aAttToLoad) && !isset($aAttToLoad[$sAttClassAlias][$sAttCode]))
|
|
||||||
{
|
|
||||||
$aAttToLoad[$sAttClassAlias][$sAttCode] = MetaModel::GetAttributeDef($sAttClass, $sAttCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$oSQLQuery = $this->GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount);
|
|
||||||
|
|
||||||
$aScalarArgs = array_merge(MetaModel::PrepareQueryArguments($aArgs), $this->GetInternalParams());
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$bBeautifulSQL = self::$m_bTraceQueries || self::$m_bDebugQuery || self::$m_bIndentQueries;
|
|
||||||
$sRes = $oSQLQuery->RenderSelect($aOrderSpec, $aScalarArgs, $iLimitCount, $iLimitStart, $bGetCount, $bBeautifulSQL);
|
|
||||||
if ($sClassAlias == '_itop_')
|
|
||||||
{
|
|
||||||
IssueLog::Info('SQL Query (_itop_): '.$sRes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (MissingQueryArgument $e)
|
|
||||||
{
|
|
||||||
// Add some information...
|
|
||||||
$e->addInfo('OQL', $this->ToOQL());
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
$this->AddQueryTraceSelect($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $sRes);
|
|
||||||
return $sRes;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected function GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $aGroupByExpr = null)
|
|
||||||
{
|
|
||||||
// Hide objects that are not visible to the current user
|
|
||||||
//
|
|
||||||
$oSearch = $this;
|
|
||||||
if (!$this->IsAllDataAllowed() && !$this->IsDataFiltered())
|
|
||||||
{
|
|
||||||
$oVisibleObjects = UserRights::GetSelectFilter($this->GetClass(), $this->GetModifierProperties('UserRightsGetSelectFilter'));
|
|
||||||
if ($oVisibleObjects === false)
|
|
||||||
{
|
|
||||||
// Make sure this is a valid search object, saying NO for all
|
|
||||||
$oVisibleObjects = DBObjectSearch::FromEmptySet($this->GetClass());
|
|
||||||
}
|
|
||||||
if (is_object($oVisibleObjects))
|
|
||||||
{
|
|
||||||
$oSearch = $this->Intersect($oVisibleObjects);
|
|
||||||
$oSearch->SetDataFiltered();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// should be true at this point, meaning that no additional filtering
|
|
||||||
// is required
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute query modifiers properties (can be set in the search itself, by the context, etc.)
|
|
||||||
//
|
|
||||||
$aModifierProperties = MetaModel::MakeModifierProperties($oSearch);
|
|
||||||
|
|
||||||
// Create a unique cache id
|
|
||||||
//
|
|
||||||
if (self::$m_bQueryCacheEnabled || self::$m_bTraceQueries)
|
|
||||||
{
|
|
||||||
// Need to identify the query
|
|
||||||
$sOqlQuery = $oSearch->ToOql();
|
|
||||||
|
|
||||||
if (count($aModifierProperties))
|
|
||||||
{
|
|
||||||
array_multisort($aModifierProperties);
|
|
||||||
$sModifierProperties = json_encode($aModifierProperties);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sModifierProperties = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$sRawId = $sOqlQuery.$sModifierProperties;
|
|
||||||
if (!is_null($aAttToLoad))
|
|
||||||
{
|
|
||||||
$sRawId .= json_encode($aAttToLoad);
|
|
||||||
}
|
|
||||||
if (!is_null($aGroupByExpr))
|
|
||||||
{
|
|
||||||
foreach($aGroupByExpr as $sAlias => $oExpr)
|
|
||||||
{
|
|
||||||
$sRawId .= 'g:'.$sAlias.'!'.$oExpr->Render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$sRawId .= $bGetCount;
|
|
||||||
$sOqlId = md5($sRawId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sOqlQuery = "SELECTING... ".$oSearch->GetClass();
|
|
||||||
$sOqlId = "query id ? n/a";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Query caching
|
|
||||||
//
|
|
||||||
if (self::$m_bQueryCacheEnabled)
|
|
||||||
{
|
|
||||||
// Warning: using directly the query string as the key to the hash array can FAIL if the string
|
|
||||||
// is long and the differences are only near the end... so it's safer (but not bullet proof?)
|
|
||||||
// to use a hash (like md5) of the string as the key !
|
|
||||||
//
|
|
||||||
// Example of two queries that were found as similar by the hash array:
|
|
||||||
// SELECT SLT JOIN lnkSLTToSLA AS L1 ON L1.slt_id=SLT.id JOIN SLA ON L1.sla_id = SLA.id JOIN lnkContractToSLA AS L2 ON L2.sla_id = SLA.id JOIN CustomerContract ON L2.contract_id = CustomerContract.id WHERE SLT.ticket_priority = 1 AND SLA.service_id = 3 AND SLT.metric = 'TTO' AND CustomerContract.customer_id = 2
|
|
||||||
// and
|
|
||||||
// SELECT SLT JOIN lnkSLTToSLA AS L1 ON L1.slt_id=SLT.id JOIN SLA ON L1.sla_id = SLA.id JOIN lnkContractToSLA AS L2 ON L2.sla_id = SLA.id JOIN CustomerContract ON L2.contract_id = CustomerContract.id WHERE SLT.ticket_priority = 1 AND SLA.service_id = 3 AND SLT.metric = 'TTR' AND CustomerContract.customer_id = 2
|
|
||||||
// the only difference is R instead or O at position 285 (TTR instead of TTO)...
|
|
||||||
//
|
|
||||||
if (array_key_exists($sOqlId, self::$m_aQueryStructCache))
|
|
||||||
{
|
|
||||||
// hit!
|
|
||||||
|
|
||||||
$oSQLQuery = unserialize(serialize(self::$m_aQueryStructCache[$sOqlId]));
|
|
||||||
// Note: cloning is not enough because the subtree is made of objects
|
|
||||||
}
|
|
||||||
elseif (self::$m_bUseAPCCache)
|
|
||||||
{
|
|
||||||
// Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter
|
|
||||||
//
|
|
||||||
$sOqlAPCCacheId = 'itop-'.MetaModel::GetEnvironmentId().'-query-cache-'.$sOqlId;
|
|
||||||
$oKPI = new ExecutionKPI();
|
|
||||||
$result = apc_fetch($sOqlAPCCacheId);
|
|
||||||
$oKPI->ComputeStats('Query APC (fetch)', $sOqlQuery);
|
|
||||||
|
|
||||||
if (is_object($result))
|
|
||||||
{
|
|
||||||
$oSQLQuery = $result;
|
|
||||||
self::$m_aQueryStructCache[$sOqlId] = $oSQLQuery;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($oSQLQuery))
|
|
||||||
{
|
|
||||||
$oKPI = new ExecutionKPI();
|
|
||||||
$oSQLQuery = $oSearch->MakeSQLQuery($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr);
|
|
||||||
$oSQLQuery->SetSourceOQL($sOqlQuery);
|
|
||||||
$oKPI->ComputeStats('MakeSQLQuery', $sOqlQuery);
|
|
||||||
|
|
||||||
if (self::$m_bQueryCacheEnabled)
|
|
||||||
{
|
|
||||||
if (self::$m_bUseAPCCache)
|
|
||||||
{
|
|
||||||
$oKPI = new ExecutionKPI();
|
|
||||||
apc_store($sOqlAPCCacheId, $oSQLQuery, self::$m_iQueryCacheTTL);
|
|
||||||
$oKPI->ComputeStats('Query APC (store)', $sOqlQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
self::$m_aQueryStructCache[$sOqlId] = $oSQLQuery->DeepClone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Join to an additional table, if required...
|
|
||||||
//
|
|
||||||
if ($aExtendedDataSpec != null)
|
|
||||||
{
|
|
||||||
$sTableAlias = '_extended_data_';
|
|
||||||
$aExtendedFields = array();
|
|
||||||
foreach($aExtendedDataSpec['fields'] as $sColumn)
|
|
||||||
{
|
|
||||||
$sColRef = $oSearch->GetClassAlias().'_extdata_'.$sColumn;
|
|
||||||
$aExtendedFields[$sColRef] = new FieldExpressionResolved($sColumn, $sTableAlias);
|
|
||||||
}
|
|
||||||
$oSQLQueryExt = new SQLObjectQuery($aExtendedDataSpec['table'], $sTableAlias, $aExtendedFields);
|
|
||||||
$oSQLQuery->AddInnerJoin($oSQLQueryExt, 'id', $aExtendedDataSpec['join_key'] /*, $sTableAlias*/);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $oSQLQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Cache/Trace/Log queries
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
protected static $m_bDebugQuery = false;
|
|
||||||
protected static $m_aQueriesLog = array();
|
|
||||||
protected static $m_bQueryCacheEnabled = false;
|
|
||||||
protected static $m_bUseAPCCache = false;
|
|
||||||
protected static $m_iQueryCacheTTL = 3600;
|
|
||||||
protected static $m_bTraceQueries = false;
|
|
||||||
protected static $m_bIndentQueries = false;
|
|
||||||
protected static $m_bOptimizeQueries = false;
|
|
||||||
|
|
||||||
public static function StartDebugQuery()
|
|
||||||
{
|
|
||||||
$aBacktrace = debug_backtrace();
|
|
||||||
self::$m_bDebugQuery = true;
|
|
||||||
}
|
|
||||||
public static function StopDebugQuery()
|
|
||||||
{
|
|
||||||
self::$m_bDebugQuery = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function EnableQueryCache($bEnabled, $bUseAPC, $iTimeToLive = 3600)
|
|
||||||
{
|
|
||||||
self::$m_bQueryCacheEnabled = $bEnabled;
|
|
||||||
self::$m_bUseAPCCache = $bUseAPC;
|
|
||||||
self::$m_iQueryCacheTTL = $iTimeToLive;
|
|
||||||
}
|
|
||||||
public static function EnableQueryTrace($bEnabled)
|
|
||||||
{
|
|
||||||
self::$m_bTraceQueries = $bEnabled;
|
|
||||||
}
|
|
||||||
public static function EnableQueryIndentation($bEnabled)
|
|
||||||
{
|
|
||||||
self::$m_bIndentQueries = $bEnabled;
|
|
||||||
}
|
|
||||||
public static function EnableOptimizeQuery($bEnabled)
|
|
||||||
{
|
|
||||||
self::$m_bOptimizeQueries = $bEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected function AddQueryTraceSelect($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $sSql)
|
|
||||||
{
|
|
||||||
if (self::$m_bTraceQueries)
|
|
||||||
{
|
|
||||||
$aQueryData = array(
|
|
||||||
'type' => 'select',
|
|
||||||
'filter' => $this,
|
|
||||||
'order_by' => $aOrderBy,
|
|
||||||
'args' => $aArgs,
|
|
||||||
'att_to_load' => $aAttToLoad,
|
|
||||||
'extended_data_spec' => $aExtendedDataSpec,
|
|
||||||
'limit_count' => $iLimitCount,
|
|
||||||
'limit_start' => $iLimitStart,
|
|
||||||
'is_count' => $bGetCount
|
|
||||||
);
|
|
||||||
$sOql = $this->ToOQL(true, $aArgs);
|
|
||||||
self::AddQueryTrace($aQueryData, $sOql, $sSql);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function AddQueryTraceGroupBy($aArgs, $aGroupByExpr, $sSql)
|
|
||||||
{
|
|
||||||
if (self::$m_bTraceQueries)
|
|
||||||
{
|
|
||||||
$aQueryData = array(
|
|
||||||
'type' => 'group_by',
|
|
||||||
'filter' => $this,
|
|
||||||
'args' => $aArgs,
|
|
||||||
'group_by_expr' => $aGroupByExpr
|
|
||||||
);
|
|
||||||
$sOql = $this->ToOQL(true, $aArgs);
|
|
||||||
self::AddQueryTrace($aQueryData, $sOql, $sSql);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function AddQueryTrace($aQueryData, $sOql, $sSql)
|
|
||||||
{
|
|
||||||
if (self::$m_bTraceQueries)
|
|
||||||
{
|
|
||||||
$sQueryId = md5(serialize($aQueryData));
|
|
||||||
$sMySQLQueryId = md5($sSql);
|
|
||||||
if(!isset(self::$m_aQueriesLog[$sQueryId]))
|
|
||||||
{
|
|
||||||
self::$m_aQueriesLog[$sQueryId]['data'] = serialize($aQueryData);
|
|
||||||
self::$m_aQueriesLog[$sQueryId]['oql'] = $sOql;
|
|
||||||
self::$m_aQueriesLog[$sQueryId]['hits'] = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self::$m_aQueriesLog[$sQueryId]['hits']++;
|
|
||||||
}
|
|
||||||
if(!isset(self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]))
|
|
||||||
{
|
|
||||||
self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]['sql'] = $sSql;
|
|
||||||
self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]['count'] = 1;
|
|
||||||
$iTableCount = count(CMDBSource::ExplainQuery($sSql));
|
|
||||||
self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]['table_count'] = $iTableCount;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]['count']++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function RecordQueryTrace()
|
|
||||||
{
|
|
||||||
if (!self::$m_bTraceQueries) return;
|
|
||||||
|
|
||||||
$iOqlCount = count(self::$m_aQueriesLog);
|
|
||||||
$iSqlCount = 0;
|
|
||||||
foreach (self::$m_aQueriesLog as $sQueryId => $aOqlData)
|
|
||||||
{
|
|
||||||
$iSqlCount += $aOqlData['hits'];
|
|
||||||
}
|
|
||||||
$sHtml = "<h2>Stats on SELECT queries: OQL=$iOqlCount, SQL=$iSqlCount</h2>\n";
|
|
||||||
foreach (self::$m_aQueriesLog as $sQueryId => $aOqlData)
|
|
||||||
{
|
|
||||||
$sOql = $aOqlData['oql'];
|
|
||||||
$sHits = $aOqlData['hits'];
|
|
||||||
|
|
||||||
$sHtml .= "<p><b>$sHits</b> hits for OQL query: $sOql</p>\n";
|
|
||||||
$sHtml .= "<ul id=\"ClassesRelationships\" class=\"treeview\">\n";
|
|
||||||
foreach($aOqlData['queries'] as $aSqlData)
|
|
||||||
{
|
|
||||||
$sQuery = $aSqlData['sql'];
|
|
||||||
$sSqlHits = $aSqlData['count'];
|
|
||||||
$iTableCount = $aSqlData['table_count'];
|
|
||||||
$sHtml .= "<li><b>$sSqlHits</b> hits for SQL ($iTableCount tables): <pre style=\"font-size:60%\">$sQuery</pre></li>\n";
|
|
||||||
}
|
|
||||||
$sHtml .= "</ul>\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
$sLogFile = 'queries.latest';
|
|
||||||
file_put_contents(APPROOT.'data/'.$sLogFile.'.html', $sHtml);
|
|
||||||
|
|
||||||
$sLog = "<?php\n\$aQueriesLog = ".var_export(self::$m_aQueriesLog, true).";";
|
|
||||||
file_put_contents(APPROOT.'data/'.$sLogFile.'.log', $sLog);
|
|
||||||
|
|
||||||
// Cumulate the queries
|
|
||||||
$sAllQueries = APPROOT.'data/queries.log';
|
|
||||||
if (file_exists($sAllQueries))
|
|
||||||
{
|
|
||||||
// Merge the new queries into the existing log
|
|
||||||
include($sAllQueries);
|
|
||||||
foreach (self::$m_aQueriesLog as $sQueryId => $aOqlData)
|
|
||||||
{
|
|
||||||
if (!array_key_exists($sQueryId, $aQueriesLog))
|
|
||||||
{
|
|
||||||
$aQueriesLog[$sQueryId] = $aOqlData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$aQueriesLog = self::$m_aQueriesLog;
|
|
||||||
}
|
|
||||||
$sLog = "<?php\n\$aQueriesLog = ".var_export($aQueriesLog, true).";";
|
|
||||||
file_put_contents($sAllQueries, $sLog);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function DbgTrace($value)
|
|
||||||
{
|
|
||||||
if (!self::$m_bDebugQuery) return;
|
|
||||||
$aBacktrace = debug_backtrace();
|
|
||||||
$iCallStackPos = count($aBacktrace) - self::$m_bDebugQuery;
|
|
||||||
$sIndent = "";
|
|
||||||
for ($i = 0 ; $i < $iCallStackPos ; $i++)
|
|
||||||
{
|
|
||||||
$sIndent .= " .-=^=-. ";
|
|
||||||
}
|
|
||||||
$aCallers = array();
|
|
||||||
foreach($aBacktrace as $aStackInfo)
|
|
||||||
{
|
|
||||||
$aCallers[] = $aStackInfo["function"];
|
|
||||||
}
|
|
||||||
$sCallers = "Callstack: ".implode(', ', $aCallers);
|
|
||||||
$sFunction = "<b title=\"$sCallers\">".$aBacktrace[1]["function"]."</b>";
|
|
||||||
|
|
||||||
if (is_string($value))
|
|
||||||
{
|
|
||||||
echo "$sIndent$sFunction: $value<br/>\n";
|
|
||||||
}
|
|
||||||
else if (is_object($value))
|
|
||||||
{
|
|
||||||
echo "$sIndent$sFunction:\n<pre>\n";
|
|
||||||
print_r($value);
|
|
||||||
echo "</pre>\n";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
echo "$sIndent$sFunction: $value<br/>\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,432 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Copyright (C) 2015 Combodo SARL
|
|
||||||
//
|
|
||||||
// This file is part of iTop.
|
|
||||||
//
|
|
||||||
// iTop is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// iTop is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A union of DBObjectSearches
|
|
||||||
*
|
|
||||||
* @copyright Copyright (C) 2015 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
class DBUnionSearch extends DBSearch
|
|
||||||
{
|
|
||||||
protected $aSearches; // source queries
|
|
||||||
protected $aSelectedClasses; // alias => classes (lowest common ancestors) computed at construction
|
|
||||||
|
|
||||||
public function __construct($aSearches)
|
|
||||||
{
|
|
||||||
if (count ($aSearches) == 0)
|
|
||||||
{
|
|
||||||
throw new CoreException('A DBUnionSearch must be made of at least one search');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->aSearches = array();
|
|
||||||
foreach ($aSearches as $oSearch)
|
|
||||||
{
|
|
||||||
if ($oSearch instanceof DBUnionSearch)
|
|
||||||
{
|
|
||||||
foreach ($oSearch->aSearches as $oSubSearch)
|
|
||||||
{
|
|
||||||
$this->aSearches[] = $oSubSearch->DeepClone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->aSearches[] = $oSearch->DeepClone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1 - Collect all the column/classes
|
|
||||||
$aColumnToClasses = array();
|
|
||||||
foreach ($this->aSearches as $iPos => $oSearch)
|
|
||||||
{
|
|
||||||
$aSelected = array_values($oSearch->GetSelectedClasses());
|
|
||||||
|
|
||||||
if ($iPos != 0)
|
|
||||||
{
|
|
||||||
if (count($aSelected) < count($aColumnToClasses))
|
|
||||||
{
|
|
||||||
throw new Exception('Too few selected classes in the subquery #'.($iPos+1));
|
|
||||||
}
|
|
||||||
if (count($aSelected) > count($aColumnToClasses))
|
|
||||||
{
|
|
||||||
throw new Exception('Too many selected classes in the subquery #'.($iPos+1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($aSelected as $iColumn => $sClass)
|
|
||||||
{
|
|
||||||
$aColumnToClasses[$iColumn][] = $sClass;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2 - Build the index column => alias
|
|
||||||
$oFirstSearch = $this->aSearches[0];
|
|
||||||
$aColumnToAlias = array_keys($oFirstSearch->GetSelectedClasses());
|
|
||||||
|
|
||||||
// 3 - Compute alias => lowest common ancestor
|
|
||||||
$this->aSelectedClasses = array();
|
|
||||||
foreach ($aColumnToClasses as $iColumn => $aClasses)
|
|
||||||
{
|
|
||||||
$sAlias = $aColumnToAlias[$iColumn];
|
|
||||||
$sAncestor = MetaModel::GetLowestCommonAncestor($aClasses);
|
|
||||||
if (is_null($sAncestor))
|
|
||||||
{
|
|
||||||
throw new Exception('Could not find a common ancestor for the column '.($iColumn+1).' (Classes: '.implode(', ', $aClasses).')');
|
|
||||||
}
|
|
||||||
$this->aSelectedClasses[$sAlias] = $sAncestor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetSearches()
|
|
||||||
{
|
|
||||||
return $this->aSearches;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Limited to the selected classes
|
|
||||||
*/
|
|
||||||
public function GetClassName($sAlias)
|
|
||||||
{
|
|
||||||
if (array_key_exists($sAlias, $this->aSelectedClasses))
|
|
||||||
{
|
|
||||||
return $this->aSelectedClasses[$sAlias];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new CoreException("Invalid class alias '$sAlias'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetClass()
|
|
||||||
{
|
|
||||||
return reset($this->aSelectedClasses);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetClassAlias()
|
|
||||||
{
|
|
||||||
reset($this->aSelectedClasses);
|
|
||||||
return key($this->aSelectedClasses);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change the class (only subclasses are supported as of now, because the conditions must fit the new class)
|
|
||||||
* Defaults to the first selected class
|
|
||||||
* Only the selected classes can be changed
|
|
||||||
*/
|
|
||||||
public function ChangeClass($sNewClass, $sAlias = null)
|
|
||||||
{
|
|
||||||
if (is_null($sAlias))
|
|
||||||
{
|
|
||||||
$sAlias = $this->GetClassAlias();
|
|
||||||
}
|
|
||||||
elseif (!array_key_exists($sAlias, $this->aSelectedClasses))
|
|
||||||
{
|
|
||||||
// discard silently - necessary when recursing (??? copied from DBObjectSearch)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1 - identify the impacted column
|
|
||||||
$iColumn = array_search($sAlias, array_keys($this->aSelectedClasses));
|
|
||||||
|
|
||||||
// 2 - change for each search
|
|
||||||
foreach ($this->aSearches as $oSearch)
|
|
||||||
{
|
|
||||||
$aSearchAliases = array_keys($oSearch->GetSelectedClasses());
|
|
||||||
$sSearchAlias = $aSearchAliases[$iColumn];
|
|
||||||
$oSearch->ChangeClass($sNewClass, $sSearchAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3 - record the change
|
|
||||||
$this->aSelectedClasses[$sAlias] = $sNewClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetSelectedClasses()
|
|
||||||
{
|
|
||||||
return $this->aSelectedClasses;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function IsAny()
|
|
||||||
{
|
|
||||||
$bIsAny = true;
|
|
||||||
foreach ($this->aSearches as $oSearch)
|
|
||||||
{
|
|
||||||
if (!$oSearch->IsAny())
|
|
||||||
{
|
|
||||||
$bIsAny = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $bIsAny;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function ResetCondition()
|
|
||||||
{
|
|
||||||
foreach ($this->aSearches as $oSearch)
|
|
||||||
{
|
|
||||||
$oSearch->ResetCondition();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function MergeConditionExpression($oExpression)
|
|
||||||
{
|
|
||||||
$aAliases = array_keys($this->aSelectedClasses);
|
|
||||||
foreach ($this->aSearches as $iSearchIndex => $oSearch)
|
|
||||||
{
|
|
||||||
$oClonedExpression = $oExpression->DeepClone();
|
|
||||||
if ($iSearchIndex != 0)
|
|
||||||
{
|
|
||||||
foreach (array_keys($oSearch->GetSelectedClasses()) as $iColumn => $sSearchAlias)
|
|
||||||
{
|
|
||||||
$oClonedExpression->RenameAlias($aAliases[$iColumn], $sSearchAlias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$oSearch->MergeConditionExpression($oClonedExpression);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function AddConditionExpression($oExpression)
|
|
||||||
{
|
|
||||||
$aAliases = array_keys($this->aSelectedClasses);
|
|
||||||
foreach ($this->aSearches as $iSearchIndex => $oSearch)
|
|
||||||
{
|
|
||||||
$oClonedExpression = $oExpression->DeepClone();
|
|
||||||
if ($iSearchIndex != 0)
|
|
||||||
{
|
|
||||||
foreach (array_keys($oSearch->GetSelectedClasses()) as $iColumn => $sSearchAlias)
|
|
||||||
{
|
|
||||||
$oClonedExpression->RenameAlias($aAliases[$iColumn], $sSearchAlias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$oSearch->AddConditionExpression($oClonedExpression);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function AddNameCondition($sName)
|
|
||||||
{
|
|
||||||
foreach ($this->aSearches as $oSearch)
|
|
||||||
{
|
|
||||||
$oSearch->AddNameCondition($sName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function AddCondition($sFilterCode, $value, $sOpCode = null)
|
|
||||||
{
|
|
||||||
foreach ($this->aSearches as $oSearch)
|
|
||||||
{
|
|
||||||
$oSearch->AddCondition($sFilterCode, $value, $sOpCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify a condition on external keys or link sets
|
|
||||||
* @param sAttSpec Can be either an attribute code or extkey->[sAttSpec] or linkset->[sAttSpec] and so on, recursively
|
|
||||||
* Example: infra_list->ci_id->location_id->country
|
|
||||||
* @param value The value to match (can be an array => IN(val1, val2...)
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function AddConditionAdvanced($sAttSpec, $value)
|
|
||||||
{
|
|
||||||
foreach ($this->aSearches as $oSearch)
|
|
||||||
{
|
|
||||||
$oSearch->AddConditionAdvanced($sAttSpec, $value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function AddCondition_FullText($sFullText)
|
|
||||||
{
|
|
||||||
foreach ($this->aSearches as $oSearch)
|
|
||||||
{
|
|
||||||
$oSearch->AddCondition_FullText($sFullText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS)
|
|
||||||
{
|
|
||||||
foreach ($this->aSearches as $oSearch)
|
|
||||||
{
|
|
||||||
$oSearch->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode)
|
|
||||||
{
|
|
||||||
foreach ($this->aSearches as $oSearch)
|
|
||||||
{
|
|
||||||
$oSearch->AddCondition_ReferencedBy($oFilter, $sForeignExtKeyAttCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function Intersect(DBSearch $oFilter)
|
|
||||||
{
|
|
||||||
$aSearches = array();
|
|
||||||
foreach ($this->aSearches as $oSearch)
|
|
||||||
{
|
|
||||||
$aSearches[] = $oSearch->Intersect($oFilter);
|
|
||||||
}
|
|
||||||
return new DBUnionSearch($aSearches);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function SetInternalParams($aParams)
|
|
||||||
{
|
|
||||||
foreach ($this->aSearches as $oSearch)
|
|
||||||
{
|
|
||||||
$oSearch->SetInternalParams($aParams);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetInternalParams()
|
|
||||||
{
|
|
||||||
$aParams = array();
|
|
||||||
foreach ($this->aSearches as $oSearch)
|
|
||||||
{
|
|
||||||
$aParams = array_merge($oSearch->GetInternalParams(), $aParams);
|
|
||||||
}
|
|
||||||
return $aParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetQueryParams()
|
|
||||||
{
|
|
||||||
$aParams = array();
|
|
||||||
foreach ($this->aSearches as $oSearch)
|
|
||||||
{
|
|
||||||
$aParams = array_merge($oSearch->GetQueryParams(), $aParams);
|
|
||||||
}
|
|
||||||
return $aParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function ListConstantFields()
|
|
||||||
{
|
|
||||||
// Somewhat complex to implement for unions, for a poor benefit
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turn the parameters (:xxx) into scalar values in order to easily
|
|
||||||
* serialize a search
|
|
||||||
*/
|
|
||||||
public function ApplyParameters($aArgs)
|
|
||||||
{
|
|
||||||
foreach ($this->aSearches as $oSearch)
|
|
||||||
{
|
|
||||||
$oSearch->ApplyParameters($aArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overloads for query building
|
|
||||||
*/
|
|
||||||
public function ToOQL($bDevelopParams = false, $aContextParams = null)
|
|
||||||
{
|
|
||||||
$aSubQueries = array();
|
|
||||||
foreach ($this->aSearches as $oSearch)
|
|
||||||
{
|
|
||||||
$aSubQueries[] = $oSearch->ToOQL($bDevelopParams, $aContextParams);
|
|
||||||
}
|
|
||||||
$sRet = implode(' UNION ', $aSubQueries);
|
|
||||||
return $sRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Construction of the SQL queries
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public function MakeDeleteQuery($aArgs = array())
|
|
||||||
{
|
|
||||||
throw new Exception('MakeDeleteQuery is not implemented for the unions!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function MakeUpdateQuery($aValues, $aArgs = array())
|
|
||||||
{
|
|
||||||
throw new Exception('MakeUpdateQuery is not implemented for the unions!');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function MakeSQLQuery($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null)
|
|
||||||
{
|
|
||||||
if (count($this->aSearches) == 1)
|
|
||||||
{
|
|
||||||
return $this->aSearches[0]->MakeSQLQuery($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr);
|
|
||||||
}
|
|
||||||
|
|
||||||
$aSQLQueries = array();
|
|
||||||
$aAliases = array_keys($this->aSelectedClasses);
|
|
||||||
foreach ($this->aSearches as $iSearch => $oSearch)
|
|
||||||
{
|
|
||||||
$aSearchAliases = array_keys($oSearch->GetSelectedClasses());
|
|
||||||
|
|
||||||
// The selected classes from the query build perspective are the lowest common ancestors amongst the various queries
|
|
||||||
// (used when it comes to determine which attributes must be selected)
|
|
||||||
$aSearchSelectedClasses = array();
|
|
||||||
foreach ($aSearchAliases as $iColumn => $sSearchAlias)
|
|
||||||
{
|
|
||||||
$sAlias = $aAliases[$iColumn];
|
|
||||||
$aSearchSelectedClasses[$sSearchAlias] = $this->aSelectedClasses[$sAlias];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_null($aAttToLoad))
|
|
||||||
{
|
|
||||||
$aQueryAttToLoad = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// (Eventually) Transform the aliases
|
|
||||||
$aQueryAttToLoad = array();
|
|
||||||
foreach ($aAttToLoad as $sAlias => $aAttributes)
|
|
||||||
{
|
|
||||||
$iColumn = array_search($sAlias, $aAliases);
|
|
||||||
$sQueryAlias = ($iColumn === false) ? $sAlias : $aSearchAliases[$iColumn];
|
|
||||||
$aQueryAttToLoad[$sQueryAlias] = $aAttributes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_null($aGroupByExpr))
|
|
||||||
{
|
|
||||||
$aQueryGroupByExpr = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Clone (and eventually transform) the group by expressions
|
|
||||||
$aQueryGroupByExpr = array();
|
|
||||||
$aTranslationData = array();
|
|
||||||
$aQueryColumns = array_keys($oSearch->GetSelectedClasses());
|
|
||||||
foreach ($aAliases as $iColumn => $sAlias)
|
|
||||||
{
|
|
||||||
$sQueryAlias = $aQueryColumns[$iColumn];
|
|
||||||
$aTranslationData[$sAlias]['*'] = $sQueryAlias;
|
|
||||||
$aQueryGroupByExpr[$sAlias.'id'] = new FieldExpression('id', $sQueryAlias);
|
|
||||||
}
|
|
||||||
foreach ($aGroupByExpr as $sExpressionAlias => $oExpression)
|
|
||||||
{
|
|
||||||
$aQueryGroupByExpr[$sExpressionAlias] = $oExpression->Translate($aTranslationData, false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$oSubQuery = $oSearch->MakeSQLQuery($aQueryAttToLoad, false, $aModifierProperties, $aQueryGroupByExpr, $aSearchSelectedClasses);
|
|
||||||
$aSQLQueries[] = $oSubQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
$oSQLQuery = new SQLUnionQuery($aSQLQueries, $aGroupByExpr);
|
|
||||||
//MyHelpers::var_dump_html($oSQLQuery, true);
|
|
||||||
//MyHelpers::var_dump_html($oSQLQuery->RenderSelect(), true);
|
|
||||||
if (self::$m_bDebugQuery) $oSQLQuery->DisplayHtml();
|
|
||||||
return $oSQLQuery;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -101,7 +101,6 @@ class DeletionPlan
|
|||||||
}
|
}
|
||||||
if ($aData['mode'] == DEL_MANUAL)
|
if ($aData['mode'] == DEL_MANUAL)
|
||||||
{
|
{
|
||||||
$this->m_aToDelete[$sClass][$iId]['issue'] = $sClass.'::'.$iId.' '.Dict::S('UI:Delete:MustBeDeletedManually');
|
|
||||||
$this->m_bFoundStopper = true;
|
$this->m_bFoundStopper = true;
|
||||||
$this->m_bFoundManualDelete = true;
|
$this->m_bFoundManualDelete = true;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -332,42 +332,6 @@ class EventWebService extends Event
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class EventRestService extends Event
|
|
||||||
{
|
|
||||||
public static function Init()
|
|
||||||
{
|
|
||||||
$aParams = array
|
|
||||||
(
|
|
||||||
"category" => "core/cmdb,view_in_gui",
|
|
||||||
"key_type" => "autoincrement",
|
|
||||||
"name_attcode" => "",
|
|
||||||
"state_attcode" => "",
|
|
||||||
"reconc_keys" => array(),
|
|
||||||
"db_table" => "priv_event_restservice",
|
|
||||||
"db_key_field" => "id",
|
|
||||||
"db_finalclass_field" => "",
|
|
||||||
"display_template" => "",
|
|
||||||
"order_by_default" => array('date' => false)
|
|
||||||
);
|
|
||||||
MetaModel::Init_Params($aParams);
|
|
||||||
MetaModel::Init_InheritAttributes();
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeString("operation", array("allowed_values"=>null, "sql"=>"operation", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeString("version", array("allowed_values"=>null, "sql"=>"version", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeText("json_input", array("allowed_values"=>null, "sql"=>"json_input", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
|
||||||
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeInteger("code", array("allowed_values"=>null, "sql"=>"code", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeText("json_output", array("allowed_values"=>null, "sql"=>"json_output", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeString("provider", array("allowed_values"=>null, "sql"=>"provider", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
|
||||||
|
|
||||||
// Display lists
|
|
||||||
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'operation', 'version', 'json_input', 'message', 'code', 'json_output', 'provider')); // Attributes to be displayed for the complete details
|
|
||||||
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'operation', 'message')); // Attributes to be displayed for a list
|
|
||||||
// Search criteria
|
|
||||||
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
|
|
||||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class EventLoginUsage extends Event
|
class EventLoginUsage extends Event
|
||||||
{
|
{
|
||||||
public static function Init()
|
public static function Init()
|
||||||
|
|||||||
@@ -1,274 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Copyright (C) 2015 Combodo SARL
|
|
||||||
//
|
|
||||||
// This file is part of iTop.
|
|
||||||
//
|
|
||||||
// iTop is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// iTop is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bulk export: Excel (xlsx) export
|
|
||||||
*
|
|
||||||
* @copyright Copyright (C) 2015 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
require_once(APPROOT.'application/xlsxwriter.class.php');
|
|
||||||
|
|
||||||
class ExcelBulkExport extends TabularBulkExport
|
|
||||||
{
|
|
||||||
protected $sData;
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->aStatusInfo['status'] = 'not_started';
|
|
||||||
$this->aStatusInfo['position'] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function Cleanup()
|
|
||||||
{
|
|
||||||
@unlink($this->aStatusInfo['tmp_file']);
|
|
||||||
parent::Cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function DisplayUsage(Page $oP)
|
|
||||||
{
|
|
||||||
$oP->p(" * xlsx format options:");
|
|
||||||
$oP->p(" *\tfields: the comma separated list of field codes to export (e.g: name,org_id,service_name...).");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function EnumFormParts()
|
|
||||||
{
|
|
||||||
return array_merge(parent::EnumFormParts(), array('interactive_fields_xlsx' => array('interactive_fields_xlsx')));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function DisplayFormPart(WebPage $oP, $sPartId)
|
|
||||||
{
|
|
||||||
switch($sPartId)
|
|
||||||
{
|
|
||||||
case 'interactive_fields_xlsx':
|
|
||||||
$this->GetInteractiveFieldsWidget($oP, 'interactive_fields_xlsx');
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return parent:: DisplayFormPart($oP, $sPartId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function SuggestField($sClass, $sAttCode)
|
|
||||||
{
|
|
||||||
switch($sAttCode)
|
|
||||||
{
|
|
||||||
case 'id': // replace 'id' by 'friendlyname'
|
|
||||||
$sAttCode = 'friendlyname';
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
|
||||||
if ($oAttDef instanceof AttributeExternalKey)
|
|
||||||
{
|
|
||||||
$sAttCode .= '_friendlyname';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::SuggestField($sClass, $sAttCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function GetSampleData($oObj, $sAttCode)
|
|
||||||
{
|
|
||||||
return '<div class="text-preview">'.htmlentities($this->GetValue($oObj, $sAttCode), ENT_QUOTES, 'UTF-8').'</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function GetValue($oObj, $sAttCode)
|
|
||||||
{
|
|
||||||
switch($sAttCode)
|
|
||||||
{
|
|
||||||
case 'id':
|
|
||||||
$sRet = $oObj->GetKey();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$value = $oObj->Get($sAttCode);
|
|
||||||
if ($value instanceOf ormCaseLog)
|
|
||||||
{
|
|
||||||
// Extract the case log as text and remove the "===" which make Excel think that the cell contains a formula the next time you edit it!
|
|
||||||
$sRet = trim(preg_replace('/========== ([^=]+) ============/', '********** $1 ************', $value->GetText()));
|
|
||||||
}
|
|
||||||
else if ($value instanceOf DBObjectSet)
|
|
||||||
{
|
|
||||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
|
|
||||||
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
|
|
||||||
$sRet = $oAttDef->GetEditValue($value, $oObj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $sRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetHeader()
|
|
||||||
{
|
|
||||||
$oSet = new DBObjectSet($this->oSearch);
|
|
||||||
$this->aStatusInfo['status'] = 'retrieving';
|
|
||||||
$this->aStatusInfo['tmp_file'] = $this->MakeTmpFile('data');
|
|
||||||
$this->aStatusInfo['position'] = 0;
|
|
||||||
$this->aStatusInfo['total'] = $oSet->Count();
|
|
||||||
|
|
||||||
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
|
|
||||||
{
|
|
||||||
$sExtendedAttCode = $aFieldSpec['sFieldSpec'];
|
|
||||||
$sAttCode = $aFieldSpec['sAttCode'];
|
|
||||||
$sColLabel = $aFieldSpec['sColLabel'];
|
|
||||||
|
|
||||||
switch($sAttCode)
|
|
||||||
{
|
|
||||||
case 'id':
|
|
||||||
$sType = '0';
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$oAttDef = MetaModel::GetAttributeDef($aFieldSpec['sClass'], $aFieldSpec['sAttCode']);
|
|
||||||
$sType = 'string';
|
|
||||||
if($oAttDef instanceof AttributeDateTime)
|
|
||||||
{
|
|
||||||
$sType = 'datetime';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$aTableHeaders[] = array('label' => $sColLabel, 'type' => $sType);
|
|
||||||
}
|
|
||||||
|
|
||||||
$sRow = json_encode($aTableHeaders);
|
|
||||||
$hFile = @fopen($this->aStatusInfo['tmp_file'], 'ab');
|
|
||||||
if ($hFile === false)
|
|
||||||
{
|
|
||||||
throw new Exception('ExcelBulkExport: Failed to open temporary data file: "'.$this->aStatusInfo['tmp_file'].'" for writing.');
|
|
||||||
}
|
|
||||||
fwrite($hFile, $sRow."\n");
|
|
||||||
fclose($hFile);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetNextChunk(&$aStatus)
|
|
||||||
{
|
|
||||||
$sRetCode = 'run';
|
|
||||||
$iPercentage = 0;
|
|
||||||
|
|
||||||
$hFile = fopen($this->aStatusInfo['tmp_file'], 'ab');
|
|
||||||
|
|
||||||
$oSet = new DBObjectSet($this->oSearch);
|
|
||||||
$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
|
|
||||||
$this->OptimizeColumnLoad($oSet);
|
|
||||||
|
|
||||||
$iCount = 0;
|
|
||||||
$iPreviousTimeLimit = ini_get('max_execution_time');
|
|
||||||
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
|
|
||||||
while($aRow = $oSet->FetchAssoc())
|
|
||||||
{
|
|
||||||
set_time_limit($iLoopTimeLimit);
|
|
||||||
$aData = array();
|
|
||||||
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
|
|
||||||
{
|
|
||||||
$sAlias = $aFieldSpec['sAlias'];
|
|
||||||
$sAttCode = $aFieldSpec['sAttCode'];
|
|
||||||
|
|
||||||
$oObj = $aRow[$sAlias];
|
|
||||||
$sField = '';
|
|
||||||
if ($oObj)
|
|
||||||
{
|
|
||||||
$sField = $this->GetValue($oObj, $sAttCode);
|
|
||||||
}
|
|
||||||
$aData[] = $sField;
|
|
||||||
}
|
|
||||||
fwrite($hFile, json_encode($aData)."\n");
|
|
||||||
$iCount++;
|
|
||||||
}
|
|
||||||
set_time_limit($iPreviousTimeLimit);
|
|
||||||
$this->aStatusInfo['position'] += $this->iChunkSize;
|
|
||||||
if ($this->aStatusInfo['total'] == 0)
|
|
||||||
{
|
|
||||||
$iPercentage = 100;
|
|
||||||
$sRetCode = 'done'; // Next phase (GetFooter) will be to build the xlsx file
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$iPercentage = floor(min(100.0, 100.0*$this->aStatusInfo['position']/$this->aStatusInfo['total']));
|
|
||||||
}
|
|
||||||
if ($iCount < $this->iChunkSize)
|
|
||||||
{
|
|
||||||
$sRetCode = 'done';
|
|
||||||
}
|
|
||||||
$aStatus = array('code' => $sRetCode, 'message' => Dict::S('Core:BulkExport:RetrievingData'), 'percentage' => $iPercentage);
|
|
||||||
return ''; // The actual XLSX file is built in GetFooter();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetFooter()
|
|
||||||
{
|
|
||||||
$hFile = @fopen($this->aStatusInfo['tmp_file'], 'rb');
|
|
||||||
if ($hFile === false)
|
|
||||||
{
|
|
||||||
throw new Exception('ExcelBulkExport: Failed to open temporary data file: "'.$this->aStatusInfo['tmp_file'].'" for reading.');
|
|
||||||
}
|
|
||||||
$sHeaders = fgets($hFile);
|
|
||||||
$aHeaders = json_decode($sHeaders, true);
|
|
||||||
|
|
||||||
$aData = array();
|
|
||||||
while($sLine = fgets($hFile))
|
|
||||||
{
|
|
||||||
$aRow = json_decode($sLine);
|
|
||||||
$aData[] = $aRow;
|
|
||||||
}
|
|
||||||
fclose($hFile);
|
|
||||||
|
|
||||||
$fStartExcel = microtime(true);
|
|
||||||
$writer = new XLSXWriter();
|
|
||||||
$writer->setAuthor(UserRights::GetUserFriendlyName());
|
|
||||||
$aHeaderTypes = array();
|
|
||||||
$aHeaderNames = array();
|
|
||||||
foreach($aHeaders as $Header)
|
|
||||||
{
|
|
||||||
$aHeaderNames[] = $Header['label'];
|
|
||||||
$aHeaderTypes[] = $Header['type'];
|
|
||||||
}
|
|
||||||
$writer->writeSheet($aData,'Sheet1', $aHeaderTypes, $aHeaderNames);
|
|
||||||
$fExcelTime = microtime(true) - $fStartExcel;
|
|
||||||
//$this->aStatistics['excel_build_duration'] = $fExcelTime;
|
|
||||||
|
|
||||||
$fTime = microtime(true);
|
|
||||||
$data = $writer->writeToString();
|
|
||||||
$fExcelSaveTime = microtime(true) - $fTime;
|
|
||||||
//$this->aStatistics['excel_write_duration'] = $fExcelSaveTime;
|
|
||||||
|
|
||||||
@unlink($this->aStatusInfo['tmp_file']);
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetMimeType()
|
|
||||||
{
|
|
||||||
return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetFileExtension()
|
|
||||||
{
|
|
||||||
return 'xlsx';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetSupportedFormats()
|
|
||||||
{
|
|
||||||
return array('xlsx' => Dict::S('Core:BulkExport:XLSXFormat'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,195 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Copyright (C) 2015 Combodo SARL
|
|
||||||
//
|
|
||||||
// This file is part of iTop.
|
|
||||||
//
|
|
||||||
// iTop is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// iTop is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bulk export: HTML export
|
|
||||||
*
|
|
||||||
* @copyright Copyright (C) 2015 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
class HTMLBulkExport extends TabularBulkExport
|
|
||||||
{
|
|
||||||
public function DisplayUsage(Page $oP)
|
|
||||||
{
|
|
||||||
$oP->p(" * html format options:");
|
|
||||||
$oP->p(" *\tfields: (mandatory) the comma separated list of field codes to export (e.g: name,org_id,service_name...).");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function EnumFormParts()
|
|
||||||
{
|
|
||||||
return array_merge(parent::EnumFormParts(), array('interactive_fields_html' => array('interactive_fields_html')));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function DisplayFormPart(WebPage $oP, $sPartId)
|
|
||||||
{
|
|
||||||
switch($sPartId)
|
|
||||||
{
|
|
||||||
case 'interactive_fields_html':
|
|
||||||
$this->GetInteractiveFieldsWidget($oP, 'interactive_fields_html');
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return parent:: DisplayFormPart($oP, $sPartId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function GetSampleData($oObj, $sAttCode)
|
|
||||||
{
|
|
||||||
return $this->GetValue($oObj, $sAttCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function GetValue($oObj, $sAttCode)
|
|
||||||
{
|
|
||||||
switch($sAttCode)
|
|
||||||
{
|
|
||||||
case 'id':
|
|
||||||
$sRet = $oObj->GetHyperlink();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$value = $oObj->Get($sAttCode);
|
|
||||||
if ($value instanceof ormCaseLog)
|
|
||||||
{
|
|
||||||
$sRet = $value->GetAsSimpleHtml();
|
|
||||||
}
|
|
||||||
elseif ($value instanceof ormStopWatch)
|
|
||||||
{
|
|
||||||
$sRet = $value->GetTimeSpent();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sRet = $oObj->GetAsHtml($sAttCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $sRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetHeader()
|
|
||||||
{
|
|
||||||
$sData = '';
|
|
||||||
|
|
||||||
$oSet = new DBObjectSet($this->oSearch);
|
|
||||||
$this->aStatusInfo['status'] = 'running';
|
|
||||||
$this->aStatusInfo['position'] = 0;
|
|
||||||
$this->aStatusInfo['total'] = $oSet->Count();
|
|
||||||
|
|
||||||
$sData .= "<table class=\"listResults\">\n";
|
|
||||||
$sData .= "<thead>\n";
|
|
||||||
$sData .= "<tr>\n";
|
|
||||||
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
|
|
||||||
{
|
|
||||||
$sData .= "<th>".$aFieldSpec['sColLabel']."</th>\n";
|
|
||||||
}
|
|
||||||
$sData .= "</tr>\n";
|
|
||||||
$sData .= "</thead>\n";
|
|
||||||
$sData .= "<tbody>\n";
|
|
||||||
return $sData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetNextChunk(&$aStatus)
|
|
||||||
{
|
|
||||||
$sRetCode = 'run';
|
|
||||||
$iPercentage = 0;
|
|
||||||
|
|
||||||
$oSet = new DBObjectSet($this->oSearch);
|
|
||||||
$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
|
|
||||||
$this->OptimizeColumnLoad($oSet);
|
|
||||||
|
|
||||||
$sFirstAlias = $this->oSearch->GetClassAlias();
|
|
||||||
|
|
||||||
$iCount = 0;
|
|
||||||
$sData = '';
|
|
||||||
$iPreviousTimeLimit = ini_get('max_execution_time');
|
|
||||||
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
|
|
||||||
while($aRow = $oSet->FetchAssoc())
|
|
||||||
{
|
|
||||||
set_time_limit($iLoopTimeLimit);
|
|
||||||
$oMainObj = $aRow[$sFirstAlias];
|
|
||||||
$sHilightClass = '';
|
|
||||||
if ($oMainObj)
|
|
||||||
{
|
|
||||||
$sHilightClass = $aRow[$sFirstAlias]->GetHilightClass();
|
|
||||||
}
|
|
||||||
if ($sHilightClass != '')
|
|
||||||
{
|
|
||||||
$sData .= "<tr class=\"$sHilightClass\">";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sData .= "<tr>";
|
|
||||||
}
|
|
||||||
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
|
|
||||||
{
|
|
||||||
$sAlias = $aFieldSpec['sAlias'];
|
|
||||||
$sAttCode = $aFieldSpec['sAttCode'];
|
|
||||||
|
|
||||||
$oObj = $aRow[$sAlias];
|
|
||||||
$sField = '';
|
|
||||||
if ($oObj)
|
|
||||||
{
|
|
||||||
$sField = $this->GetValue($oObj, $sAttCode);
|
|
||||||
}
|
|
||||||
$sValue = ($sField === '') ? ' ' : $sField;
|
|
||||||
$sData .= "<td>$sValue</td>";
|
|
||||||
}
|
|
||||||
$sData .= "</tr>";
|
|
||||||
$iCount++;
|
|
||||||
}
|
|
||||||
set_time_limit($iPreviousTimeLimit);
|
|
||||||
$this->aStatusInfo['position'] += $this->iChunkSize;
|
|
||||||
if ($this->aStatusInfo['total'] == 0)
|
|
||||||
{
|
|
||||||
$iPercentage = 100;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$iPercentage = floor(min(100.0, 100.0*$this->aStatusInfo['position']/$this->aStatusInfo['total']));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($iCount < $this->iChunkSize)
|
|
||||||
{
|
|
||||||
$sRetCode = 'done';
|
|
||||||
}
|
|
||||||
|
|
||||||
$aStatus = array('code' => $sRetCode, 'message' => Dict::S('Core:BulkExport:RetrievingData'), 'percentage' => $iPercentage);
|
|
||||||
return $sData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetFooter()
|
|
||||||
{
|
|
||||||
$sData = "</tbody>\n";
|
|
||||||
$sData .= "</table>\n";
|
|
||||||
return $sData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetSupportedFormats()
|
|
||||||
{
|
|
||||||
return array('html' => Dict::S('Core:BulkExport:HTMLFormat'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetMimeType()
|
|
||||||
{
|
|
||||||
return 'text/html';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetFileExtension()
|
|
||||||
{
|
|
||||||
return 'html';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -59,11 +59,8 @@ class FileLog
|
|||||||
$hLogFile = @fopen($this->m_sFile, 'a');
|
$hLogFile = @fopen($this->m_sFile, 'a');
|
||||||
if ($hLogFile !== false)
|
if ($hLogFile !== false)
|
||||||
{
|
{
|
||||||
flock($hLogFile, LOCK_EX);
|
|
||||||
$sDate = date('Y-m-d H:i:s');
|
$sDate = date('Y-m-d H:i:s');
|
||||||
fwrite($hLogFile, "$sDate | $sText\n");
|
fwrite($hLogFile, "$sDate | $sText\n");
|
||||||
fflush($hLogFile);
|
|
||||||
flock($hLogFile, LOCK_UN);
|
|
||||||
fclose($hLogFile);
|
fclose($hLogFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -23,21 +23,7 @@
|
|||||||
* @copyright Copyright (C) 2013 Combodo SARL
|
* @copyright Copyright (C) 2013 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* Exclude the parent class from the list
|
|
||||||
*
|
|
||||||
* @package iTopORM
|
|
||||||
*/
|
|
||||||
define('ENUM_CHILD_CLASSES_EXCLUDETOP', 1);
|
|
||||||
/**
|
|
||||||
* Include the parent class in the list
|
|
||||||
*
|
|
||||||
* @package iTopORM
|
|
||||||
*/
|
|
||||||
define('ENUM_CHILD_CLASSES_ALL', 2);
|
|
||||||
|
|
||||||
|
|
||||||
abstract class ModelReflection
|
abstract class ModelReflection
|
||||||
{
|
{
|
||||||
abstract public function GetClassIcon($sClass, $bImgTag = true);
|
abstract public function GetClassIcon($sClass, $bImgTag = true);
|
||||||
@@ -76,9 +62,6 @@ abstract class ModelReflection
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract public function GetIconSelectionField($sCode, $sLabel = '', $defaultValue = '');
|
abstract public function GetIconSelectionField($sCode, $sLabel = '', $defaultValue = '');
|
||||||
|
|
||||||
abstract public function GetRootClass($sClass);
|
|
||||||
abstract public function EnumChildClasses($sClass, $iOption = ENUM_CHILD_CLASSES_EXCLUDETOP);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class QueryReflection
|
abstract class QueryReflection
|
||||||
@@ -86,7 +69,7 @@ abstract class QueryReflection
|
|||||||
/**
|
/**
|
||||||
* Throws an exception in case of an invalid syntax
|
* Throws an exception in case of an invalid syntax
|
||||||
*/
|
*/
|
||||||
abstract public function __construct($sOQL, ModelReflection $oModelReflection);
|
abstract public function __construct($sOQL);
|
||||||
|
|
||||||
abstract public function GetClass();
|
abstract public function GetClass();
|
||||||
abstract public function GetClassAlias();
|
abstract public function GetClassAlias();
|
||||||
@@ -239,7 +222,7 @@ class ModelReflectionRuntime extends ModelReflection
|
|||||||
|
|
||||||
public function GetQuery($sOQL)
|
public function GetQuery($sOQL)
|
||||||
{
|
{
|
||||||
return new QueryReflectionRuntime($sOQL, $this);
|
return new QueryReflectionRuntime($sOQL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function DictString($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
|
public function DictString($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
|
||||||
@@ -251,16 +234,6 @@ class ModelReflectionRuntime extends ModelReflection
|
|||||||
{
|
{
|
||||||
return new RunTimeIconSelectionField($sCode, $sLabel, $defaultValue);
|
return new RunTimeIconSelectionField($sCode, $sLabel, $defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetRootClass($sClass)
|
|
||||||
{
|
|
||||||
return MetaModel::GetRootClass($sClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function EnumChildClasses($sClass, $iOption = ENUM_CHILD_CLASSES_EXCLUDETOP)
|
|
||||||
{
|
|
||||||
return MetaModel::EnumChildClasses($sClass, $iOption);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -271,7 +244,7 @@ class QueryReflectionRuntime extends QueryReflection
|
|||||||
/**
|
/**
|
||||||
* throws an exception in case of a wrong syntax
|
* throws an exception in case of a wrong syntax
|
||||||
*/
|
*/
|
||||||
public function __construct($sOQL, ModelReflection $oModelReflection)
|
public function __construct($sOQL)
|
||||||
{
|
{
|
||||||
$this->oFilter = DBObjectSearch::FromOQL($sOQL);
|
$this->oFilter = DBObjectSearch::FromOQL($sOQL);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2013-2016 Combodo SARL
|
// Copyright (C) 2013 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
* Relies on MySQL locks because the API sem_get is not always present in the
|
* Relies on MySQL locks because the API sem_get is not always present in the
|
||||||
* installed PHP.
|
* installed PHP.
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2013-2016 Combodo SARL
|
* @copyright Copyright (C) 2013 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
class iTopMutex
|
class iTopMutex
|
||||||
@@ -37,19 +37,8 @@ class iTopMutex
|
|||||||
public function __construct($sName, $sDBHost = null, $sDBUser = null, $sDBPwd = null)
|
public function __construct($sName, $sDBHost = null, $sDBUser = null, $sDBPwd = null)
|
||||||
{
|
{
|
||||||
// Compute the name of a lock for mysql
|
// Compute the name of a lock for mysql
|
||||||
// Note: names are server-wide!!! So let's make the name specific to this iTop instance
|
// Note: the name is server-wide!!!
|
||||||
$oConfig = utils::GetConfig(); // Will return an empty config when called during the setup
|
|
||||||
$sDBName = $oConfig->GetDBName();
|
|
||||||
$sDBSubname = $oConfig->GetDBSubname();
|
|
||||||
$this->sName = 'itop.'.$sName;
|
$this->sName = 'itop.'.$sName;
|
||||||
if (substr($sName, -strlen($sDBName.$sDBSubname)) != $sDBName.$sDBSubname)
|
|
||||||
{
|
|
||||||
// If the name supplied already ends with the expected suffix
|
|
||||||
// don't add it twice, since the setup may try to detect an already
|
|
||||||
// running cron job by its mutex, without knowing if the config already exists or not
|
|
||||||
$this->sName .= $sDBName.$sDBSubname;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->bLocked = false; // Not yet locked
|
$this->bLocked = false; // Not yet locked
|
||||||
|
|
||||||
if (!array_key_exists($this->sName, self::$aAcquiredLocks))
|
if (!array_key_exists($this->sName, self::$aAcquiredLocks))
|
||||||
@@ -59,6 +48,7 @@ class iTopMutex
|
|||||||
|
|
||||||
// It is a MUST to create a dedicated session each time a lock is required, because
|
// It is a MUST to create a dedicated session each time a lock is required, because
|
||||||
// using GET_LOCK anytime on the same session will RELEASE the current and unique session lock (known issue)
|
// using GET_LOCK anytime on the same session will RELEASE the current and unique session lock (known issue)
|
||||||
|
$oConfig = utils::GetConfig();
|
||||||
$sDBHost = is_null($sDBHost) ? $oConfig->GetDBHost() : $sDBHost;
|
$sDBHost = is_null($sDBHost) ? $oConfig->GetDBHost() : $sDBHost;
|
||||||
$sDBUser = is_null($sDBUser) ? $oConfig->GetDBUser() : $sDBUser;
|
$sDBUser = is_null($sDBUser) ? $oConfig->GetDBUser() : $sDBUser;
|
||||||
$sDBPwd = is_null($sDBPwd) ? $oConfig->GetDBPwd() : $sDBPwd;
|
$sDBPwd = is_null($sDBPwd) ? $oConfig->GetDBPwd() : $sDBPwd;
|
||||||
@@ -131,43 +121,7 @@ class iTopMutex
|
|||||||
$this->bLocked = true;
|
$this->bLocked = true;
|
||||||
self::$aAcquiredLocks[$this->sName]++;
|
self::$aAcquiredLocks[$this->sName]++;
|
||||||
}
|
}
|
||||||
if (($res !== '1') && ($res !== '0'))
|
return ($res === '1');
|
||||||
{
|
|
||||||
$sMsg = 'GET_LOCK('.$this->sName.', 0) returned: '.var_export($res, true).'. Expected values are: 0, 1 or null';
|
|
||||||
IssueLog::Error($sMsg);
|
|
||||||
throw new Exception($sMsg);
|
|
||||||
}
|
|
||||||
return ($res !== '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the mutex is locked WITHOUT TRYING TO ACQUIRE IT
|
|
||||||
* @returns bool True if the mutex is in use, false otherwise
|
|
||||||
*/
|
|
||||||
public function IsLocked()
|
|
||||||
{
|
|
||||||
if ($this->bLocked)
|
|
||||||
{
|
|
||||||
return true; // Already acquired
|
|
||||||
}
|
|
||||||
if (self::$aAcquiredLocks[$this->sName] > 0)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$res = $this->QueryToScalar("SELECT IS_FREE_LOCK('".$this->sName."')"); // IS_FREE_LOCK detects some error cases that IS_USED_LOCK do not detect
|
|
||||||
if (is_null($res))
|
|
||||||
{
|
|
||||||
$sMsg = "MySQL Error, IS_FREE_LOCK('".$this->sName."') returned null. Error (".mysqli_errno($this->hDBLink).") = '".mysqli_error($this->hDBLink)."'";
|
|
||||||
IssueLog::Error($sMsg);
|
|
||||||
throw new Exception($sMsg);
|
|
||||||
}
|
|
||||||
else if ($res == '1')
|
|
||||||
{
|
|
||||||
// Lock is free
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
4
core/oql/build.bash
Executable file
4
core/oql/build.bash
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
php /usr/share/php/PHP/LexerGenerator/cli.php oql-lexer.plex
|
||||||
|
php /usr/share/php/PHP/ParserGenerator/cli.php oql-parser.y
|
||||||
|
|
||||||
3
core/oql/build.cmd
Normal file
3
core/oql/build.cmd
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
c:\itop\php-5.2.3\php.exe -q "C:\itop\PHP-5.2.3\PEAR\PHP\LexerGenerator\cli.php" oql-lexer.plex
|
||||||
|
c:\itop\php-5.2.3\php.exe -q "C:\itop\PHP-5.2.3\PEAR\PHP\ParserGenerator\cli.php" oql-parser.y
|
||||||
|
pause
|
||||||
@@ -1,937 +0,0 @@
|
|||||||
<?php
|
|
||||||
/* Driver template for the PHP_ParserGenerator parser generator. (PHP port of LEMON)
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This can be used to store both the string representation of
|
|
||||||
* a token, and any useful meta-data associated with the token.
|
|
||||||
*
|
|
||||||
* meta-data should be stored as an array
|
|
||||||
*/
|
|
||||||
class ParseyyToken implements ArrayAccess
|
|
||||||
{
|
|
||||||
public $string = '';
|
|
||||||
public $metadata = array();
|
|
||||||
|
|
||||||
function __construct($s, $m = array())
|
|
||||||
{
|
|
||||||
if ($s instanceof ParseyyToken) {
|
|
||||||
$this->string = $s->string;
|
|
||||||
$this->metadata = $s->metadata;
|
|
||||||
} else {
|
|
||||||
$this->string = (string) $s;
|
|
||||||
if ($m instanceof ParseyyToken) {
|
|
||||||
$this->metadata = $m->metadata;
|
|
||||||
} elseif (is_array($m)) {
|
|
||||||
$this->metadata = $m;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function __toString()
|
|
||||||
{
|
|
||||||
return $this->string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function offsetExists($offset)
|
|
||||||
{
|
|
||||||
return isset($this->metadata[$offset]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function offsetGet($offset)
|
|
||||||
{
|
|
||||||
return $this->metadata[$offset];
|
|
||||||
}
|
|
||||||
|
|
||||||
function offsetSet($offset, $value)
|
|
||||||
{
|
|
||||||
if ($offset === null) {
|
|
||||||
if (isset($value[0])) {
|
|
||||||
$x = ($value instanceof ParseyyToken) ?
|
|
||||||
$value->metadata : $value;
|
|
||||||
$this->metadata = array_merge($this->metadata, $x);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$offset = count($this->metadata);
|
|
||||||
}
|
|
||||||
if ($value === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ($value instanceof ParseyyToken) {
|
|
||||||
if ($value->metadata) {
|
|
||||||
$this->metadata[$offset] = $value->metadata;
|
|
||||||
}
|
|
||||||
} elseif ($value) {
|
|
||||||
$this->metadata[$offset] = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function offsetUnset($offset)
|
|
||||||
{
|
|
||||||
unset($this->metadata[$offset]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The following structure represents a single element of the
|
|
||||||
* parser's stack. Information stored includes:
|
|
||||||
*
|
|
||||||
* + The state number for the parser at this level of the stack.
|
|
||||||
*
|
|
||||||
* + The value of the token stored at this level of the stack.
|
|
||||||
* (In other words, the "major" token.)
|
|
||||||
*
|
|
||||||
* + The semantic value stored at this level of the stack. This is
|
|
||||||
* the information used by the action routines in the grammar.
|
|
||||||
* It is sometimes called the "minor" token.
|
|
||||||
*/
|
|
||||||
class ParseyyStackEntry
|
|
||||||
{
|
|
||||||
public $stateno; /* The state-number */
|
|
||||||
public $major; /* The major token value. This is the code
|
|
||||||
** number for the token at this stack level */
|
|
||||||
public $minor; /* The user-supplied minor token value. This
|
|
||||||
** is the value of the token */
|
|
||||||
};
|
|
||||||
|
|
||||||
// code external to the class is included here
|
|
||||||
%%
|
|
||||||
|
|
||||||
// declare_class is output here
|
|
||||||
%%
|
|
||||||
{
|
|
||||||
/* First off, code is included which follows the "include_class" declaration
|
|
||||||
** in the input file. */
|
|
||||||
%%
|
|
||||||
|
|
||||||
/* Next is all token values, as class constants
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** These constants (all generated automatically by the parser generator)
|
|
||||||
** specify the various kinds of tokens (terminals) that the parser
|
|
||||||
** understands.
|
|
||||||
**
|
|
||||||
** Each symbol here is a terminal symbol in the grammar.
|
|
||||||
*/
|
|
||||||
%%
|
|
||||||
|
|
||||||
/* Next are that tables used to determine what action to take based on the
|
|
||||||
** current state and lookahead token. These tables are used to implement
|
|
||||||
** functions that take a state number and lookahead value and return an
|
|
||||||
** action integer.
|
|
||||||
**
|
|
||||||
** Suppose the action integer is N. Then the action is determined as
|
|
||||||
** follows
|
|
||||||
**
|
|
||||||
** 0 <= N < self::YYNSTATE Shift N. That is,
|
|
||||||
** push the lookahead
|
|
||||||
** token onto the stack
|
|
||||||
** and goto state N.
|
|
||||||
**
|
|
||||||
** self::YYNSTATE <= N < self::YYNSTATE+self::YYNRULE Reduce by rule N-YYNSTATE.
|
|
||||||
**
|
|
||||||
** N == self::YYNSTATE+self::YYNRULE A syntax error has occurred.
|
|
||||||
**
|
|
||||||
** N == self::YYNSTATE+self::YYNRULE+1 The parser accepts its
|
|
||||||
** input. (and concludes parsing)
|
|
||||||
**
|
|
||||||
** N == self::YYNSTATE+self::YYNRULE+2 No such action. Denotes unused
|
|
||||||
** slots in the yy_action[] table.
|
|
||||||
**
|
|
||||||
** The action table is constructed as a single large static array $yy_action.
|
|
||||||
** Given state S and lookahead X, the action is computed as
|
|
||||||
**
|
|
||||||
** self::$yy_action[self::$yy_shift_ofst[S] + X ]
|
|
||||||
**
|
|
||||||
** If the index value self::$yy_shift_ofst[S]+X is out of range or if the value
|
|
||||||
** self::$yy_lookahead[self::$yy_shift_ofst[S]+X] is not equal to X or if
|
|
||||||
** self::$yy_shift_ofst[S] is equal to self::YY_SHIFT_USE_DFLT, it means that
|
|
||||||
** the action is not in the table and that self::$yy_default[S] should be used instead.
|
|
||||||
**
|
|
||||||
** The formula above is for computing the action when the lookahead is
|
|
||||||
** a terminal symbol. If the lookahead is a non-terminal (as occurs after
|
|
||||||
** a reduce action) then the static $yy_reduce_ofst array is used in place of
|
|
||||||
** the static $yy_shift_ofst array and self::YY_REDUCE_USE_DFLT is used in place of
|
|
||||||
** self::YY_SHIFT_USE_DFLT.
|
|
||||||
**
|
|
||||||
** The following are the tables generated in this section:
|
|
||||||
**
|
|
||||||
** self::$yy_action A single table containing all actions.
|
|
||||||
** self::$yy_lookahead A table containing the lookahead for each entry in
|
|
||||||
** yy_action. Used to detect hash collisions.
|
|
||||||
** self::$yy_shift_ofst For each state, the offset into self::$yy_action for
|
|
||||||
** shifting terminals.
|
|
||||||
** self::$yy_reduce_ofst For each state, the offset into self::$yy_action for
|
|
||||||
** shifting non-terminals after a reduce.
|
|
||||||
** self::$yy_default Default action for each state.
|
|
||||||
*/
|
|
||||||
%%
|
|
||||||
/* The next thing included is series of defines which control
|
|
||||||
** various aspects of the generated parser.
|
|
||||||
** self::YYNOCODE is a number which corresponds
|
|
||||||
** to no legal terminal or nonterminal number. This
|
|
||||||
** number is used to fill in empty slots of the hash
|
|
||||||
** table.
|
|
||||||
** self::YYFALLBACK If defined, this indicates that one or more tokens
|
|
||||||
** have fall-back values which should be used if the
|
|
||||||
** original value of the token will not parse.
|
|
||||||
** self::YYSTACKDEPTH is the maximum depth of the parser's stack.
|
|
||||||
** self::YYNSTATE the combined number of states.
|
|
||||||
** self::YYNRULE the number of rules in the grammar
|
|
||||||
** self::YYERRORSYMBOL is the code number of the error symbol. If not
|
|
||||||
** defined, then do no error processing.
|
|
||||||
*/
|
|
||||||
%%
|
|
||||||
/** The next table maps tokens into fallback tokens. If a construct
|
|
||||||
* like the following:
|
|
||||||
*
|
|
||||||
* %fallback ID X Y Z.
|
|
||||||
*
|
|
||||||
* appears in the grammer, then ID becomes a fallback token for X, Y,
|
|
||||||
* and Z. Whenever one of the tokens X, Y, or Z is input to the parser
|
|
||||||
* but it does not parse, the type of the token is changed to ID and
|
|
||||||
* the parse is retried before an error is thrown.
|
|
||||||
*/
|
|
||||||
static public $yyFallback = array(
|
|
||||||
%%
|
|
||||||
);
|
|
||||||
/**
|
|
||||||
* Turn parser tracing on by giving a stream to which to write the trace
|
|
||||||
* and a prompt to preface each trace message. Tracing is turned off
|
|
||||||
* by making either argument NULL
|
|
||||||
*
|
|
||||||
* Inputs:
|
|
||||||
*
|
|
||||||
* - A stream resource to which trace output should be written.
|
|
||||||
* If NULL, then tracing is turned off.
|
|
||||||
* - A prefix string written at the beginning of every
|
|
||||||
* line of trace output. If NULL, then tracing is
|
|
||||||
* turned off.
|
|
||||||
*
|
|
||||||
* Outputs:
|
|
||||||
*
|
|
||||||
* - None.
|
|
||||||
* @param resource
|
|
||||||
* @param string
|
|
||||||
*/
|
|
||||||
static function Trace($TraceFILE, $zTracePrompt)
|
|
||||||
{
|
|
||||||
if (!$TraceFILE) {
|
|
||||||
$zTracePrompt = 0;
|
|
||||||
} elseif (!$zTracePrompt) {
|
|
||||||
$TraceFILE = 0;
|
|
||||||
}
|
|
||||||
self::$yyTraceFILE = $TraceFILE;
|
|
||||||
self::$yyTracePrompt = $zTracePrompt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Output debug information to output (php://output stream)
|
|
||||||
*/
|
|
||||||
static function PrintTrace()
|
|
||||||
{
|
|
||||||
self::$yyTraceFILE = fopen('php://output', 'w');
|
|
||||||
self::$yyTracePrompt = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var resource|0
|
|
||||||
*/
|
|
||||||
static public $yyTraceFILE;
|
|
||||||
/**
|
|
||||||
* String to prepend to debug output
|
|
||||||
* @var string|0
|
|
||||||
*/
|
|
||||||
static public $yyTracePrompt;
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $yyidx = -1; /* Index of top element in stack */
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $yyerrcnt; /* Shifts left before out of the error */
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $yystack = array(); /* The parser's stack */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For tracing shifts, the names of all terminals and nonterminals
|
|
||||||
* are required. The following table supplies these names
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
static public $yyTokenName = array(
|
|
||||||
%%
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For tracing reduce actions, the names of all rules are required.
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
static public $yyRuleName = array(
|
|
||||||
%%
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function returns the symbolic name associated with a token
|
|
||||||
* value.
|
|
||||||
* @param int
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function tokenName($tokenType)
|
|
||||||
{
|
|
||||||
if ($tokenType === 0) {
|
|
||||||
return 'End of Input';
|
|
||||||
}
|
|
||||||
if ($tokenType > 0 && $tokenType < count(self::$yyTokenName)) {
|
|
||||||
return self::$yyTokenName[$tokenType];
|
|
||||||
} else {
|
|
||||||
return "Unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The following function deletes the value associated with a
|
|
||||||
* symbol. The symbol can be either a terminal or nonterminal.
|
|
||||||
* @param int the symbol code
|
|
||||||
* @param mixed the symbol's value
|
|
||||||
*/
|
|
||||||
static function yy_destructor($yymajor, $yypminor)
|
|
||||||
{
|
|
||||||
switch ($yymajor) {
|
|
||||||
/* Here is inserted the actions which take place when a
|
|
||||||
** terminal or non-terminal is destroyed. This can happen
|
|
||||||
** when the symbol is popped from the stack during a
|
|
||||||
** reduce or during error processing or when a parser is
|
|
||||||
** being destroyed before it is finished parsing.
|
|
||||||
**
|
|
||||||
** Note: during a reduce, the only symbols destroyed are those
|
|
||||||
** which appear on the RHS of the rule, but which are not used
|
|
||||||
** inside the C code.
|
|
||||||
*/
|
|
||||||
%%
|
|
||||||
default: break; /* If no destructor action specified: do nothing */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pop the parser's stack once.
|
|
||||||
*
|
|
||||||
* If there is a destructor routine associated with the token which
|
|
||||||
* is popped from the stack, then call it.
|
|
||||||
*
|
|
||||||
* Return the major token number for the symbol popped.
|
|
||||||
* @param ParseyyParser
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
function yy_pop_parser_stack()
|
|
||||||
{
|
|
||||||
if (!count($this->yystack)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$yytos = array_pop($this->yystack);
|
|
||||||
if (self::$yyTraceFILE && $this->yyidx >= 0) {
|
|
||||||
fwrite(self::$yyTraceFILE,
|
|
||||||
self::$yyTracePrompt . 'Popping ' . self::$yyTokenName[$yytos->major] .
|
|
||||||
"\n");
|
|
||||||
}
|
|
||||||
$yymajor = $yytos->major;
|
|
||||||
self::yy_destructor($yymajor, $yytos->minor);
|
|
||||||
$this->yyidx--;
|
|
||||||
return $yymajor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deallocate and destroy a parser. Destructors are all called for
|
|
||||||
* all stack elements before shutting the parser down.
|
|
||||||
*/
|
|
||||||
function __destruct()
|
|
||||||
{
|
|
||||||
while ($this->yyidx >= 0) {
|
|
||||||
$this->yy_pop_parser_stack();
|
|
||||||
}
|
|
||||||
if (is_resource(self::$yyTraceFILE)) {
|
|
||||||
fclose(self::$yyTraceFILE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Based on the current state and parser stack, get a list of all
|
|
||||||
* possible lookahead tokens
|
|
||||||
* @param int
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
function yy_get_expected_tokens($token)
|
|
||||||
{
|
|
||||||
$state = $this->yystack[$this->yyidx]->stateno;
|
|
||||||
$expected = self::$yyExpectedTokens[$state];
|
|
||||||
if (in_array($token, self::$yyExpectedTokens[$state], true)) {
|
|
||||||
return $expected;
|
|
||||||
}
|
|
||||||
$stack = $this->yystack;
|
|
||||||
$yyidx = $this->yyidx;
|
|
||||||
do {
|
|
||||||
$yyact = $this->yy_find_shift_action($token);
|
|
||||||
if ($yyact >= self::YYNSTATE && $yyact < self::YYNSTATE + self::YYNRULE) {
|
|
||||||
// reduce action
|
|
||||||
$done = 0;
|
|
||||||
do {
|
|
||||||
if ($done++ == 100) {
|
|
||||||
$this->yyidx = $yyidx;
|
|
||||||
$this->yystack = $stack;
|
|
||||||
// too much recursion prevents proper detection
|
|
||||||
// so give up
|
|
||||||
return array_unique($expected);
|
|
||||||
}
|
|
||||||
$yyruleno = $yyact - self::YYNSTATE;
|
|
||||||
$this->yyidx -= self::$yyRuleInfo[$yyruleno]['rhs'];
|
|
||||||
$nextstate = $this->yy_find_reduce_action(
|
|
||||||
$this->yystack[$this->yyidx]->stateno,
|
|
||||||
self::$yyRuleInfo[$yyruleno]['lhs']);
|
|
||||||
if (isset(self::$yyExpectedTokens[$nextstate])) {
|
|
||||||
$expected += self::$yyExpectedTokens[$nextstate];
|
|
||||||
if (in_array($token,
|
|
||||||
self::$yyExpectedTokens[$nextstate], true)) {
|
|
||||||
$this->yyidx = $yyidx;
|
|
||||||
$this->yystack = $stack;
|
|
||||||
return array_unique($expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($nextstate < self::YYNSTATE) {
|
|
||||||
// we need to shift a non-terminal
|
|
||||||
$this->yyidx++;
|
|
||||||
$x = new ParseyyStackEntry;
|
|
||||||
$x->stateno = $nextstate;
|
|
||||||
$x->major = self::$yyRuleInfo[$yyruleno]['lhs'];
|
|
||||||
$this->yystack[$this->yyidx] = $x;
|
|
||||||
continue 2;
|
|
||||||
} elseif ($nextstate == self::YYNSTATE + self::YYNRULE + 1) {
|
|
||||||
$this->yyidx = $yyidx;
|
|
||||||
$this->yystack = $stack;
|
|
||||||
// the last token was just ignored, we can't accept
|
|
||||||
// by ignoring input, this is in essence ignoring a
|
|
||||||
// syntax error!
|
|
||||||
return array_unique($expected);
|
|
||||||
} elseif ($nextstate === self::YY_NO_ACTION) {
|
|
||||||
$this->yyidx = $yyidx;
|
|
||||||
$this->yystack = $stack;
|
|
||||||
// input accepted, but not shifted (I guess)
|
|
||||||
return $expected;
|
|
||||||
} else {
|
|
||||||
$yyact = $nextstate;
|
|
||||||
}
|
|
||||||
} while (true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} while (true);
|
|
||||||
return array_unique($expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Based on the parser state and current parser stack, determine whether
|
|
||||||
* the lookahead token is possible.
|
|
||||||
*
|
|
||||||
* The parser will convert the token value to an error token if not. This
|
|
||||||
* catches some unusual edge cases where the parser would fail.
|
|
||||||
* @param int
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
function yy_is_expected_token($token)
|
|
||||||
{
|
|
||||||
if ($token === 0) {
|
|
||||||
return true; // 0 is not part of this
|
|
||||||
}
|
|
||||||
$state = $this->yystack[$this->yyidx]->stateno;
|
|
||||||
if (in_array($token, self::$yyExpectedTokens[$state], true)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$stack = $this->yystack;
|
|
||||||
$yyidx = $this->yyidx;
|
|
||||||
do {
|
|
||||||
$yyact = $this->yy_find_shift_action($token);
|
|
||||||
if ($yyact >= self::YYNSTATE && $yyact < self::YYNSTATE + self::YYNRULE) {
|
|
||||||
// reduce action
|
|
||||||
$done = 0;
|
|
||||||
do {
|
|
||||||
if ($done++ == 100) {
|
|
||||||
$this->yyidx = $yyidx;
|
|
||||||
$this->yystack = $stack;
|
|
||||||
// too much recursion prevents proper detection
|
|
||||||
// so give up
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$yyruleno = $yyact - self::YYNSTATE;
|
|
||||||
$this->yyidx -= self::$yyRuleInfo[$yyruleno]['rhs'];
|
|
||||||
$nextstate = $this->yy_find_reduce_action(
|
|
||||||
$this->yystack[$this->yyidx]->stateno,
|
|
||||||
self::$yyRuleInfo[$yyruleno]['lhs']);
|
|
||||||
if (isset(self::$yyExpectedTokens[$nextstate]) &&
|
|
||||||
in_array($token, self::$yyExpectedTokens[$nextstate], true)) {
|
|
||||||
$this->yyidx = $yyidx;
|
|
||||||
$this->yystack = $stack;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ($nextstate < self::YYNSTATE) {
|
|
||||||
// we need to shift a non-terminal
|
|
||||||
$this->yyidx++;
|
|
||||||
$x = new ParseyyStackEntry;
|
|
||||||
$x->stateno = $nextstate;
|
|
||||||
$x->major = self::$yyRuleInfo[$yyruleno]['lhs'];
|
|
||||||
$this->yystack[$this->yyidx] = $x;
|
|
||||||
continue 2;
|
|
||||||
} elseif ($nextstate == self::YYNSTATE + self::YYNRULE + 1) {
|
|
||||||
$this->yyidx = $yyidx;
|
|
||||||
$this->yystack = $stack;
|
|
||||||
if (!$token) {
|
|
||||||
// end of input: this is valid
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// the last token was just ignored, we can't accept
|
|
||||||
// by ignoring input, this is in essence ignoring a
|
|
||||||
// syntax error!
|
|
||||||
return false;
|
|
||||||
} elseif ($nextstate === self::YY_NO_ACTION) {
|
|
||||||
$this->yyidx = $yyidx;
|
|
||||||
$this->yystack = $stack;
|
|
||||||
// input accepted, but not shifted (I guess)
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
$yyact = $nextstate;
|
|
||||||
}
|
|
||||||
} while (true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} while (true);
|
|
||||||
$this->yyidx = $yyidx;
|
|
||||||
$this->yystack = $stack;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the appropriate action for a parser given the terminal
|
|
||||||
* look-ahead token iLookAhead.
|
|
||||||
*
|
|
||||||
* If the look-ahead token is YYNOCODE, then check to see if the action is
|
|
||||||
* independent of the look-ahead. If it is, return the action, otherwise
|
|
||||||
* return YY_NO_ACTION.
|
|
||||||
* @param int The look-ahead token
|
|
||||||
*/
|
|
||||||
function yy_find_shift_action($iLookAhead)
|
|
||||||
{
|
|
||||||
$stateno = $this->yystack[$this->yyidx]->stateno;
|
|
||||||
|
|
||||||
/* if ($this->yyidx < 0) return self::YY_NO_ACTION; */
|
|
||||||
if (!isset(self::$yy_shift_ofst[$stateno])) {
|
|
||||||
// no shift actions
|
|
||||||
return self::$yy_default[$stateno];
|
|
||||||
}
|
|
||||||
$i = self::$yy_shift_ofst[$stateno];
|
|
||||||
if ($i === self::YY_SHIFT_USE_DFLT) {
|
|
||||||
return self::$yy_default[$stateno];
|
|
||||||
}
|
|
||||||
if ($iLookAhead == self::YYNOCODE) {
|
|
||||||
return self::YY_NO_ACTION;
|
|
||||||
}
|
|
||||||
$i += $iLookAhead;
|
|
||||||
if ($i < 0 || $i >= self::YY_SZ_ACTTAB ||
|
|
||||||
self::$yy_lookahead[$i] != $iLookAhead) {
|
|
||||||
if (count(self::$yyFallback) && $iLookAhead < count(self::$yyFallback)
|
|
||||||
&& ($iFallback = self::$yyFallback[$iLookAhead]) != 0) {
|
|
||||||
if (self::$yyTraceFILE) {
|
|
||||||
fwrite(self::$yyTraceFILE, self::$yyTracePrompt . "FALLBACK " .
|
|
||||||
self::$yyTokenName[$iLookAhead] . " => " .
|
|
||||||
self::$yyTokenName[$iFallback] . "\n");
|
|
||||||
}
|
|
||||||
return $this->yy_find_shift_action($iFallback);
|
|
||||||
}
|
|
||||||
return self::$yy_default[$stateno];
|
|
||||||
} else {
|
|
||||||
return self::$yy_action[$i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the appropriate action for a parser given the non-terminal
|
|
||||||
* look-ahead token $iLookAhead.
|
|
||||||
*
|
|
||||||
* If the look-ahead token is self::YYNOCODE, then check to see if the action is
|
|
||||||
* independent of the look-ahead. If it is, return the action, otherwise
|
|
||||||
* return self::YY_NO_ACTION.
|
|
||||||
* @param int Current state number
|
|
||||||
* @param int The look-ahead token
|
|
||||||
*/
|
|
||||||
function yy_find_reduce_action($stateno, $iLookAhead)
|
|
||||||
{
|
|
||||||
/* $stateno = $this->yystack[$this->yyidx]->stateno; */
|
|
||||||
|
|
||||||
if (!isset(self::$yy_reduce_ofst[$stateno])) {
|
|
||||||
return self::$yy_default[$stateno];
|
|
||||||
}
|
|
||||||
$i = self::$yy_reduce_ofst[$stateno];
|
|
||||||
if ($i == self::YY_REDUCE_USE_DFLT) {
|
|
||||||
return self::$yy_default[$stateno];
|
|
||||||
}
|
|
||||||
if ($iLookAhead == self::YYNOCODE) {
|
|
||||||
return self::YY_NO_ACTION;
|
|
||||||
}
|
|
||||||
$i += $iLookAhead;
|
|
||||||
if ($i < 0 || $i >= self::YY_SZ_ACTTAB ||
|
|
||||||
self::$yy_lookahead[$i] != $iLookAhead) {
|
|
||||||
return self::$yy_default[$stateno];
|
|
||||||
} else {
|
|
||||||
return self::$yy_action[$i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform a shift action.
|
|
||||||
* @param int The new state to shift in
|
|
||||||
* @param int The major token to shift in
|
|
||||||
* @param mixed the minor token to shift in
|
|
||||||
*/
|
|
||||||
function yy_shift($yyNewState, $yyMajor, $yypMinor)
|
|
||||||
{
|
|
||||||
$this->yyidx++;
|
|
||||||
if ($this->yyidx >= self::YYSTACKDEPTH) {
|
|
||||||
$this->yyidx--;
|
|
||||||
if (self::$yyTraceFILE) {
|
|
||||||
fprintf(self::$yyTraceFILE, "%sStack Overflow!\n", self::$yyTracePrompt);
|
|
||||||
}
|
|
||||||
while ($this->yyidx >= 0) {
|
|
||||||
$this->yy_pop_parser_stack();
|
|
||||||
}
|
|
||||||
/* Here code is inserted which will execute if the parser
|
|
||||||
** stack ever overflows */
|
|
||||||
%%
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$yytos = new ParseyyStackEntry;
|
|
||||||
$yytos->stateno = $yyNewState;
|
|
||||||
$yytos->major = $yyMajor;
|
|
||||||
$yytos->minor = $yypMinor;
|
|
||||||
array_push($this->yystack, $yytos);
|
|
||||||
if (self::$yyTraceFILE && $this->yyidx > 0) {
|
|
||||||
fprintf(self::$yyTraceFILE, "%sShift %d\n", self::$yyTracePrompt,
|
|
||||||
$yyNewState);
|
|
||||||
fprintf(self::$yyTraceFILE, "%sStack:", self::$yyTracePrompt);
|
|
||||||
for ($i = 1; $i <= $this->yyidx; $i++) {
|
|
||||||
fprintf(self::$yyTraceFILE, " %s",
|
|
||||||
self::$yyTokenName[$this->yystack[$i]->major]);
|
|
||||||
}
|
|
||||||
fwrite(self::$yyTraceFILE,"\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The following table contains information about every rule that
|
|
||||||
* is used during the reduce.
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* array(
|
|
||||||
* array(
|
|
||||||
* int $lhs; Symbol on the left-hand side of the rule
|
|
||||||
* int $nrhs; Number of right-hand side symbols in the rule
|
|
||||||
* ),...
|
|
||||||
* );
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
static public $yyRuleInfo = array(
|
|
||||||
%%
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The following table contains a mapping of reduce action to method name
|
|
||||||
* that handles the reduction.
|
|
||||||
*
|
|
||||||
* If a rule is not set, it has no handler.
|
|
||||||
*/
|
|
||||||
static public $yyReduceMap = array(
|
|
||||||
%%
|
|
||||||
);
|
|
||||||
/* Beginning here are the reduction cases. A typical example
|
|
||||||
** follows:
|
|
||||||
** #line <lineno> <grammarfile>
|
|
||||||
** function yy_r0($yymsp){ ... } // User supplied code
|
|
||||||
** #line <lineno> <thisfile>
|
|
||||||
*/
|
|
||||||
%%
|
|
||||||
|
|
||||||
/**
|
|
||||||
* placeholder for the left hand side in a reduce operation.
|
|
||||||
*
|
|
||||||
* For a parser with a rule like this:
|
|
||||||
* <pre>
|
|
||||||
* rule(A) ::= B. { A = 1; }
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* The parser will translate to something like:
|
|
||||||
*
|
|
||||||
* <code>
|
|
||||||
* function yy_r0(){$this->_retvalue = 1;}
|
|
||||||
* </code>
|
|
||||||
*/
|
|
||||||
private $_retvalue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform a reduce action and the shift that must immediately
|
|
||||||
* follow the reduce.
|
|
||||||
*
|
|
||||||
* For a rule such as:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* A ::= B blah C. { dosomething(); }
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* This function will first call the action, if any, ("dosomething();" in our
|
|
||||||
* example), and then it will pop three states from the stack,
|
|
||||||
* one for each entry on the right-hand side of the expression
|
|
||||||
* (B, blah, and C in our example rule), and then push the result of the action
|
|
||||||
* back on to the stack with the resulting state reduced to (as described in the .out
|
|
||||||
* file)
|
|
||||||
* @param int Number of the rule by which to reduce
|
|
||||||
*/
|
|
||||||
function yy_reduce($yyruleno)
|
|
||||||
{
|
|
||||||
//int $yygoto; /* The next state */
|
|
||||||
//int $yyact; /* The next action */
|
|
||||||
//mixed $yygotominor; /* The LHS of the rule reduced */
|
|
||||||
//ParseyyStackEntry $yymsp; /* The top of the parser's stack */
|
|
||||||
//int $yysize; /* Amount to pop the stack */
|
|
||||||
$yymsp = $this->yystack[$this->yyidx];
|
|
||||||
if (self::$yyTraceFILE && $yyruleno >= 0
|
|
||||||
&& $yyruleno < count(self::$yyRuleName)) {
|
|
||||||
fprintf(self::$yyTraceFILE, "%sReduce (%d) [%s].\n",
|
|
||||||
self::$yyTracePrompt, $yyruleno,
|
|
||||||
self::$yyRuleName[$yyruleno]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_retvalue = $yy_lefthand_side = null;
|
|
||||||
if (array_key_exists($yyruleno, self::$yyReduceMap)) {
|
|
||||||
// call the action
|
|
||||||
$this->_retvalue = null;
|
|
||||||
$this->{'yy_r' . self::$yyReduceMap[$yyruleno]}();
|
|
||||||
$yy_lefthand_side = $this->_retvalue;
|
|
||||||
}
|
|
||||||
$yygoto = self::$yyRuleInfo[$yyruleno]['lhs'];
|
|
||||||
$yysize = self::$yyRuleInfo[$yyruleno]['rhs'];
|
|
||||||
$this->yyidx -= $yysize;
|
|
||||||
for ($i = $yysize; $i; $i--) {
|
|
||||||
// pop all of the right-hand side parameters
|
|
||||||
array_pop($this->yystack);
|
|
||||||
}
|
|
||||||
$yyact = $this->yy_find_reduce_action($this->yystack[$this->yyidx]->stateno, $yygoto);
|
|
||||||
if ($yyact < self::YYNSTATE) {
|
|
||||||
/* If we are not debugging and the reduce action popped at least
|
|
||||||
** one element off the stack, then we can push the new element back
|
|
||||||
** onto the stack here, and skip the stack overflow test in yy_shift().
|
|
||||||
** That gives a significant speed improvement. */
|
|
||||||
if (!self::$yyTraceFILE && $yysize) {
|
|
||||||
$this->yyidx++;
|
|
||||||
$x = new ParseyyStackEntry;
|
|
||||||
$x->stateno = $yyact;
|
|
||||||
$x->major = $yygoto;
|
|
||||||
$x->minor = $yy_lefthand_side;
|
|
||||||
$this->yystack[$this->yyidx] = $x;
|
|
||||||
} else {
|
|
||||||
$this->yy_shift($yyact, $yygoto, $yy_lefthand_side);
|
|
||||||
}
|
|
||||||
} elseif ($yyact == self::YYNSTATE + self::YYNRULE + 1) {
|
|
||||||
$this->yy_accept();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The following code executes when the parse fails
|
|
||||||
*
|
|
||||||
* Code from %parse_fail is inserted here
|
|
||||||
*/
|
|
||||||
function yy_parse_failed()
|
|
||||||
{
|
|
||||||
if (self::$yyTraceFILE) {
|
|
||||||
fprintf(self::$yyTraceFILE, "%sFail!\n", self::$yyTracePrompt);
|
|
||||||
}
|
|
||||||
while ($this->yyidx >= 0) {
|
|
||||||
$this->yy_pop_parser_stack();
|
|
||||||
}
|
|
||||||
/* Here code is inserted which will be executed whenever the
|
|
||||||
** parser fails */
|
|
||||||
%%
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The following code executes when a syntax error first occurs.
|
|
||||||
*
|
|
||||||
* %syntax_error code is inserted here
|
|
||||||
* @param int The major type of the error token
|
|
||||||
* @param mixed The minor type of the error token
|
|
||||||
*/
|
|
||||||
function yy_syntax_error($yymajor, $TOKEN)
|
|
||||||
{
|
|
||||||
%%
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The following is executed when the parser accepts
|
|
||||||
*
|
|
||||||
* %parse_accept code is inserted here
|
|
||||||
*/
|
|
||||||
function yy_accept()
|
|
||||||
{
|
|
||||||
if (self::$yyTraceFILE) {
|
|
||||||
fprintf(self::$yyTraceFILE, "%sAccept!\n", self::$yyTracePrompt);
|
|
||||||
}
|
|
||||||
while ($this->yyidx >= 0) {
|
|
||||||
$stack = $this->yy_pop_parser_stack();
|
|
||||||
}
|
|
||||||
/* Here code is inserted which will be executed whenever the
|
|
||||||
** parser accepts */
|
|
||||||
%%
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main parser program.
|
|
||||||
*
|
|
||||||
* The first argument is the major token number. The second is
|
|
||||||
* the token value string as scanned from the input.
|
|
||||||
*
|
|
||||||
* @param int $yymajor the token number
|
|
||||||
* @param mixed $yytokenvalue the token value
|
|
||||||
* @param mixed ... any extra arguments that should be passed to handlers
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
function doParse($yymajor, $yytokenvalue)
|
|
||||||
{
|
|
||||||
// $yyact; /* The parser action. */
|
|
||||||
// $yyendofinput; /* True if we are at the end of input */
|
|
||||||
$yyerrorhit = 0; /* True if yymajor has invoked an error */
|
|
||||||
|
|
||||||
/* (re)initialize the parser, if necessary */
|
|
||||||
if ($this->yyidx === null || $this->yyidx < 0) {
|
|
||||||
/* if ($yymajor == 0) return; // not sure why this was here... */
|
|
||||||
$this->yyidx = 0;
|
|
||||||
$this->yyerrcnt = -1;
|
|
||||||
$x = new ParseyyStackEntry;
|
|
||||||
$x->stateno = 0;
|
|
||||||
$x->major = 0;
|
|
||||||
$this->yystack = array();
|
|
||||||
array_push($this->yystack, $x);
|
|
||||||
}
|
|
||||||
$yyendofinput = ($yymajor==0);
|
|
||||||
|
|
||||||
if (self::$yyTraceFILE) {
|
|
||||||
fprintf(
|
|
||||||
self::$yyTraceFILE,
|
|
||||||
"%sInput %s\n",
|
|
||||||
self::$yyTracePrompt,
|
|
||||||
self::$yyTokenName[$yymajor]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
$yyact = $this->yy_find_shift_action($yymajor);
|
|
||||||
if ($yymajor < self::YYERRORSYMBOL
|
|
||||||
&& !$this->yy_is_expected_token($yymajor)
|
|
||||||
) {
|
|
||||||
// force a syntax error
|
|
||||||
$yyact = self::YY_ERROR_ACTION;
|
|
||||||
}
|
|
||||||
if ($yyact < self::YYNSTATE) {
|
|
||||||
$this->yy_shift($yyact, $yymajor, $yytokenvalue);
|
|
||||||
$this->yyerrcnt--;
|
|
||||||
if ($yyendofinput && $this->yyidx >= 0) {
|
|
||||||
$yymajor = 0;
|
|
||||||
} else {
|
|
||||||
$yymajor = self::YYNOCODE;
|
|
||||||
}
|
|
||||||
} elseif ($yyact < self::YYNSTATE + self::YYNRULE) {
|
|
||||||
$this->yy_reduce($yyact - self::YYNSTATE);
|
|
||||||
} elseif ($yyact == self::YY_ERROR_ACTION) {
|
|
||||||
if (self::$yyTraceFILE) {
|
|
||||||
fprintf(
|
|
||||||
self::$yyTraceFILE,
|
|
||||||
"%sSyntax Error!\n",
|
|
||||||
self::$yyTracePrompt
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (self::YYERRORSYMBOL) {
|
|
||||||
/* A syntax error has occurred.
|
|
||||||
** The response to an error depends upon whether or not the
|
|
||||||
** grammar defines an error token "ERROR".
|
|
||||||
**
|
|
||||||
** This is what we do if the grammar does define ERROR:
|
|
||||||
**
|
|
||||||
** * Call the %syntax_error function.
|
|
||||||
**
|
|
||||||
** * Begin popping the stack until we enter a state where
|
|
||||||
** it is legal to shift the error symbol, then shift
|
|
||||||
** the error symbol.
|
|
||||||
**
|
|
||||||
** * Set the error count to three.
|
|
||||||
**
|
|
||||||
** * Begin accepting and shifting new tokens. No new error
|
|
||||||
** processing will occur until three tokens have been
|
|
||||||
** shifted successfully.
|
|
||||||
**
|
|
||||||
*/
|
|
||||||
if ($this->yyerrcnt < 0) {
|
|
||||||
$this->yy_syntax_error($yymajor, $yytokenvalue);
|
|
||||||
}
|
|
||||||
$yymx = $this->yystack[$this->yyidx]->major;
|
|
||||||
if ($yymx == self::YYERRORSYMBOL || $yyerrorhit ) {
|
|
||||||
if (self::$yyTraceFILE) {
|
|
||||||
fprintf(
|
|
||||||
self::$yyTraceFILE,
|
|
||||||
"%sDiscard input token %s\n",
|
|
||||||
self::$yyTracePrompt,
|
|
||||||
self::$yyTokenName[$yymajor]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$this->yy_destructor($yymajor, $yytokenvalue);
|
|
||||||
$yymajor = self::YYNOCODE;
|
|
||||||
} else {
|
|
||||||
while ($this->yyidx >= 0
|
|
||||||
&& $yymx != self::YYERRORSYMBOL
|
|
||||||
&& ($yyact = $this->yy_find_shift_action(self::YYERRORSYMBOL)) >= self::YYNSTATE
|
|
||||||
) {
|
|
||||||
$this->yy_pop_parser_stack();
|
|
||||||
}
|
|
||||||
if ($this->yyidx < 0 || $yymajor==0) {
|
|
||||||
$this->yy_destructor($yymajor, $yytokenvalue);
|
|
||||||
$this->yy_parse_failed();
|
|
||||||
$yymajor = self::YYNOCODE;
|
|
||||||
} elseif ($yymx != self::YYERRORSYMBOL) {
|
|
||||||
$u2 = 0;
|
|
||||||
$this->yy_shift($yyact, self::YYERRORSYMBOL, $u2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->yyerrcnt = 3;
|
|
||||||
$yyerrorhit = 1;
|
|
||||||
} else {
|
|
||||||
/* YYERRORSYMBOL is not defined */
|
|
||||||
/* This is what we do if the grammar does not define ERROR:
|
|
||||||
**
|
|
||||||
** * Report an error message, and throw away the input token.
|
|
||||||
**
|
|
||||||
** * If the input token is $, then fail the parse.
|
|
||||||
**
|
|
||||||
** As before, subsequent error messages are suppressed until
|
|
||||||
** three input tokens have been successfully shifted.
|
|
||||||
*/
|
|
||||||
if ($this->yyerrcnt <= 0) {
|
|
||||||
$this->yy_syntax_error($yymajor, $yytokenvalue);
|
|
||||||
}
|
|
||||||
$this->yyerrcnt = 3;
|
|
||||||
$this->yy_destructor($yymajor, $yytokenvalue);
|
|
||||||
if ($yyendofinput) {
|
|
||||||
$this->yy_parse_failed();
|
|
||||||
}
|
|
||||||
$yymajor = self::YYNOCODE;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->yy_accept();
|
|
||||||
$yymajor = self::YYNOCODE;
|
|
||||||
}
|
|
||||||
} while ($yymajor != self::YYNOCODE && $this->yyidx >= 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,332 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* PHP_LexerGenerator, a php 5 lexer generator.
|
|
||||||
*
|
|
||||||
* This lexer generator translates a file in a format similar to
|
|
||||||
* re2c ({@link http://re2c.org}) and translates it into a PHP 5-based lexer
|
|
||||||
*
|
|
||||||
* PHP version 5
|
|
||||||
*
|
|
||||||
* LICENSE:
|
|
||||||
*
|
|
||||||
* Copyright (c) 2006, Gregory Beaver <cellog@php.net>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in
|
|
||||||
* the documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the PHP_LexerGenerator nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived
|
|
||||||
* from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
||||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
||||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* @category php
|
|
||||||
* @package PHP_LexerGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version CVS: $Id: LexerGenerator.php 294970 2010-02-12 03:46:38Z clockwerx $
|
|
||||||
* @since File available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* The Lexer generation parser
|
|
||||||
*/
|
|
||||||
require_once 'PHP/LexerGenerator/Parser.php';
|
|
||||||
/**
|
|
||||||
* Hand-written lexer for lex2php format files
|
|
||||||
*/
|
|
||||||
require_once 'PHP/LexerGenerator/Lexer.php';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The basic home class for the lexer generator. A lexer scans text and
|
|
||||||
* organizes it into tokens for usage by a parser.
|
|
||||||
*
|
|
||||||
* Sample Usage:
|
|
||||||
* <code>
|
|
||||||
* require_once 'PHP/LexerGenerator.php';
|
|
||||||
* $lex = new PHP_LexerGenerator('/path/to/lexerfile.plex');
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* A file named "/path/to/lexerfile.php" will be created.
|
|
||||||
*
|
|
||||||
* File format consists of a PHP file containing specially
|
|
||||||
* formatted comments like so:
|
|
||||||
*
|
|
||||||
* <code>
|
|
||||||
* /*!lex2php
|
|
||||||
* {@*}
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* All lexer definition files must contain at least two lex2php comment blocks:
|
|
||||||
* - 1 regex declaration block
|
|
||||||
* - 1 or more rule declaration blocks
|
|
||||||
*
|
|
||||||
* The first lex2php comment is the regex declaration block and must contain
|
|
||||||
* several processor instruction as well as defining a name for all
|
|
||||||
* regular expressions. Processor instructions start with
|
|
||||||
* a "%" symbol and must be:
|
|
||||||
*
|
|
||||||
* - %counter
|
|
||||||
* - %input
|
|
||||||
* - %token
|
|
||||||
* - %value
|
|
||||||
* - %line
|
|
||||||
*
|
|
||||||
* token and counter should define the class variables used to define lexer input
|
|
||||||
* and the index into the input. token and value should be used to define the class
|
|
||||||
* variables used to store the token number and its textual value. Finally, line
|
|
||||||
* should be used to define the class variable used to define the current line number
|
|
||||||
* of scanning.
|
|
||||||
*
|
|
||||||
* For example:
|
|
||||||
* <code>
|
|
||||||
* /*!lex2php
|
|
||||||
* %counter {$this->N}
|
|
||||||
* %input {$this->data}
|
|
||||||
* %token {$this->token}
|
|
||||||
* %value {$this->value}
|
|
||||||
* %line {%this->linenumber}
|
|
||||||
* {@*}
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* Patterns consist of an identifier containing an letters or an underscore, and
|
|
||||||
* a descriptive match pattern.
|
|
||||||
*
|
|
||||||
* Descriptive match patterns may either be regular expressions (regexes) or
|
|
||||||
* quoted literal strings. Here are some examples:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* pattern = "quoted literal"
|
|
||||||
* ANOTHER = /[a-zA-Z_]+/
|
|
||||||
* COMPLEX = @<([a-zA-Z_]+)( +(([a-zA-Z_]+)=((["\'])([^\6]*)\6))+){0,1}>[^<]*</\1>@
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* Quoted strings must escape the \ and " characters with \" and \\.
|
|
||||||
*
|
|
||||||
* Regex patterns must be in Perl-compatible regular expression format (preg).
|
|
||||||
* special characters (like \t \n or \x3H) can only be used in regexes, all
|
|
||||||
* \ will be escaped in literal strings.
|
|
||||||
*
|
|
||||||
* Sub-patterns may be defined and back-references (like \1) may be used. Any sub-
|
|
||||||
* patterns detected will be passed to the token handler in the variable
|
|
||||||
* $yysubmatches.
|
|
||||||
*
|
|
||||||
* In addition, lookahead expressions, and once-only expressions are allowed.
|
|
||||||
* Lookbehind expressions are impossible (scanning always occurs from the
|
|
||||||
* current position forward), and recursion (?R) can't work and is not allowed.
|
|
||||||
*
|
|
||||||
* <code>
|
|
||||||
* /*!lex2php
|
|
||||||
* %counter {$this->N}
|
|
||||||
* %input {$this->data}
|
|
||||||
* %token {$this->token}
|
|
||||||
* %value {$this->value}
|
|
||||||
* %line {%this->linenumber}
|
|
||||||
* alpha = /[a-zA-Z]/
|
|
||||||
* alphaplus = /[a-zA-Z]+/
|
|
||||||
* number = /[0-9]/
|
|
||||||
* numerals = /[0-9]+/
|
|
||||||
* whitespace = /[ \t\n]+/
|
|
||||||
* blah = "$\""
|
|
||||||
* blahblah = /a\$/
|
|
||||||
* GAMEEND = @(?:1\-0|0\-1|1/2\-1/2)@
|
|
||||||
* PAWNMOVE = /P?[a-h]([2-7]|[18]\=(Q|R|B|N))|P?[a-h]x[a-h]([2-7]|[18]\=(Q|R|B|N))/
|
|
||||||
* {@*}
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* All regexes must be delimited. Any legal preg delimiter can be used (as in @ or / in
|
|
||||||
* the example above)
|
|
||||||
*
|
|
||||||
* Rule lex2php blocks each define a lexer state. You can optionally name the state
|
|
||||||
* with the %statename processor instruction. State names can be used to transfer to
|
|
||||||
* a new lexer state with the yybegin() method
|
|
||||||
*
|
|
||||||
* <code>
|
|
||||||
* /*!lexphp
|
|
||||||
* %statename INITIAL
|
|
||||||
* blah {
|
|
||||||
* $this->yybegin(self::INBLAH);
|
|
||||||
* // note - $this->yybegin(2) would also work
|
|
||||||
* }
|
|
||||||
* {@*}
|
|
||||||
* /*!lex2php
|
|
||||||
* %statename INBLAH
|
|
||||||
* ANYTHING {
|
|
||||||
* $this->yybegin(self::INITIAL);
|
|
||||||
* // note - $this->yybegin(1) would also work
|
|
||||||
* }
|
|
||||||
* {@*}
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* You can maintain a parser state stack simply by using yypushstate() and
|
|
||||||
* yypopstate() instead of yybegin():
|
|
||||||
*
|
|
||||||
* <code>
|
|
||||||
* /*!lexphp
|
|
||||||
* %statename INITIAL
|
|
||||||
* blah {
|
|
||||||
* $this->yypushstate(self::INBLAH);
|
|
||||||
* }
|
|
||||||
* {@*}
|
|
||||||
* /*!lex2php
|
|
||||||
* %statename INBLAH
|
|
||||||
* ANYTHING {
|
|
||||||
* $this->yypopstate();
|
|
||||||
* // now INBLAH doesn't care where it was called from
|
|
||||||
* }
|
|
||||||
* {@*}
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* Code blocks can choose to skip the current token and cycle to the next token by
|
|
||||||
* returning "false"
|
|
||||||
*
|
|
||||||
* <code>
|
|
||||||
* /*!lex2php
|
|
||||||
* WHITESPACE {
|
|
||||||
* return false;
|
|
||||||
* }
|
|
||||||
* {@*}
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* If you wish to re-process the current token in a new state, simply return true.
|
|
||||||
* If you forget to change lexer state, this will cause an unterminated loop,
|
|
||||||
* so be careful!
|
|
||||||
*
|
|
||||||
* <code>
|
|
||||||
* /*!lex2php
|
|
||||||
* "(" {
|
|
||||||
* $this->yypushstate(self::INPARAMS);
|
|
||||||
* return true;
|
|
||||||
* }
|
|
||||||
* {@*}
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* Lastly, if you wish to cycle to the next matching rule, return any value other than
|
|
||||||
* true, false or null:
|
|
||||||
*
|
|
||||||
* <code>
|
|
||||||
* /*!lex2php
|
|
||||||
* "{@" ALPHA {
|
|
||||||
* if ($this->value == '{@internal') {
|
|
||||||
* return 'more';
|
|
||||||
* }
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* "{@internal" {
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* {@*}
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* Note that this procedure is exceptionally inefficient, and it would be far better
|
|
||||||
* to take advantage of PHP_LexerGenerator's top-down precedence and instead code:
|
|
||||||
*
|
|
||||||
* <code>
|
|
||||||
* /*!lex2php
|
|
||||||
* "{@internal" {
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* "{@" ALPHA {
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* {@*}
|
|
||||||
* </code>
|
|
||||||
* @package PHP_LexerGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.php.net/license/3_01.txt PHP License 3.01
|
|
||||||
* @version @package_version@
|
|
||||||
* @since Class available since Release 0.1.0
|
|
||||||
* @example TestLexer.plex Example lexer source
|
|
||||||
* @example TestLexer.php Example lexer generated php code
|
|
||||||
* @example usage.php Example usage of PHP_LexerGenerator
|
|
||||||
* @example Lexer.plex File_ChessPGN lexer source (complex)
|
|
||||||
* @example Lexer.php File_ChessPGN lexer generated php code
|
|
||||||
*/
|
|
||||||
|
|
||||||
class PHP_LexerGenerator
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Plex file lexer.
|
|
||||||
* @var PHP_LexerGenerator_Lexer
|
|
||||||
*/
|
|
||||||
private $_lex;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Plex file parser.
|
|
||||||
* @var PHP_LexerGenerator_Parser
|
|
||||||
*/
|
|
||||||
private $_parser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Path to the output PHP file.
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $_outfile;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Debug flag. When set, Parser trace information is generated.
|
|
||||||
* @var boolean
|
|
||||||
*/
|
|
||||||
public $debug = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a lexer generator and optionally generate a lexer file.
|
|
||||||
*
|
|
||||||
* @param string Optional plex file {@see PHP_LexerGenerator::create}.
|
|
||||||
* @param string Optional output file {@see PHP_LexerGenerator::create}.
|
|
||||||
*/
|
|
||||||
function __construct($lexerfile = '', $outfile = '')
|
|
||||||
{
|
|
||||||
if ($lexerfile) {
|
|
||||||
$this -> create($lexerfile, $outfile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a lexer file from its skeleton plex file.
|
|
||||||
*
|
|
||||||
* @param string Path to the plex file.
|
|
||||||
* @param string Optional path to output file. Default is lexerfile with
|
|
||||||
* extension of ".php".
|
|
||||||
*/
|
|
||||||
function create($lexerfile, $outfile = '')
|
|
||||||
{
|
|
||||||
$this->_lex = new PHP_LexerGenerator_Lexer(file_get_contents($lexerfile));
|
|
||||||
$info = pathinfo($lexerfile);
|
|
||||||
if ($outfile) {
|
|
||||||
$this->outfile = $outfile;
|
|
||||||
} else {
|
|
||||||
$this->outfile = $info['dirname'] . DIRECTORY_SEPARATOR .
|
|
||||||
substr($info['basename'], 0,
|
|
||||||
strlen($info['basename']) - strlen($info['extension'])) . 'php';
|
|
||||||
}
|
|
||||||
$this->_parser = new PHP_LexerGenerator_Parser($this->outfile, $this->_lex);
|
|
||||||
if ($this -> debug) {
|
|
||||||
$this->_parser->PrintTrace();
|
|
||||||
}
|
|
||||||
while ($this->_lex->advance($this->_parser)) {
|
|
||||||
$this->_parser->doParse($this->_lex->token, $this->_lex->value);
|
|
||||||
}
|
|
||||||
$this->_parser->doParse(0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//$a = new PHP_LexerGenerator('/development/File_ChessPGN/ChessPGN/Lexer.plex');
|
|
||||||
?>
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* PHP_LexerGenerator, a php 5 lexer generator.
|
|
||||||
*
|
|
||||||
* Exception classes for the lexer generator
|
|
||||||
*
|
|
||||||
* PHP version 5
|
|
||||||
*
|
|
||||||
* LICENSE:
|
|
||||||
*
|
|
||||||
* Copyright (c) 2006, Gregory Beaver <cellog@php.net>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in
|
|
||||||
* the documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the PHP_LexerGenerator nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived
|
|
||||||
* from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
||||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
||||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* @category php
|
|
||||||
* @package PHP_LexerGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
*/
|
|
||||||
require_once 'PEAR/Exception.php';
|
|
||||||
/**
|
|
||||||
* @package PHP_LexerGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version @package_version@
|
|
||||||
* @since File available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
class PHP_LexerGenerator_Exception extends PEAR_Exception {}
|
|
||||||
?>
|
|
||||||
@@ -1,533 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* PHP_LexerGenerator, a php 5 lexer generator.
|
|
||||||
*
|
|
||||||
* This lexer generator translates a file in a format similar to
|
|
||||||
* re2c ({@link http://re2c.org}) and translates it into a PHP 5-based lexer
|
|
||||||
*
|
|
||||||
* PHP version 5
|
|
||||||
*
|
|
||||||
* LICENSE: This source file is subject to version 3.01 of the PHP license
|
|
||||||
* that is available through the world-wide-web at the following URI:
|
|
||||||
* http://www.php.net/license/3_01.txt. If you did not receive a copy of
|
|
||||||
* the PHP License and are unable to obtain it through the web, please
|
|
||||||
* send a note to license@php.net so we can mail you a copy immediately.
|
|
||||||
*
|
|
||||||
* @category php
|
|
||||||
* @package PHP_LexerGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.php.net/license/3_01.txt PHP License 3.01
|
|
||||||
* @version CVS: $Id: Lexer.php 246683 2007-11-22 04:43:52Z instance $
|
|
||||||
* @since File available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
require_once 'PHP/LexerGenerator/Parser.php';
|
|
||||||
/**
|
|
||||||
* Token scanner for plex files.
|
|
||||||
*
|
|
||||||
* This scanner detects comments beginning with "/*!lex2php" and
|
|
||||||
* then returns their components (processing instructions, patterns, strings
|
|
||||||
* action code, and regexes)
|
|
||||||
* @package PHP_LexerGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.php.net/license/3_01.txt PHP License 3.01
|
|
||||||
* @version @package_version@
|
|
||||||
* @since Class available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
class PHP_LexerGenerator_Lexer
|
|
||||||
{
|
|
||||||
private $data;
|
|
||||||
private $N;
|
|
||||||
private $state;
|
|
||||||
/**
|
|
||||||
* Current line number in input
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $line;
|
|
||||||
/**
|
|
||||||
* Number of scanning errors detected
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $errors = 0;
|
|
||||||
/**
|
|
||||||
* integer identifier of the current token
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $token;
|
|
||||||
/**
|
|
||||||
* string content of current token
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $value;
|
|
||||||
|
|
||||||
const CODE = PHP_LexerGenerator_Parser::CODE;
|
|
||||||
const COMMENTEND = PHP_LexerGenerator_Parser::COMMENTEND;
|
|
||||||
const COMMENTSTART = PHP_LexerGenerator_Parser::COMMENTSTART;
|
|
||||||
const PATTERN = PHP_LexerGenerator_Parser::PATTERN;
|
|
||||||
const PHPCODE = PHP_LexerGenerator_Parser::PHPCODE;
|
|
||||||
const PI = PHP_LexerGenerator_Parser::PI;
|
|
||||||
const QUOTE = PHP_LexerGenerator_Parser::QUOTE;
|
|
||||||
const SINGLEQUOTE = PHP_LexerGenerator_Parser::SINGLEQUOTE;
|
|
||||||
const SUBPATTERN = PHP_LexerGenerator_Parser::SUBPATTERN;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* prepare scanning
|
|
||||||
* @param string the input
|
|
||||||
*/
|
|
||||||
function __construct($data)
|
|
||||||
{
|
|
||||||
$this->data = str_replace("\r\n", "\n", $data);
|
|
||||||
$this->N = 0;
|
|
||||||
$this->line = 1;
|
|
||||||
$this->state = 'Start';
|
|
||||||
$this->errors = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Output an error message
|
|
||||||
* @param string
|
|
||||||
*/
|
|
||||||
private function error($msg)
|
|
||||||
{
|
|
||||||
echo 'Error on line ' . $this->line . ': ' . $msg;
|
|
||||||
$this->errors++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initial scanning state lexer
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
private function lexStart()
|
|
||||||
{
|
|
||||||
if ($this->N >= strlen($this->data)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$a = strpos($this->data, '/*!lex2php' . "\n", $this->N);
|
|
||||||
if ($a === false) {
|
|
||||||
$this->value = substr($this->data, $this->N);
|
|
||||||
$this->N = strlen($this->data);
|
|
||||||
$this->token = self::PHPCODE;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ($a > $this->N) {
|
|
||||||
$this->value = substr($this->data, $this->N, $a - $this->N);
|
|
||||||
$this->N = $a;
|
|
||||||
$this->token = self::PHPCODE;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$this->value = '/*!lex2php' . "\n";
|
|
||||||
$this->N += 11; // strlen("/*lex2php\n")
|
|
||||||
$this->token = self::COMMENTSTART;
|
|
||||||
$this->state = 'Declare';
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* lexer for top-level canning state after the initial declaration comment
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
private function lexStartNonDeclare()
|
|
||||||
{
|
|
||||||
if ($this->N >= strlen($this->data)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$a = strpos($this->data, '/*!lex2php' . "\n", $this->N);
|
|
||||||
if ($a === false) {
|
|
||||||
$this->value = substr($this->data, $this->N);
|
|
||||||
$this->N = strlen($this->data);
|
|
||||||
$this->token = self::PHPCODE;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ($a > $this->N) {
|
|
||||||
$this->value = substr($this->data, $this->N, $a - $this->N);
|
|
||||||
$this->N = $a;
|
|
||||||
$this->token = self::PHPCODE;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$this->value = '/*!lex2php' . "\n";
|
|
||||||
$this->N += 11; // strlen("/*lex2php\n")
|
|
||||||
$this->token = self::COMMENTSTART;
|
|
||||||
$this->state = 'Rule';
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* lexer for declaration comment state
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
private function lexDeclare()
|
|
||||||
{
|
|
||||||
while (true) {
|
|
||||||
$this -> skipWhitespaceEol();
|
|
||||||
if (
|
|
||||||
$this->N + 1 >= strlen($this->data)
|
|
||||||
|| $this->data[$this->N] != '/'
|
|
||||||
|| $this->data[$this->N + 1] != '/'
|
|
||||||
) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Skip single-line comment
|
|
||||||
while (
|
|
||||||
$this->N < strlen($this->data)
|
|
||||||
&& $this->data[$this->N] != "\n"
|
|
||||||
) {
|
|
||||||
++$this->N;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($this->data[$this->N] == '*' && $this->data[$this->N + 1] == '/') {
|
|
||||||
$this->state = 'StartNonDeclare';
|
|
||||||
$this->value = '*/';
|
|
||||||
$this->N += 2;
|
|
||||||
$this->token = self::COMMENTEND;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (preg_match('/\G%([a-z]+)/', $this->data, $token, null, $this->N)) {
|
|
||||||
$this->value = $token[1];
|
|
||||||
$this->N += strlen($token[1]) + 1;
|
|
||||||
$this->state = 'DeclarePI';
|
|
||||||
$this->token = self::PI;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (preg_match('/\G[a-zA-Z_][a-zA-Z0-9_]*/', $this->data, $token, null, $this->N)) {
|
|
||||||
$this->value = $token[0];
|
|
||||||
$this->token = self::PATTERN;
|
|
||||||
$this->N += strlen($token[0]);
|
|
||||||
$this->state = 'DeclareEquals';
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$this->error('expecting declaration of sub-patterns');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* lexer for processor instructions within declaration comment
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
private function lexDeclarePI()
|
|
||||||
{
|
|
||||||
$this -> skipWhitespace();
|
|
||||||
if ($this->data[$this->N] == "\n") {
|
|
||||||
$this->N++;
|
|
||||||
$this->state = 'Declare';
|
|
||||||
$this->line++;
|
|
||||||
return $this->lexDeclare();
|
|
||||||
}
|
|
||||||
if ($this->data[$this->N] == '{') {
|
|
||||||
return $this->lexCode();
|
|
||||||
}
|
|
||||||
if (!preg_match("/\G[^\n]+/", $this->data, $token, null, $this->N)) {
|
|
||||||
$this->error('Unexpected end of file');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->value = $token[0];
|
|
||||||
$this->N += strlen($this->value);
|
|
||||||
$this->token = self::SUBPATTERN;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* lexer for processor instructions inside rule comments
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
private function lexDeclarePIRule()
|
|
||||||
{
|
|
||||||
$this -> skipWhitespace();
|
|
||||||
if ($this->data[$this->N] == "\n") {
|
|
||||||
$this->N++;
|
|
||||||
$this->state = 'Rule';
|
|
||||||
$this->line++;
|
|
||||||
return $this->lexRule();
|
|
||||||
}
|
|
||||||
if ($this->data[$this->N] == '{') {
|
|
||||||
return $this->lexCode();
|
|
||||||
}
|
|
||||||
if (!preg_match("/\G[^\n]+/", $this->data, $token, null, $this->N)) {
|
|
||||||
$this->error('Unexpected end of file');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->value = $token[0];
|
|
||||||
$this->N += strlen($this->value);
|
|
||||||
$this->token = self::SUBPATTERN;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* lexer for the state representing scanning between a pattern and the "=" sign
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
private function lexDeclareEquals()
|
|
||||||
{
|
|
||||||
$this -> skipWhitespace();
|
|
||||||
if ($this->N >= strlen($this->data)) {
|
|
||||||
$this->error('unexpected end of input, expecting "=" for sub-pattern declaration');
|
|
||||||
}
|
|
||||||
if ($this->data[$this->N] != '=') {
|
|
||||||
$this->error('expecting "=" for sub-pattern declaration');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->N++;
|
|
||||||
$this->state = 'DeclareRightside';
|
|
||||||
$this -> skipWhitespace();
|
|
||||||
if ($this->N >= strlen($this->data)) {
|
|
||||||
$this->error('unexpected end of file, expecting right side of sub-pattern declaration');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return $this->lexDeclareRightside();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* lexer for the right side of a pattern, detects quotes or regexes
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
private function lexDeclareRightside()
|
|
||||||
{
|
|
||||||
if ($this->data[$this->N] == "\n") {
|
|
||||||
$this->state = 'lexDeclare';
|
|
||||||
$this->N++;
|
|
||||||
$this->line++;
|
|
||||||
return $this->lexDeclare();
|
|
||||||
}
|
|
||||||
if ($this->data[$this->N] == '"') {
|
|
||||||
return $this->lexQuote();
|
|
||||||
}
|
|
||||||
if ($this->data[$this->N] == '\'') {
|
|
||||||
return $this->lexQuote('\'');
|
|
||||||
}
|
|
||||||
$this -> skipWhitespace();
|
|
||||||
// match a pattern
|
|
||||||
$test = $this->data[$this->N];
|
|
||||||
$token = $this->N + 1;
|
|
||||||
$a = 0;
|
|
||||||
do {
|
|
||||||
if ($a++) {
|
|
||||||
$token++;
|
|
||||||
}
|
|
||||||
$token = strpos($this->data, $test, $token);
|
|
||||||
} while ($token !== false && ($this->data[$token - 1] == '\\'
|
|
||||||
&& $this->data[$token - 2] != '\\'));
|
|
||||||
if ($token === false) {
|
|
||||||
$this->error('Unterminated regex pattern (started with "' . $test . '"');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (substr_count($this->data, "\n", $this->N, $token - $this->N)) {
|
|
||||||
$this->error('Regex pattern extends over multiple lines');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->value = substr($this->data, $this->N + 1, $token - $this->N - 1);
|
|
||||||
// unescape the regex marker
|
|
||||||
// we will re-escape when creating the final regex
|
|
||||||
$this->value = str_replace('\\' . $test, $test, $this->value);
|
|
||||||
$this->N = $token + 1;
|
|
||||||
$this->token = self::SUBPATTERN;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* lexer for quoted literals
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
private function lexQuote($quote = '"')
|
|
||||||
{
|
|
||||||
$token = $this->N + 1;
|
|
||||||
$a = 0;
|
|
||||||
do {
|
|
||||||
if ($a++) {
|
|
||||||
$token++;
|
|
||||||
}
|
|
||||||
$token = strpos($this->data, $quote, $token);
|
|
||||||
} while ($token !== false && $token < strlen($this->data) &&
|
|
||||||
($this->data[$token - 1] == '\\' && $this->data[$token - 2] != '\\'));
|
|
||||||
if ($token === false) {
|
|
||||||
$this->error('unterminated quote');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (substr_count($this->data, "\n", $this->N, $token - $this->N)) {
|
|
||||||
$this->error('quote extends over multiple lines');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->value = substr($this->data, $this->N + 1, $token - $this->N - 1);
|
|
||||||
$this->value = str_replace('\\'.$quote, $quote, $this->value);
|
|
||||||
$this->value = str_replace('\\\\', '\\', $this->value);
|
|
||||||
$this->N = $token + 1;
|
|
||||||
if ($quote == '\'' ) {
|
|
||||||
$this->token = self::SINGLEQUOTE;
|
|
||||||
} else {
|
|
||||||
$this->token = self::QUOTE;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* lexer for rules
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
private function lexRule()
|
|
||||||
{
|
|
||||||
while (
|
|
||||||
$this->N < strlen($this->data)
|
|
||||||
&& (
|
|
||||||
$this->data[$this->N] == ' '
|
|
||||||
|| $this->data[$this->N] == "\t"
|
|
||||||
|| $this->data[$this->N] == "\n"
|
|
||||||
) || (
|
|
||||||
$this->N < strlen($this->data) - 1
|
|
||||||
&& $this->data[$this->N] == '/'
|
|
||||||
&& $this->data[$this->N + 1] == '/'
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
if ( $this->data[$this->N] == '/' && $this->data[$this->N + 1] == '/' ) {
|
|
||||||
// Skip single line comments
|
|
||||||
$next_newline = strpos($this->data, "\n", $this->N) + 1;
|
|
||||||
if ($next_newline) {
|
|
||||||
$this->N = $next_newline;
|
|
||||||
} else {
|
|
||||||
$this->N = sizeof($this->data);
|
|
||||||
}
|
|
||||||
$this->line++;
|
|
||||||
} else {
|
|
||||||
if ($this->data[$this->N] == "\n") {
|
|
||||||
$this->line++;
|
|
||||||
}
|
|
||||||
$this->N++; // skip all whitespace
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->N >= strlen($this->data)) {
|
|
||||||
$this->error('unexpected end of input, expecting rule declaration');
|
|
||||||
}
|
|
||||||
if ($this->data[$this->N] == '*' && $this->data[$this->N + 1] == '/') {
|
|
||||||
$this->state = 'StartNonDeclare';
|
|
||||||
$this->value = '*/';
|
|
||||||
$this->N += 2;
|
|
||||||
$this->token = self::COMMENTEND;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ($this->data[$this->N] == '\'') {
|
|
||||||
return $this->lexQuote('\'');
|
|
||||||
}
|
|
||||||
if (preg_match('/\G%([a-zA-Z_]+)/', $this->data, $token, null, $this->N)) {
|
|
||||||
$this->value = $token[1];
|
|
||||||
$this->N += strlen($token[1]) + 1;
|
|
||||||
$this->state = 'DeclarePIRule';
|
|
||||||
$this->token = self::PI;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ($this->data[$this->N] == "{") {
|
|
||||||
return $this->lexCode();
|
|
||||||
}
|
|
||||||
if ($this->data[$this->N] == '"') {
|
|
||||||
return $this->lexQuote();
|
|
||||||
}
|
|
||||||
if (preg_match('/\G[a-zA-Z_][a-zA-Z0-9_]*/', $this->data, $token, null, $this->N)) {
|
|
||||||
$this->value = $token[0];
|
|
||||||
$this->N += strlen($token[0]);
|
|
||||||
$this->token = self::SUBPATTERN;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
$this->error('expecting token rule (quotes or sub-patterns)');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* lexer for php code blocks
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
private function lexCode()
|
|
||||||
{
|
|
||||||
$cp = $this->N + 1;
|
|
||||||
for ($level = 1; $cp < strlen($this->data) && ($level > 1 || $this->data[$cp] != '}'); $cp++) {
|
|
||||||
if ($this->data[$cp] == '{') {
|
|
||||||
$level++;
|
|
||||||
} elseif ($this->data[$cp] == '}') {
|
|
||||||
$level--;
|
|
||||||
} elseif ($this->data[$cp] == '/' && $this->data[$cp + 1] == '/') {
|
|
||||||
/* Skip C++ style comments */
|
|
||||||
$cp += 2;
|
|
||||||
$z = strpos($this->data, "\n", $cp);
|
|
||||||
if ($z === false) {
|
|
||||||
$cp = strlen($this->data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$cp = $z;
|
|
||||||
} elseif ($this->data[$cp] == "'" || $this->data[$cp] == '"') {
|
|
||||||
/* String a character literals */
|
|
||||||
$startchar = $this->data[$cp];
|
|
||||||
$prevc = 0;
|
|
||||||
for ($cp++; $cp < strlen($this->data) && ($this->data[$cp] != $startchar || $prevc === '\\'); $cp++) {
|
|
||||||
if ($prevc === '\\') {
|
|
||||||
$prevc = 0;
|
|
||||||
} else {
|
|
||||||
$prevc = $this->data[$cp];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($cp >= strlen($this->data)) {
|
|
||||||
$this->error("PHP code starting on this line is not terminated before the end of the file.");
|
|
||||||
$this->error++;
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
$this->value = substr($this->data, $this->N + 1, $cp - $this->N - 1);
|
|
||||||
$this->token = self::CODE;
|
|
||||||
$this->N = $cp + 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Skip whitespace characters
|
|
||||||
*/
|
|
||||||
private function skipWhitespace() {
|
|
||||||
while (
|
|
||||||
$this->N < strlen($this->data)
|
|
||||||
&& (
|
|
||||||
$this->data[$this->N] == ' '
|
|
||||||
|| $this->data[$this->N] == "\t"
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
$this->N++; // skip whitespace
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Skip whitespace and EOL characters
|
|
||||||
*/
|
|
||||||
private function skipWhitespaceEol() {
|
|
||||||
while (
|
|
||||||
$this->N < strlen($this->data)
|
|
||||||
&& (
|
|
||||||
$this->data[$this->N] == ' '
|
|
||||||
|| $this->data[$this->N] == "\t"
|
|
||||||
|| $this->data[$this->N] == "\n"
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
if ($this->data[$this->N] == "\n") {
|
|
||||||
++$this -> line;
|
|
||||||
}
|
|
||||||
$this->N++; // skip whitespace
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Primary scanner
|
|
||||||
*
|
|
||||||
* In addition to lexing, this properly increments the line number of lexing.
|
|
||||||
* This calls the proper sub-lexer based on the parser state
|
|
||||||
* @param unknown_type $parser
|
|
||||||
* @return unknown
|
|
||||||
*/
|
|
||||||
public function advance($parser)
|
|
||||||
{
|
|
||||||
if ($this->N >= strlen($this->data)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ($this->{'lex' . $this->state}()) {
|
|
||||||
$this->line += substr_count($this->value, "\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@@ -1,492 +0,0 @@
|
|||||||
State 0:
|
|
||||||
start ::= * lexfile
|
|
||||||
lexfile ::= * declare rules
|
|
||||||
lexfile ::= * declare PHPCODE rules
|
|
||||||
lexfile ::= * PHPCODE declare rules
|
|
||||||
lexfile ::= * PHPCODE declare PHPCODE rules
|
|
||||||
declare ::= * COMMENTSTART declarations COMMENTEND
|
|
||||||
|
|
||||||
PHPCODE shift 17
|
|
||||||
COMMENTSTART shift 8
|
|
||||||
start accept
|
|
||||||
lexfile shift 52
|
|
||||||
declare shift 6
|
|
||||||
|
|
||||||
State 1:
|
|
||||||
rules ::= reset_rules * rule COMMENTEND
|
|
||||||
rules ::= reset_rules * PI SUBPATTERN rule COMMENTEND
|
|
||||||
rules ::= reset_rules * rule COMMENTEND PHPCODE
|
|
||||||
rules ::= reset_rules * PI SUBPATTERN rule COMMENTEND PHPCODE
|
|
||||||
rule ::= * rule_subpattern CODE
|
|
||||||
rule ::= * rule rule_subpattern CODE
|
|
||||||
rule_subpattern ::= * QUOTE
|
|
||||||
rule_subpattern ::= * SUBPATTERN
|
|
||||||
rule_subpattern ::= * rule_subpattern QUOTE
|
|
||||||
rule_subpattern ::= * rule_subpattern SUBPATTERN
|
|
||||||
|
|
||||||
PI shift 29
|
|
||||||
SUBPATTERN shift 50
|
|
||||||
QUOTE shift 51
|
|
||||||
rule shift 12
|
|
||||||
rule_subpattern shift 18
|
|
||||||
|
|
||||||
State 2:
|
|
||||||
rules ::= COMMENTSTART * rule COMMENTEND
|
|
||||||
rules ::= COMMENTSTART * PI SUBPATTERN rule COMMENTEND
|
|
||||||
rules ::= COMMENTSTART * rule COMMENTEND PHPCODE
|
|
||||||
rules ::= COMMENTSTART * PI SUBPATTERN rule COMMENTEND PHPCODE
|
|
||||||
rule ::= * rule_subpattern CODE
|
|
||||||
rule ::= * rule rule_subpattern CODE
|
|
||||||
rule_subpattern ::= * QUOTE
|
|
||||||
rule_subpattern ::= * SUBPATTERN
|
|
||||||
rule_subpattern ::= * rule_subpattern QUOTE
|
|
||||||
rule_subpattern ::= * rule_subpattern SUBPATTERN
|
|
||||||
|
|
||||||
PI shift 34
|
|
||||||
SUBPATTERN shift 50
|
|
||||||
QUOTE shift 51
|
|
||||||
rule shift 11
|
|
||||||
rule_subpattern shift 18
|
|
||||||
|
|
||||||
State 3:
|
|
||||||
rules ::= COMMENTSTART PI SUBPATTERN * rule COMMENTEND
|
|
||||||
rules ::= COMMENTSTART PI SUBPATTERN * rule COMMENTEND PHPCODE
|
|
||||||
rule ::= * rule_subpattern CODE
|
|
||||||
rule ::= * rule rule_subpattern CODE
|
|
||||||
rule_subpattern ::= * QUOTE
|
|
||||||
rule_subpattern ::= * SUBPATTERN
|
|
||||||
rule_subpattern ::= * rule_subpattern QUOTE
|
|
||||||
rule_subpattern ::= * rule_subpattern SUBPATTERN
|
|
||||||
|
|
||||||
SUBPATTERN shift 50
|
|
||||||
QUOTE shift 51
|
|
||||||
rule shift 10
|
|
||||||
rule_subpattern shift 18
|
|
||||||
|
|
||||||
State 4:
|
|
||||||
lexfile ::= PHPCODE declare * rules
|
|
||||||
lexfile ::= PHPCODE declare * PHPCODE rules
|
|
||||||
rules ::= * COMMENTSTART rule COMMENTEND
|
|
||||||
rules ::= * COMMENTSTART PI SUBPATTERN rule COMMENTEND
|
|
||||||
rules ::= * COMMENTSTART rule COMMENTEND PHPCODE
|
|
||||||
rules ::= * COMMENTSTART PI SUBPATTERN rule COMMENTEND PHPCODE
|
|
||||||
rules ::= * reset_rules rule COMMENTEND
|
|
||||||
rules ::= * reset_rules PI SUBPATTERN rule COMMENTEND
|
|
||||||
rules ::= * reset_rules rule COMMENTEND PHPCODE
|
|
||||||
rules ::= * reset_rules PI SUBPATTERN rule COMMENTEND PHPCODE
|
|
||||||
reset_rules ::= * rules COMMENTSTART
|
|
||||||
|
|
||||||
PHPCODE shift 7
|
|
||||||
COMMENTSTART shift 2
|
|
||||||
rules shift 25
|
|
||||||
reset_rules shift 1
|
|
||||||
|
|
||||||
State 5:
|
|
||||||
rules ::= reset_rules PI SUBPATTERN * rule COMMENTEND
|
|
||||||
rules ::= reset_rules PI SUBPATTERN * rule COMMENTEND PHPCODE
|
|
||||||
rule ::= * rule_subpattern CODE
|
|
||||||
rule ::= * rule rule_subpattern CODE
|
|
||||||
rule_subpattern ::= * QUOTE
|
|
||||||
rule_subpattern ::= * SUBPATTERN
|
|
||||||
rule_subpattern ::= * rule_subpattern QUOTE
|
|
||||||
rule_subpattern ::= * rule_subpattern SUBPATTERN
|
|
||||||
|
|
||||||
SUBPATTERN shift 50
|
|
||||||
QUOTE shift 51
|
|
||||||
rule shift 13
|
|
||||||
rule_subpattern shift 18
|
|
||||||
|
|
||||||
State 6:
|
|
||||||
lexfile ::= declare * rules
|
|
||||||
lexfile ::= declare * PHPCODE rules
|
|
||||||
rules ::= * COMMENTSTART rule COMMENTEND
|
|
||||||
rules ::= * COMMENTSTART PI SUBPATTERN rule COMMENTEND
|
|
||||||
rules ::= * COMMENTSTART rule COMMENTEND PHPCODE
|
|
||||||
rules ::= * COMMENTSTART PI SUBPATTERN rule COMMENTEND PHPCODE
|
|
||||||
rules ::= * reset_rules rule COMMENTEND
|
|
||||||
rules ::= * reset_rules PI SUBPATTERN rule COMMENTEND
|
|
||||||
rules ::= * reset_rules rule COMMENTEND PHPCODE
|
|
||||||
rules ::= * reset_rules PI SUBPATTERN rule COMMENTEND PHPCODE
|
|
||||||
reset_rules ::= * rules COMMENTSTART
|
|
||||||
|
|
||||||
PHPCODE shift 9
|
|
||||||
COMMENTSTART shift 2
|
|
||||||
rules shift 33
|
|
||||||
reset_rules shift 1
|
|
||||||
|
|
||||||
State 7:
|
|
||||||
lexfile ::= PHPCODE declare PHPCODE * rules
|
|
||||||
rules ::= * COMMENTSTART rule COMMENTEND
|
|
||||||
rules ::= * COMMENTSTART PI SUBPATTERN rule COMMENTEND
|
|
||||||
rules ::= * COMMENTSTART rule COMMENTEND PHPCODE
|
|
||||||
rules ::= * COMMENTSTART PI SUBPATTERN rule COMMENTEND PHPCODE
|
|
||||||
rules ::= * reset_rules rule COMMENTEND
|
|
||||||
rules ::= * reset_rules PI SUBPATTERN rule COMMENTEND
|
|
||||||
rules ::= * reset_rules rule COMMENTEND PHPCODE
|
|
||||||
rules ::= * reset_rules PI SUBPATTERN rule COMMENTEND PHPCODE
|
|
||||||
reset_rules ::= * rules COMMENTSTART
|
|
||||||
|
|
||||||
COMMENTSTART shift 2
|
|
||||||
rules shift 27
|
|
||||||
reset_rules shift 1
|
|
||||||
|
|
||||||
State 8:
|
|
||||||
declare ::= COMMENTSTART * declarations COMMENTEND
|
|
||||||
declarations ::= * processing_instructions pattern_declarations
|
|
||||||
processing_instructions ::= * PI SUBPATTERN
|
|
||||||
processing_instructions ::= * PI CODE
|
|
||||||
processing_instructions ::= * processing_instructions PI SUBPATTERN
|
|
||||||
processing_instructions ::= * processing_instructions PI CODE
|
|
||||||
|
|
||||||
PI shift 23
|
|
||||||
declarations shift 28
|
|
||||||
processing_instructions shift 14
|
|
||||||
|
|
||||||
State 9:
|
|
||||||
lexfile ::= declare PHPCODE * rules
|
|
||||||
rules ::= * COMMENTSTART rule COMMENTEND
|
|
||||||
rules ::= * COMMENTSTART PI SUBPATTERN rule COMMENTEND
|
|
||||||
rules ::= * COMMENTSTART rule COMMENTEND PHPCODE
|
|
||||||
rules ::= * COMMENTSTART PI SUBPATTERN rule COMMENTEND PHPCODE
|
|
||||||
rules ::= * reset_rules rule COMMENTEND
|
|
||||||
rules ::= * reset_rules PI SUBPATTERN rule COMMENTEND
|
|
||||||
rules ::= * reset_rules rule COMMENTEND PHPCODE
|
|
||||||
rules ::= * reset_rules PI SUBPATTERN rule COMMENTEND PHPCODE
|
|
||||||
reset_rules ::= * rules COMMENTSTART
|
|
||||||
|
|
||||||
COMMENTSTART shift 2
|
|
||||||
rules shift 31
|
|
||||||
reset_rules shift 1
|
|
||||||
|
|
||||||
State 10:
|
|
||||||
rules ::= COMMENTSTART PI SUBPATTERN rule * COMMENTEND
|
|
||||||
rules ::= COMMENTSTART PI SUBPATTERN rule * COMMENTEND PHPCODE
|
|
||||||
rule ::= rule * rule_subpattern CODE
|
|
||||||
rule_subpattern ::= * QUOTE
|
|
||||||
rule_subpattern ::= * SUBPATTERN
|
|
||||||
rule_subpattern ::= * rule_subpattern QUOTE
|
|
||||||
rule_subpattern ::= * rule_subpattern SUBPATTERN
|
|
||||||
|
|
||||||
COMMENTEND shift 30
|
|
||||||
SUBPATTERN shift 50
|
|
||||||
QUOTE shift 51
|
|
||||||
rule_subpattern shift 19
|
|
||||||
|
|
||||||
State 11:
|
|
||||||
rules ::= COMMENTSTART rule * COMMENTEND
|
|
||||||
rules ::= COMMENTSTART rule * COMMENTEND PHPCODE
|
|
||||||
rule ::= rule * rule_subpattern CODE
|
|
||||||
rule_subpattern ::= * QUOTE
|
|
||||||
rule_subpattern ::= * SUBPATTERN
|
|
||||||
rule_subpattern ::= * rule_subpattern QUOTE
|
|
||||||
rule_subpattern ::= * rule_subpattern SUBPATTERN
|
|
||||||
|
|
||||||
COMMENTEND shift 32
|
|
||||||
SUBPATTERN shift 50
|
|
||||||
QUOTE shift 51
|
|
||||||
rule_subpattern shift 19
|
|
||||||
|
|
||||||
State 12:
|
|
||||||
rules ::= reset_rules rule * COMMENTEND
|
|
||||||
rules ::= reset_rules rule * COMMENTEND PHPCODE
|
|
||||||
rule ::= rule * rule_subpattern CODE
|
|
||||||
rule_subpattern ::= * QUOTE
|
|
||||||
rule_subpattern ::= * SUBPATTERN
|
|
||||||
rule_subpattern ::= * rule_subpattern QUOTE
|
|
||||||
rule_subpattern ::= * rule_subpattern SUBPATTERN
|
|
||||||
|
|
||||||
COMMENTEND shift 35
|
|
||||||
SUBPATTERN shift 50
|
|
||||||
QUOTE shift 51
|
|
||||||
rule_subpattern shift 19
|
|
||||||
|
|
||||||
State 13:
|
|
||||||
rules ::= reset_rules PI SUBPATTERN rule * COMMENTEND
|
|
||||||
rules ::= reset_rules PI SUBPATTERN rule * COMMENTEND PHPCODE
|
|
||||||
rule ::= rule * rule_subpattern CODE
|
|
||||||
rule_subpattern ::= * QUOTE
|
|
||||||
rule_subpattern ::= * SUBPATTERN
|
|
||||||
rule_subpattern ::= * rule_subpattern QUOTE
|
|
||||||
rule_subpattern ::= * rule_subpattern SUBPATTERN
|
|
||||||
|
|
||||||
COMMENTEND shift 24
|
|
||||||
SUBPATTERN shift 50
|
|
||||||
QUOTE shift 51
|
|
||||||
rule_subpattern shift 19
|
|
||||||
|
|
||||||
State 14:
|
|
||||||
declarations ::= processing_instructions * pattern_declarations
|
|
||||||
processing_instructions ::= processing_instructions * PI SUBPATTERN
|
|
||||||
processing_instructions ::= processing_instructions * PI CODE
|
|
||||||
pattern_declarations ::= * PATTERN subpattern
|
|
||||||
pattern_declarations ::= * pattern_declarations PATTERN subpattern
|
|
||||||
|
|
||||||
PI shift 20
|
|
||||||
PATTERN shift 16
|
|
||||||
pattern_declarations shift 26
|
|
||||||
|
|
||||||
State 15:
|
|
||||||
pattern_declarations ::= pattern_declarations PATTERN * subpattern
|
|
||||||
subpattern ::= * QUOTE
|
|
||||||
subpattern ::= * SUBPATTERN
|
|
||||||
subpattern ::= * subpattern QUOTE
|
|
||||||
subpattern ::= * subpattern SUBPATTERN
|
|
||||||
|
|
||||||
SUBPATTERN shift 36
|
|
||||||
QUOTE shift 37
|
|
||||||
subpattern shift 21
|
|
||||||
|
|
||||||
State 16:
|
|
||||||
pattern_declarations ::= PATTERN * subpattern
|
|
||||||
subpattern ::= * QUOTE
|
|
||||||
subpattern ::= * SUBPATTERN
|
|
||||||
subpattern ::= * subpattern QUOTE
|
|
||||||
subpattern ::= * subpattern SUBPATTERN
|
|
||||||
|
|
||||||
SUBPATTERN shift 36
|
|
||||||
QUOTE shift 37
|
|
||||||
subpattern shift 22
|
|
||||||
|
|
||||||
State 17:
|
|
||||||
lexfile ::= PHPCODE * declare rules
|
|
||||||
lexfile ::= PHPCODE * declare PHPCODE rules
|
|
||||||
declare ::= * COMMENTSTART declarations COMMENTEND
|
|
||||||
|
|
||||||
COMMENTSTART shift 8
|
|
||||||
declare shift 4
|
|
||||||
|
|
||||||
State 18:
|
|
||||||
rule ::= rule_subpattern * CODE
|
|
||||||
rule_subpattern ::= rule_subpattern * QUOTE
|
|
||||||
rule_subpattern ::= rule_subpattern * SUBPATTERN
|
|
||||||
|
|
||||||
SUBPATTERN shift 54
|
|
||||||
CODE shift 47
|
|
||||||
QUOTE shift 53
|
|
||||||
|
|
||||||
State 19:
|
|
||||||
rule ::= rule rule_subpattern * CODE
|
|
||||||
rule_subpattern ::= rule_subpattern * QUOTE
|
|
||||||
rule_subpattern ::= rule_subpattern * SUBPATTERN
|
|
||||||
|
|
||||||
SUBPATTERN shift 54
|
|
||||||
CODE shift 45
|
|
||||||
QUOTE shift 53
|
|
||||||
|
|
||||||
State 20:
|
|
||||||
processing_instructions ::= processing_instructions PI * SUBPATTERN
|
|
||||||
processing_instructions ::= processing_instructions PI * CODE
|
|
||||||
|
|
||||||
SUBPATTERN shift 44
|
|
||||||
CODE shift 40
|
|
||||||
|
|
||||||
State 21:
|
|
||||||
(12) pattern_declarations ::= pattern_declarations PATTERN subpattern *
|
|
||||||
subpattern ::= subpattern * QUOTE
|
|
||||||
subpattern ::= subpattern * SUBPATTERN
|
|
||||||
|
|
||||||
SUBPATTERN shift 38
|
|
||||||
QUOTE shift 41
|
|
||||||
{default} reduce 12
|
|
||||||
|
|
||||||
State 22:
|
|
||||||
(11) pattern_declarations ::= PATTERN subpattern *
|
|
||||||
subpattern ::= subpattern * QUOTE
|
|
||||||
subpattern ::= subpattern * SUBPATTERN
|
|
||||||
|
|
||||||
SUBPATTERN shift 38
|
|
||||||
QUOTE shift 41
|
|
||||||
{default} reduce 11
|
|
||||||
|
|
||||||
State 23:
|
|
||||||
processing_instructions ::= PI * SUBPATTERN
|
|
||||||
processing_instructions ::= PI * CODE
|
|
||||||
|
|
||||||
SUBPATTERN shift 42
|
|
||||||
CODE shift 39
|
|
||||||
|
|
||||||
State 24:
|
|
||||||
(18) rules ::= reset_rules PI SUBPATTERN rule COMMENTEND *
|
|
||||||
rules ::= reset_rules PI SUBPATTERN rule COMMENTEND * PHPCODE
|
|
||||||
|
|
||||||
PHPCODE shift 48
|
|
||||||
{default} reduce 18
|
|
||||||
|
|
||||||
State 25:
|
|
||||||
(3) lexfile ::= PHPCODE declare rules *
|
|
||||||
reset_rules ::= rules * COMMENTSTART
|
|
||||||
|
|
||||||
COMMENTSTART shift 43
|
|
||||||
{default} reduce 3
|
|
||||||
|
|
||||||
State 26:
|
|
||||||
(6) declarations ::= processing_instructions pattern_declarations *
|
|
||||||
pattern_declarations ::= pattern_declarations * PATTERN subpattern
|
|
||||||
|
|
||||||
PATTERN shift 15
|
|
||||||
{default} reduce 6
|
|
||||||
|
|
||||||
State 27:
|
|
||||||
(4) lexfile ::= PHPCODE declare PHPCODE rules *
|
|
||||||
reset_rules ::= rules * COMMENTSTART
|
|
||||||
|
|
||||||
COMMENTSTART shift 43
|
|
||||||
{default} reduce 4
|
|
||||||
|
|
||||||
State 28:
|
|
||||||
declare ::= COMMENTSTART declarations * COMMENTEND
|
|
||||||
|
|
||||||
COMMENTEND shift 55
|
|
||||||
|
|
||||||
State 29:
|
|
||||||
rules ::= reset_rules PI * SUBPATTERN rule COMMENTEND
|
|
||||||
rules ::= reset_rules PI * SUBPATTERN rule COMMENTEND PHPCODE
|
|
||||||
|
|
||||||
SUBPATTERN shift 5
|
|
||||||
|
|
||||||
State 30:
|
|
||||||
(14) rules ::= COMMENTSTART PI SUBPATTERN rule COMMENTEND *
|
|
||||||
rules ::= COMMENTSTART PI SUBPATTERN rule COMMENTEND * PHPCODE
|
|
||||||
|
|
||||||
PHPCODE shift 46
|
|
||||||
{default} reduce 14
|
|
||||||
|
|
||||||
State 31:
|
|
||||||
(2) lexfile ::= declare PHPCODE rules *
|
|
||||||
reset_rules ::= rules * COMMENTSTART
|
|
||||||
|
|
||||||
COMMENTSTART shift 43
|
|
||||||
{default} reduce 2
|
|
||||||
|
|
||||||
State 32:
|
|
||||||
(13) rules ::= COMMENTSTART rule COMMENTEND *
|
|
||||||
rules ::= COMMENTSTART rule COMMENTEND * PHPCODE
|
|
||||||
|
|
||||||
PHPCODE shift 56
|
|
||||||
{default} reduce 13
|
|
||||||
|
|
||||||
State 33:
|
|
||||||
(1) lexfile ::= declare rules *
|
|
||||||
reset_rules ::= rules * COMMENTSTART
|
|
||||||
|
|
||||||
COMMENTSTART shift 43
|
|
||||||
{default} reduce 1
|
|
||||||
|
|
||||||
State 34:
|
|
||||||
rules ::= COMMENTSTART PI * SUBPATTERN rule COMMENTEND
|
|
||||||
rules ::= COMMENTSTART PI * SUBPATTERN rule COMMENTEND PHPCODE
|
|
||||||
|
|
||||||
SUBPATTERN shift 3
|
|
||||||
|
|
||||||
State 35:
|
|
||||||
(17) rules ::= reset_rules rule COMMENTEND *
|
|
||||||
rules ::= reset_rules rule COMMENTEND * PHPCODE
|
|
||||||
|
|
||||||
PHPCODE shift 49
|
|
||||||
{default} reduce 17
|
|
||||||
|
|
||||||
State 36:
|
|
||||||
(29) subpattern ::= SUBPATTERN *
|
|
||||||
|
|
||||||
{default} reduce 29
|
|
||||||
|
|
||||||
State 37:
|
|
||||||
(28) subpattern ::= QUOTE *
|
|
||||||
|
|
||||||
{default} reduce 28
|
|
||||||
|
|
||||||
State 38:
|
|
||||||
(31) subpattern ::= subpattern SUBPATTERN *
|
|
||||||
|
|
||||||
{default} reduce 31
|
|
||||||
|
|
||||||
State 39:
|
|
||||||
(8) processing_instructions ::= PI CODE *
|
|
||||||
|
|
||||||
{default} reduce 8
|
|
||||||
|
|
||||||
State 40:
|
|
||||||
(10) processing_instructions ::= processing_instructions PI CODE *
|
|
||||||
|
|
||||||
{default} reduce 10
|
|
||||||
|
|
||||||
State 41:
|
|
||||||
(30) subpattern ::= subpattern QUOTE *
|
|
||||||
|
|
||||||
{default} reduce 30
|
|
||||||
|
|
||||||
State 42:
|
|
||||||
(7) processing_instructions ::= PI SUBPATTERN *
|
|
||||||
|
|
||||||
{default} reduce 7
|
|
||||||
|
|
||||||
State 43:
|
|
||||||
(21) reset_rules ::= rules COMMENTSTART *
|
|
||||||
|
|
||||||
{default} reduce 21
|
|
||||||
|
|
||||||
State 44:
|
|
||||||
(9) processing_instructions ::= processing_instructions PI SUBPATTERN *
|
|
||||||
|
|
||||||
{default} reduce 9
|
|
||||||
|
|
||||||
State 45:
|
|
||||||
(23) rule ::= rule rule_subpattern CODE *
|
|
||||||
|
|
||||||
{default} reduce 23
|
|
||||||
|
|
||||||
State 46:
|
|
||||||
(16) rules ::= COMMENTSTART PI SUBPATTERN rule COMMENTEND PHPCODE *
|
|
||||||
|
|
||||||
{default} reduce 16
|
|
||||||
|
|
||||||
State 47:
|
|
||||||
(22) rule ::= rule_subpattern CODE *
|
|
||||||
|
|
||||||
{default} reduce 22
|
|
||||||
|
|
||||||
State 48:
|
|
||||||
(20) rules ::= reset_rules PI SUBPATTERN rule COMMENTEND PHPCODE *
|
|
||||||
|
|
||||||
{default} reduce 20
|
|
||||||
|
|
||||||
State 49:
|
|
||||||
(19) rules ::= reset_rules rule COMMENTEND PHPCODE *
|
|
||||||
|
|
||||||
{default} reduce 19
|
|
||||||
|
|
||||||
State 50:
|
|
||||||
(25) rule_subpattern ::= SUBPATTERN *
|
|
||||||
|
|
||||||
{default} reduce 25
|
|
||||||
|
|
||||||
State 51:
|
|
||||||
(24) rule_subpattern ::= QUOTE *
|
|
||||||
|
|
||||||
{default} reduce 24
|
|
||||||
|
|
||||||
State 52:
|
|
||||||
(0) start ::= lexfile *
|
|
||||||
|
|
||||||
{default} reduce 0
|
|
||||||
|
|
||||||
State 53:
|
|
||||||
(26) rule_subpattern ::= rule_subpattern QUOTE *
|
|
||||||
|
|
||||||
{default} reduce 26
|
|
||||||
|
|
||||||
State 54:
|
|
||||||
(27) rule_subpattern ::= rule_subpattern SUBPATTERN *
|
|
||||||
|
|
||||||
{default} reduce 27
|
|
||||||
|
|
||||||
State 55:
|
|
||||||
(5) declare ::= COMMENTSTART declarations COMMENTEND *
|
|
||||||
|
|
||||||
{default} reduce 5
|
|
||||||
|
|
||||||
State 56:
|
|
||||||
(15) rules ::= COMMENTSTART rule COMMENTEND PHPCODE *
|
|
||||||
|
|
||||||
{default} reduce 15
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,795 +0,0 @@
|
|||||||
%name PHP_LexerGenerator_Parser
|
|
||||||
%declare_class {class PHP_LexerGenerator_Parser}
|
|
||||||
%include {
|
|
||||||
/* ?><?php {//*/
|
|
||||||
/**
|
|
||||||
* PHP_LexerGenerator, a php 5 lexer generator.
|
|
||||||
*
|
|
||||||
* This lexer generator translates a file in a format similar to
|
|
||||||
* re2c ({@link http://re2c.org}) and translates it into a PHP 5-based lexer
|
|
||||||
*
|
|
||||||
* PHP version 5
|
|
||||||
*
|
|
||||||
* LICENSE:
|
|
||||||
*
|
|
||||||
* Copyright (c) 2006, Gregory Beaver <cellog@php.net>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in
|
|
||||||
* the documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the PHP_LexerGenerator nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived
|
|
||||||
* from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
||||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
||||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* @category php
|
|
||||||
* @package PHP_LexerGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version CVS: $Id: Parser.y 246683 2007-11-22 04:43:52Z instance $
|
|
||||||
* @since File available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* For regular expression validation
|
|
||||||
*/
|
|
||||||
require_once 'PHP/LexerGenerator/Regex/Lexer.php';
|
|
||||||
require_once 'PHP/LexerGenerator/Regex/Parser.php';
|
|
||||||
require_once 'PHP/LexerGenerator/Exception.php';
|
|
||||||
/**
|
|
||||||
* Token parser for plex files.
|
|
||||||
*
|
|
||||||
* This parser converts tokens pulled from {@link PHP_LexerGenerator_Lexer}
|
|
||||||
* into abstract patterns and rules, then creates the output file
|
|
||||||
* @package PHP_LexerGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.php.net/license/3_01.txt PHP License 3.01
|
|
||||||
* @version @package_version@
|
|
||||||
* @since Class available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
%syntax_error {
|
|
||||||
echo "Syntax Error on line " . $this->lex->line . ": token '" .
|
|
||||||
$this->lex->value . "' while parsing rule:";
|
|
||||||
foreach ($this->yystack as $entry) {
|
|
||||||
echo $this->tokenName($entry->major) . ' ';
|
|
||||||
}
|
|
||||||
foreach ($this->yy_get_expected_tokens($yymajor) as $token) {
|
|
||||||
$expect[] = self::$yyTokenName[$token];
|
|
||||||
}
|
|
||||||
throw new Exception('Unexpected ' . $this->tokenName($yymajor) . '(' . $TOKEN
|
|
||||||
. '), expected one of: ' . implode(',', $expect));
|
|
||||||
}
|
|
||||||
%include_class {
|
|
||||||
private $patterns;
|
|
||||||
private $out;
|
|
||||||
private $lex;
|
|
||||||
private $input;
|
|
||||||
private $counter;
|
|
||||||
private $token;
|
|
||||||
private $value;
|
|
||||||
private $line;
|
|
||||||
private $matchlongest;
|
|
||||||
private $_regexLexer;
|
|
||||||
private $_regexParser;
|
|
||||||
private $_patternIndex = 0;
|
|
||||||
private $_outRuleIndex = 1;
|
|
||||||
private $caseinsensitive;
|
|
||||||
private $patternFlags;
|
|
||||||
private $unicode;
|
|
||||||
|
|
||||||
public $transTable = array(
|
|
||||||
1 => self::PHPCODE,
|
|
||||||
2 => self::COMMENTSTART,
|
|
||||||
3 => self::COMMENTEND,
|
|
||||||
4 => self::QUOTE,
|
|
||||||
5 => self::SINGLEQUOTE,
|
|
||||||
6 => self::PATTERN,
|
|
||||||
7 => self::CODE,
|
|
||||||
8 => self::SUBPATTERN,
|
|
||||||
9 => self::PI,
|
|
||||||
);
|
|
||||||
|
|
||||||
function __construct($outfile, $lex)
|
|
||||||
{
|
|
||||||
$this->out = fopen($outfile, 'wb');
|
|
||||||
if (!$this->out) {
|
|
||||||
throw new Exception('unable to open lexer output file "' . $outfile . '"');
|
|
||||||
}
|
|
||||||
$this->lex = $lex;
|
|
||||||
$this->_regexLexer = new PHP_LexerGenerator_Regex_Lexer('');
|
|
||||||
$this->_regexParser = new PHP_LexerGenerator_Regex_Parser($this->_regexLexer);
|
|
||||||
}
|
|
||||||
|
|
||||||
function doLongestMatch($rules, $statename, $ruleindex)
|
|
||||||
{
|
|
||||||
fwrite($this->out, '
|
|
||||||
if (' . $this->counter . ' >= strlen(' . $this->input . ')) {
|
|
||||||
return false; // end of input
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
$rules = array(');
|
|
||||||
foreach ($rules as $rule) {
|
|
||||||
fwrite($this->out, '
|
|
||||||
\'/\G' . $rule['pattern'] . '/' . $this->patternFlags . ' \',');
|
|
||||||
}
|
|
||||||
fwrite($this->out, '
|
|
||||||
);
|
|
||||||
$match = false;
|
|
||||||
foreach ($rules as $index => $rule) {
|
|
||||||
if (preg_match($rule, substr(' . $this->input . ', ' .
|
|
||||||
$this->counter . '), $yymatches)) {
|
|
||||||
if ($match) {
|
|
||||||
if (strlen($yymatches[0]) > strlen($match[0][0])) {
|
|
||||||
$match = array($yymatches, $index); // matches, token
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$match = array($yymatches, $index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!$match) {
|
|
||||||
throw new Exception(\'Unexpected input at line \' . ' . $this->line . ' .
|
|
||||||
\': \' . ' . $this->input . '[' . $this->counter . ']);
|
|
||||||
}
|
|
||||||
' . $this->token . ' = $match[1];
|
|
||||||
' . $this->value . ' = $match[0][0];
|
|
||||||
$yysubmatches = $match[0];
|
|
||||||
array_shift($yysubmatches);
|
|
||||||
if (!$yysubmatches) {
|
|
||||||
$yysubmatches = array();
|
|
||||||
}
|
|
||||||
$r = $this->{\'yy_r' . $ruleindex . '_\' . ' . $this->token . '}($yysubmatches);
|
|
||||||
if ($r === null) {
|
|
||||||
' . $this->counter . ' += strlen(' . $this->value . ');
|
|
||||||
' . $this->line . ' += substr_count(' . $this->value . ', "\n");
|
|
||||||
// accept this token
|
|
||||||
return true;
|
|
||||||
} elseif ($r === true) {
|
|
||||||
// we have changed state
|
|
||||||
// process this token in the new state
|
|
||||||
return $this->yylex();
|
|
||||||
} elseif ($r === false) {
|
|
||||||
' . $this->counter . ' += strlen(' . $this->value . ');
|
|
||||||
' . $this->line . ' += substr_count(' . $this->value . ', "\n");
|
|
||||||
if (' . $this->counter . ' >= strlen(' . $this->input . ')) {
|
|
||||||
return false; // end of input
|
|
||||||
}
|
|
||||||
// skip this token
|
|
||||||
continue;
|
|
||||||
} else {');
|
|
||||||
fwrite($this->out, '
|
|
||||||
$yy_yymore_patterns = array_slice($rules, $this->token, true);
|
|
||||||
// yymore is needed
|
|
||||||
do {
|
|
||||||
if (!isset($yy_yymore_patterns[' . $this->token . '])) {
|
|
||||||
throw new Exception(\'cannot do yymore for the last token\');
|
|
||||||
}
|
|
||||||
$match = false;
|
|
||||||
foreach ($yy_yymore_patterns[' . $this->token . '] as $index => $rule) {
|
|
||||||
if (preg_match(\'/\' . $rule . \'/' . $this->patternFlags . '\',
|
|
||||||
' . $this->input . ', $yymatches, null, ' . $this->counter . ')) {
|
|
||||||
$yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
|
|
||||||
if ($match) {
|
|
||||||
if (strlen($yymatches[0]) > strlen($match[0][0])) {
|
|
||||||
$match = array($yymatches, $index); // matches, token
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$match = array($yymatches, $index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!$match) {
|
|
||||||
throw new Exception(\'Unexpected input at line \' . ' . $this->line . ' .
|
|
||||||
\': \' . ' . $this->input . '[' . $this->counter . ']);
|
|
||||||
}
|
|
||||||
' . $this->token . ' = $match[1];
|
|
||||||
' . $this->value . ' = $match[0][0];
|
|
||||||
$yysubmatches = $match[0];
|
|
||||||
array_shift($yysubmatches);
|
|
||||||
if (!$yysubmatches) {
|
|
||||||
$yysubmatches = array();
|
|
||||||
}
|
|
||||||
' . $this->line . ' = substr_count(' . $this->value . ', "\n");
|
|
||||||
$r = $this->{\'yy_r' . $ruleindex . '_\' . ' . $this->token . '}();
|
|
||||||
} while ($r !== null || !$r);
|
|
||||||
if ($r === true) {
|
|
||||||
// we have changed state
|
|
||||||
// process this token in the new state
|
|
||||||
return $this->yylex();
|
|
||||||
} else {
|
|
||||||
// accept
|
|
||||||
' . $this->counter . ' += strlen(' . $this->value . ');
|
|
||||||
' . $this->line . ' += substr_count(' . $this->value . ', "\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (true);
|
|
||||||
');
|
|
||||||
}
|
|
||||||
|
|
||||||
function doFirstMatch($rules, $statename, $ruleindex)
|
|
||||||
{
|
|
||||||
$patterns = array();
|
|
||||||
$pattern = '/';
|
|
||||||
$ruleMap = array();
|
|
||||||
$tokenindex = array();
|
|
||||||
$actualindex = 1;
|
|
||||||
$i = 0;
|
|
||||||
foreach ($rules as $rule) {
|
|
||||||
$ruleMap[$i++] = $actualindex;
|
|
||||||
$tokenindex[$actualindex] = $rule['subpatterns'];
|
|
||||||
$actualindex += $rule['subpatterns'] + 1;
|
|
||||||
$patterns[] = '\G(' . $rule['pattern'] . ')';
|
|
||||||
}
|
|
||||||
// Re-index tokencount from zero.
|
|
||||||
$tokencount = array_values($tokenindex);
|
|
||||||
$tokenindex = var_export($tokenindex, true);
|
|
||||||
$tokenindex = explode("\n", $tokenindex);
|
|
||||||
// indent for prettiness
|
|
||||||
$tokenindex = implode("\n ", $tokenindex);
|
|
||||||
$pattern .= implode('|', $patterns);
|
|
||||||
$pattern .= '/' . $this->patternFlags;
|
|
||||||
fwrite($this->out, '
|
|
||||||
$tokenMap = ' . $tokenindex . ';
|
|
||||||
if (' . $this->counter . ' >= strlen(' . $this->input . ')) {
|
|
||||||
return false; // end of input
|
|
||||||
}
|
|
||||||
');
|
|
||||||
fwrite($this->out, '$yy_global_pattern = \'' .
|
|
||||||
$pattern . '\';' . "\n");
|
|
||||||
fwrite($this->out, '
|
|
||||||
do {
|
|
||||||
if (preg_match($yy_global_pattern,' . $this->input . ', $yymatches, null, ' .
|
|
||||||
$this->counter .
|
|
||||||
')) {
|
|
||||||
$yysubmatches = $yymatches;
|
|
||||||
$yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
|
|
||||||
if (!count($yymatches)) {
|
|
||||||
throw new Exception(\'Error: lexing failed because a rule matched\' .
|
|
||||||
\' an empty string. Input "\' . substr(' . $this->input . ',
|
|
||||||
' . $this->counter . ', 5) . \'... state ' . $statename . '\');
|
|
||||||
}
|
|
||||||
next($yymatches); // skip global match
|
|
||||||
' . $this->token . ' = key($yymatches); // token number
|
|
||||||
if ($tokenMap[' . $this->token . ']) {
|
|
||||||
// extract sub-patterns for passing to lex function
|
|
||||||
$yysubmatches = array_slice($yysubmatches, ' . $this->token . ' + 1,
|
|
||||||
$tokenMap[' . $this->token . ']);
|
|
||||||
} else {
|
|
||||||
$yysubmatches = array();
|
|
||||||
}
|
|
||||||
' . $this->value . ' = current($yymatches); // token value
|
|
||||||
$r = $this->{\'yy_r' . $ruleindex . '_\' . ' . $this->token . '}($yysubmatches);
|
|
||||||
if ($r === null) {
|
|
||||||
' . $this->counter . ' += strlen(' . $this->value . ');
|
|
||||||
' . $this->line . ' += substr_count(' . $this->value . ', "\n");
|
|
||||||
// accept this token
|
|
||||||
return true;
|
|
||||||
} elseif ($r === true) {
|
|
||||||
// we have changed state
|
|
||||||
// process this token in the new state
|
|
||||||
return $this->yylex();
|
|
||||||
} elseif ($r === false) {
|
|
||||||
' . $this->counter . ' += strlen(' . $this->value . ');
|
|
||||||
' . $this->line . ' += substr_count(' . $this->value . ', "\n");
|
|
||||||
if (' . $this->counter . ' >= strlen(' . $this->input . ')) {
|
|
||||||
return false; // end of input
|
|
||||||
}
|
|
||||||
// skip this token
|
|
||||||
continue;
|
|
||||||
} else {');
|
|
||||||
fwrite($this->out, '
|
|
||||||
$yy_yymore_patterns = array(' . "\n");
|
|
||||||
$extra = 0;
|
|
||||||
for($i = 0; count($patterns); $i++) {
|
|
||||||
unset($patterns[$i]);
|
|
||||||
$extra += $tokencount[0];
|
|
||||||
array_shift($tokencount);
|
|
||||||
fwrite($this->out, ' ' . $ruleMap[$i] . ' => array(' . $extra . ', "' .
|
|
||||||
implode('|', $patterns) . "\"),\n");
|
|
||||||
}
|
|
||||||
fwrite($this->out, ' );' . "\n");
|
|
||||||
fwrite($this->out, '
|
|
||||||
// yymore is needed
|
|
||||||
do {
|
|
||||||
if (!strlen($yy_yymore_patterns[' . $this->token . '][1])) {
|
|
||||||
throw new Exception(\'cannot do yymore for the last token\');
|
|
||||||
}
|
|
||||||
$yysubmatches = array();
|
|
||||||
if (preg_match(\'/\' . $yy_yymore_patterns[' . $this->token . '][1] . \'/' . $this->patternFlags . '\',
|
|
||||||
' . $this->input . ', $yymatches, null, ' . $this->counter .')) {
|
|
||||||
$yysubmatches = $yymatches;
|
|
||||||
$yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
|
|
||||||
next($yymatches); // skip global match
|
|
||||||
' . $this->token . ' += key($yymatches) + $yy_yymore_patterns[' . $this->token . '][0]; // token number
|
|
||||||
' . $this->value . ' = current($yymatches); // token value
|
|
||||||
' . $this->line . ' = substr_count(' . $this->value . ', "\n");
|
|
||||||
if ($tokenMap[' . $this->token . ']) {
|
|
||||||
// extract sub-patterns for passing to lex function
|
|
||||||
$yysubmatches = array_slice($yysubmatches, ' . $this->token . ' + 1,
|
|
||||||
$tokenMap[' . $this->token . ']);
|
|
||||||
} else {
|
|
||||||
$yysubmatches = array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$r = $this->{\'yy_r' . $ruleindex . '_\' . ' . $this->token . '}($yysubmatches);
|
|
||||||
} while ($r !== null && !is_bool($r));
|
|
||||||
if ($r === true) {
|
|
||||||
// we have changed state
|
|
||||||
// process this token in the new state
|
|
||||||
return $this->yylex();
|
|
||||||
} elseif ($r === false) {
|
|
||||||
' . $this->counter . ' += strlen(' . $this->value . ');
|
|
||||||
' . $this->line . ' += substr_count(' . $this->value . ', "\n");
|
|
||||||
if (' . $this->counter . ' >= strlen(' . $this->input . ')) {
|
|
||||||
return false; // end of input
|
|
||||||
}
|
|
||||||
// skip this token
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
// accept
|
|
||||||
' . $this->counter . ' += strlen(' . $this->value . ');
|
|
||||||
' . $this->line . ' += substr_count(' . $this->value . ', "\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Exception(\'Unexpected input at line\' . ' . $this->line . ' .
|
|
||||||
\': \' . ' . $this->input . '[' . $this->counter . ']);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} while (true);
|
|
||||||
');
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeCaseInsensitve($string)
|
|
||||||
{
|
|
||||||
return preg_replace('/[a-z]/ie', "'[\\0'.strtoupper('\\0').']'", strtolower($string));
|
|
||||||
}
|
|
||||||
|
|
||||||
function outputRules($rules, $statename)
|
|
||||||
{
|
|
||||||
if (!$statename) {
|
|
||||||
$statename = $this -> _outRuleIndex;
|
|
||||||
}
|
|
||||||
fwrite($this->out, '
|
|
||||||
function yylex' . $this -> _outRuleIndex . '()
|
|
||||||
{');
|
|
||||||
if ($this->matchlongest) {
|
|
||||||
$ruleMap = array();
|
|
||||||
foreach ($rules as $i => $rule) {
|
|
||||||
$ruleMap[$i] = $i;
|
|
||||||
}
|
|
||||||
$this->doLongestMatch($rules, $statename, $this -> _outRuleIndex);
|
|
||||||
} else {
|
|
||||||
$ruleMap = array();
|
|
||||||
$actualindex = 1;
|
|
||||||
$i = 0;
|
|
||||||
foreach ($rules as $rule) {
|
|
||||||
$ruleMap[$i++] = $actualindex;
|
|
||||||
$actualindex += $rule['subpatterns'] + 1;
|
|
||||||
}
|
|
||||||
$this->doFirstMatch($rules, $statename, $this -> _outRuleIndex);
|
|
||||||
}
|
|
||||||
fwrite($this->out, '
|
|
||||||
} // end function
|
|
||||||
|
|
||||||
');
|
|
||||||
if (is_string($statename)) {
|
|
||||||
fwrite($this->out, '
|
|
||||||
const ' . $statename . ' = ' . $this -> _outRuleIndex . ';
|
|
||||||
');
|
|
||||||
}
|
|
||||||
foreach ($rules as $i => $rule) {
|
|
||||||
fwrite($this->out, ' function yy_r' . $this -> _outRuleIndex . '_' . $ruleMap[$i] . '($yy_subpatterns)
|
|
||||||
{
|
|
||||||
' . $rule['code'] .
|
|
||||||
' }
|
|
||||||
');
|
|
||||||
}
|
|
||||||
$this -> _outRuleIndex++; // for next set of rules
|
|
||||||
}
|
|
||||||
|
|
||||||
function error($msg)
|
|
||||||
{
|
|
||||||
echo 'Error on line ' . $this->lex->line . ': ' , $msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _validatePattern($pattern, $update = false)
|
|
||||||
{
|
|
||||||
$this->_regexLexer->reset($pattern, $this->lex->line);
|
|
||||||
$this->_regexParser->reset($this->_patternIndex, $update);
|
|
||||||
try {
|
|
||||||
while ($this->_regexLexer->yylex()) {
|
|
||||||
$this->_regexParser->doParse(
|
|
||||||
$this->_regexLexer->token, $this->_regexLexer->value);
|
|
||||||
}
|
|
||||||
$this->_regexParser->doParse(0, 0);
|
|
||||||
} catch (PHP_LexerGenerator_Exception $e) {
|
|
||||||
$this->error($e->getMessage());
|
|
||||||
throw new PHP_LexerGenerator_Exception('Invalid pattern "' . $pattern . '"');
|
|
||||||
}
|
|
||||||
return $this->_regexParser->result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
start ::= lexfile.
|
|
||||||
|
|
||||||
lexfile ::= declare rules(B). {
|
|
||||||
fwrite($this->out, '
|
|
||||||
private $_yy_state = 1;
|
|
||||||
private $_yy_stack = array();
|
|
||||||
|
|
||||||
function yylex()
|
|
||||||
{
|
|
||||||
return $this->{\'yylex\' . $this->_yy_state}();
|
|
||||||
}
|
|
||||||
|
|
||||||
function yypushstate($state)
|
|
||||||
{
|
|
||||||
array_push($this->_yy_stack, $this->_yy_state);
|
|
||||||
$this->_yy_state = $state;
|
|
||||||
}
|
|
||||||
|
|
||||||
function yypopstate()
|
|
||||||
{
|
|
||||||
$this->_yy_state = array_pop($this->_yy_stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
function yybegin($state)
|
|
||||||
{
|
|
||||||
$this->_yy_state = $state;
|
|
||||||
}
|
|
||||||
|
|
||||||
');
|
|
||||||
foreach (B as $rule) {
|
|
||||||
$this->outputRules($rule['rules'], $rule['statename']);
|
|
||||||
if ($rule['code']) {
|
|
||||||
fwrite($this->out, $rule['code']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lexfile ::= declare(D) PHPCODE(B) rules(C). {
|
|
||||||
fwrite($this->out, '
|
|
||||||
private $_yy_state = 1;
|
|
||||||
private $_yy_stack = array();
|
|
||||||
|
|
||||||
function yylex()
|
|
||||||
{
|
|
||||||
return $this->{\'yylex\' . $this->_yy_state}();
|
|
||||||
}
|
|
||||||
|
|
||||||
function yypushstate($state)
|
|
||||||
{
|
|
||||||
array_push($this->_yy_stack, $this->_yy_state);
|
|
||||||
$this->_yy_state = $state;
|
|
||||||
}
|
|
||||||
|
|
||||||
function yypopstate()
|
|
||||||
{
|
|
||||||
$this->_yy_state = array_pop($this->_yy_stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
function yybegin($state)
|
|
||||||
{
|
|
||||||
$this->_yy_state = $state;
|
|
||||||
}
|
|
||||||
|
|
||||||
');
|
|
||||||
if (strlen(B)) {
|
|
||||||
fwrite($this->out, B);
|
|
||||||
}
|
|
||||||
foreach (C as $rule) {
|
|
||||||
$this->outputRules($rule['rules'], $rule['statename']);
|
|
||||||
if ($rule['code']) {
|
|
||||||
fwrite($this->out, $rule['code']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lexfile ::= PHPCODE(B) declare(D) rules(C). {
|
|
||||||
if (strlen(B)) {
|
|
||||||
fwrite($this->out, B);
|
|
||||||
}
|
|
||||||
fwrite($this->out, '
|
|
||||||
private $_yy_state = 1;
|
|
||||||
private $_yy_stack = array();
|
|
||||||
|
|
||||||
function yylex()
|
|
||||||
{
|
|
||||||
return $this->{\'yylex\' . $this->_yy_state}();
|
|
||||||
}
|
|
||||||
|
|
||||||
function yypushstate($state)
|
|
||||||
{
|
|
||||||
array_push($this->_yy_stack, $this->_yy_state);
|
|
||||||
$this->_yy_state = $state;
|
|
||||||
}
|
|
||||||
|
|
||||||
function yypopstate()
|
|
||||||
{
|
|
||||||
$this->_yy_state = array_pop($this->_yy_stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
function yybegin($state)
|
|
||||||
{
|
|
||||||
$this->_yy_state = $state;
|
|
||||||
}
|
|
||||||
|
|
||||||
');
|
|
||||||
foreach (C as $rule) {
|
|
||||||
$this->outputRules($rule['rules'], $rule['statename']);
|
|
||||||
if ($rule['code']) {
|
|
||||||
fwrite($this->out, $rule['code']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lexfile ::= PHPCODE(A) declare(D) PHPCODE(B) rules(C). {
|
|
||||||
if (strlen(A)) {
|
|
||||||
fwrite($this->out, A);
|
|
||||||
}
|
|
||||||
fwrite($this->out, '
|
|
||||||
private $_yy_state = 1;
|
|
||||||
private $_yy_stack = array();
|
|
||||||
|
|
||||||
function yylex()
|
|
||||||
{
|
|
||||||
return $this->{\'yylex\' . $this->_yy_state}();
|
|
||||||
}
|
|
||||||
|
|
||||||
function yypushstate($state)
|
|
||||||
{
|
|
||||||
array_push($this->_yy_stack, $this->_yy_state);
|
|
||||||
$this->_yy_state = $state;
|
|
||||||
}
|
|
||||||
|
|
||||||
function yypopstate()
|
|
||||||
{
|
|
||||||
$this->_yy_state = array_pop($this->_yy_stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
function yybegin($state)
|
|
||||||
{
|
|
||||||
$this->_yy_state = $state;
|
|
||||||
}
|
|
||||||
|
|
||||||
');
|
|
||||||
if (strlen(B)) {
|
|
||||||
fwrite($this->out, B);
|
|
||||||
}
|
|
||||||
foreach (C as $rule) {
|
|
||||||
$this->outputRules($rule['rules'], $rule['statename']);
|
|
||||||
if ($rule['code']) {
|
|
||||||
fwrite($this->out, $rule['code']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare(A) ::= COMMENTSTART declarations(B) COMMENTEND. {
|
|
||||||
A = B;
|
|
||||||
$this->patterns = B['patterns'];
|
|
||||||
$this->_patternIndex = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
declarations(A) ::= processing_instructions(B) pattern_declarations(C). {
|
|
||||||
$expected = array(
|
|
||||||
'counter' => true,
|
|
||||||
'input' => true,
|
|
||||||
'token' => true,
|
|
||||||
'value' => true,
|
|
||||||
'line' => true,
|
|
||||||
);
|
|
||||||
foreach (B as $pi) {
|
|
||||||
if (isset($expected[$pi['pi']])) {
|
|
||||||
unset($expected[$pi['pi']]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (count($expected)) {
|
|
||||||
throw new Exception('Processing Instructions "' .
|
|
||||||
implode(', ', array_keys($expected)) . '" must be defined');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$expected = array(
|
|
||||||
'caseinsensitive' => true,
|
|
||||||
'counter' => true,
|
|
||||||
'input' => true,
|
|
||||||
'token' => true,
|
|
||||||
'value' => true,
|
|
||||||
'line' => true,
|
|
||||||
'matchlongest' => true,
|
|
||||||
'unicode' => true,
|
|
||||||
);
|
|
||||||
foreach (B as $pi) {
|
|
||||||
if (isset($expected[$pi['pi']])) {
|
|
||||||
$this->{$pi['pi']} = $pi['definition'];
|
|
||||||
if ($pi['pi'] == 'matchlongest') {
|
|
||||||
$this->matchlongest = true;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$this->error('Unknown processing instruction %' . $pi['pi'] .
|
|
||||||
', should be one of "' . implode(', ', array_keys($expected)) . '"');
|
|
||||||
}
|
|
||||||
$this->patternFlags = ($this->caseinsensitive ? 'i' : '')
|
|
||||||
. ($this->unicode ? 'u' : '');
|
|
||||||
A = array('patterns' => C, 'pis' => B);
|
|
||||||
$this->_patternIndex = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
processing_instructions(A) ::= PI(B) SUBPATTERN(C). {
|
|
||||||
A = array(array('pi' => B, 'definition' => C));
|
|
||||||
}
|
|
||||||
processing_instructions(A) ::= PI(B) CODE(C). {
|
|
||||||
A = array(array('pi' => B, 'definition' => C));
|
|
||||||
}
|
|
||||||
processing_instructions(A) ::= processing_instructions(P) PI(B) SUBPATTERN(C). {
|
|
||||||
A = P;
|
|
||||||
A[] = array('pi' => B, 'definition' => C);
|
|
||||||
}
|
|
||||||
processing_instructions(A) ::= processing_instructions(P) PI(B) CODE(C). {
|
|
||||||
A = P;
|
|
||||||
A[] = array('pi' => B, 'definition' => C);
|
|
||||||
}
|
|
||||||
|
|
||||||
pattern_declarations(A) ::= PATTERN(B) subpattern(C). {
|
|
||||||
A = array(B => C);
|
|
||||||
// reset internal indicator of where we are in a pattern
|
|
||||||
$this->_patternIndex = 0;
|
|
||||||
}
|
|
||||||
pattern_declarations(A) ::= pattern_declarations(B) PATTERN(C) subpattern(D). {
|
|
||||||
A = B;
|
|
||||||
if (isset(A[C])) {
|
|
||||||
throw new Exception('Pattern "' . C . '" is already defined as "' .
|
|
||||||
A[C] . '", cannot redefine as "' . D->string . '"');
|
|
||||||
}
|
|
||||||
A[C] = D;
|
|
||||||
// reset internal indicator of where we are in a pattern declaration
|
|
||||||
$this->_patternIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
rules(A) ::= COMMENTSTART rule(B) COMMENTEND. {
|
|
||||||
A = array(array('rules' => B, 'code' => '', 'statename' => ''));
|
|
||||||
}
|
|
||||||
rules(A) ::= COMMENTSTART PI(P) SUBPATTERN(S) rule(B) COMMENTEND. {
|
|
||||||
if (P != 'statename') {
|
|
||||||
throw new Exception('Error: only %statename processing instruction ' .
|
|
||||||
'is allowed in rule sections (found ' . P . ').');
|
|
||||||
}
|
|
||||||
A = array(array('rules' => B, 'code' => '', 'statename' => S));
|
|
||||||
}
|
|
||||||
rules(A) ::= COMMENTSTART rule(B) COMMENTEND PHPCODE(C). {
|
|
||||||
A = array(array('rules' => B, 'code' => C, 'statename' => ''));
|
|
||||||
}
|
|
||||||
rules(A) ::= COMMENTSTART PI(P) SUBPATTERN(S) rule(B) COMMENTEND PHPCODE(C). {
|
|
||||||
if (P != 'statename') {
|
|
||||||
throw new Exception('Error: only %statename processing instruction ' .
|
|
||||||
'is allowed in rule sections (found ' . P . ').');
|
|
||||||
}
|
|
||||||
A = array(array('rules' => B, 'code' => C, 'statename' => S));
|
|
||||||
$this->_patternIndex = 1;
|
|
||||||
}
|
|
||||||
rules(A) ::= reset_rules(R) rule(B) COMMENTEND. {
|
|
||||||
A = R;
|
|
||||||
A[] = array('rules' => B, 'code' => '', 'statename' => '');
|
|
||||||
$this->_patternIndex = 1;
|
|
||||||
}
|
|
||||||
rules(A) ::= reset_rules(R) PI(P) SUBPATTERN(S) rule(B) COMMENTEND. {
|
|
||||||
if (P != 'statename') {
|
|
||||||
throw new Exception('Error: only %statename processing instruction ' .
|
|
||||||
'is allowed in rule sections (found ' . P . ').');
|
|
||||||
}
|
|
||||||
A = R;
|
|
||||||
A[] = array('rules' => B, 'code' => '', 'statename' => S);
|
|
||||||
}
|
|
||||||
rules(A) ::= reset_rules(R) rule(B) COMMENTEND PHPCODE(C). {
|
|
||||||
A = R;
|
|
||||||
A[] = array('rules' => B, 'code' => C, 'statename' => '');
|
|
||||||
}
|
|
||||||
rules(A) ::= reset_rules(R) PI(P) SUBPATTERN(S) rule(B) COMMENTEND PHPCODE(C). {
|
|
||||||
if (P != 'statename') {
|
|
||||||
throw new Exception('Error: only %statename processing instruction ' .
|
|
||||||
'is allowed in rule sections (found ' . P . ').');
|
|
||||||
}
|
|
||||||
A = R;
|
|
||||||
A[] = array('rules' => B, 'code' => C, 'statename' => S);
|
|
||||||
}
|
|
||||||
|
|
||||||
reset_rules(A) ::= rules(R) COMMENTSTART. {
|
|
||||||
A = R;
|
|
||||||
$this->_patternIndex = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
rule(A) ::= rule_subpattern(B) CODE(C). {
|
|
||||||
$name = B[1];
|
|
||||||
B = B[0];
|
|
||||||
B = $this->_validatePattern(B);
|
|
||||||
$this->_patternIndex += B['subpatterns'] + 1;
|
|
||||||
if (@preg_match('/' . str_replace('/', '\\/', B['pattern']) . '/', '')) {
|
|
||||||
$this->error('Rule "' . $name . '" can match the empty string, this will break lexing');
|
|
||||||
}
|
|
||||||
A = array(array('pattern' => str_replace('/', '\\/', B->string), 'code' => C, 'subpatterns' => B['subpatterns']));
|
|
||||||
}
|
|
||||||
rule(A) ::= rule(R) rule_subpattern(B) CODE(C).{
|
|
||||||
A = R;
|
|
||||||
$name = B[1];
|
|
||||||
B = B[0];
|
|
||||||
B = $this->_validatePattern(B);
|
|
||||||
$this->_patternIndex += B['subpatterns'] + 1;
|
|
||||||
if (@preg_match('/' . str_replace('/', '\\/', B['pattern']) . '/', '')) {
|
|
||||||
$this->error('Rule "' . $name . '" can match the empty string, this will break lexing');
|
|
||||||
}
|
|
||||||
A[] = array('pattern' => str_replace('/', '\\/', B->string), 'code' => C, 'subpatterns' => B['subpatterns']);
|
|
||||||
}
|
|
||||||
|
|
||||||
rule_subpattern(A) ::= QUOTE(B). {
|
|
||||||
A = array(preg_quote(B, '/'), B);
|
|
||||||
}
|
|
||||||
rule_subpattern(A) ::= SINGLEQUOTE(B). {
|
|
||||||
A = array($this->makeCaseInsensitve(preg_quote(B, '/')), B);
|
|
||||||
}
|
|
||||||
rule_subpattern(A) ::= SUBPATTERN(B). {
|
|
||||||
if (!isset($this->patterns[B])) {
|
|
||||||
$this->error('Undefined pattern "' . B . '" used in rules');
|
|
||||||
throw new Exception('Undefined pattern "' . B . '" used in rules');
|
|
||||||
}
|
|
||||||
A = array($this->patterns[B], B);
|
|
||||||
}
|
|
||||||
rule_subpattern(A) ::= rule_subpattern(B) QUOTE(C). {
|
|
||||||
A = array(B[0] . preg_quote(C, '/'), B[1] . ' ' . C);
|
|
||||||
}
|
|
||||||
rule_subpattern(A) ::= rule_subpattern(B) SINGLEQUOTE(C). {
|
|
||||||
A = array(B[0] . $this->makeCaseInsensitve(preg_quote(C, '/')), B[1] . ' ' . C);
|
|
||||||
}
|
|
||||||
rule_subpattern(A) ::= rule_subpattern(B) SUBPATTERN(C). {
|
|
||||||
if (!isset($this->patterns[C])) {
|
|
||||||
$this->error('Undefined pattern "' . C . '" used in rules');
|
|
||||||
throw new Exception('Undefined pattern "' . C . '" used in rules');
|
|
||||||
}
|
|
||||||
A = array(B[0] . $this->patterns[C], B[1] . ' ' . C);
|
|
||||||
}
|
|
||||||
|
|
||||||
subpattern(A) ::= QUOTE(B). {
|
|
||||||
A = preg_quote(B, '/');
|
|
||||||
}
|
|
||||||
subpattern(A) ::= SINGLEQUOTE(B). {
|
|
||||||
A = $this->makeCaseInsensitve(preg_quote(B, '/'));
|
|
||||||
}
|
|
||||||
subpattern(A) ::= SUBPATTERN(B). {
|
|
||||||
// increment internal sub-pattern counter
|
|
||||||
// adjust back-references in pattern based on previous pattern
|
|
||||||
$test = $this->_validatePattern(B, true);
|
|
||||||
$this->_patternIndex += $test['subpatterns'];
|
|
||||||
A = $test['pattern'];
|
|
||||||
}
|
|
||||||
subpattern(A) ::= subpattern(B) QUOTE(C). {
|
|
||||||
A = B . preg_quote(C, '/');
|
|
||||||
}
|
|
||||||
subpattern(A) ::= subpattern(B) SINGLEQUOTE(C). {
|
|
||||||
A = B . $this->makeCaseInsensitve(preg_quote(C, '/'));
|
|
||||||
}
|
|
||||||
subpattern(A) ::= subpattern(B) SUBPATTERN(C). {
|
|
||||||
// increment internal sub-pattern counter
|
|
||||||
// adjust back-references in pattern based on previous pattern
|
|
||||||
$test = $this->_validatePattern(C, true);
|
|
||||||
$this->_patternIndex += $test['subpatterns'];
|
|
||||||
A = B . $test['pattern'];
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,285 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once 'PHP/LexerGenerator/Regex/Parser.php';
|
|
||||||
class PHP_LexerGenerator_Regex_Lexer
|
|
||||||
{
|
|
||||||
const MATCHSTART = PHP_LexerGenerator_Regex_Parser::MATCHSTART;
|
|
||||||
const MATCHEND = PHP_LexerGenerator_Regex_Parser::MATCHEND;
|
|
||||||
const CONTROLCHAR = PHP_LexerGenerator_Regex_Parser::CONTROLCHAR;
|
|
||||||
const OPENCHARCLASS = PHP_LexerGenerator_Regex_Parser::OPENCHARCLASS;
|
|
||||||
const FULLSTOP = PHP_LexerGenerator_Regex_Parser::FULLSTOP;
|
|
||||||
const TEXT = PHP_LexerGenerator_Regex_Parser::TEXT;
|
|
||||||
const BACKREFERENCE = PHP_LexerGenerator_Regex_Parser::BACKREFERENCE;
|
|
||||||
const OPENASSERTION = PHP_LexerGenerator_Regex_Parser::OPENASSERTION;
|
|
||||||
const COULDBEBACKREF = PHP_LexerGenerator_Regex_Parser::COULDBEBACKREF;
|
|
||||||
const NEGATE = PHP_LexerGenerator_Regex_Parser::NEGATE;
|
|
||||||
const HYPHEN = PHP_LexerGenerator_Regex_Parser::HYPHEN;
|
|
||||||
const CLOSECHARCLASS = PHP_LexerGenerator_Regex_Parser::CLOSECHARCLASS;
|
|
||||||
const BAR = PHP_LexerGenerator_Regex_Parser::BAR;
|
|
||||||
const MULTIPLIER = PHP_LexerGenerator_Regex_Parser::MULTIPLIER;
|
|
||||||
const INTERNALOPTIONS = PHP_LexerGenerator_Regex_Parser::INTERNALOPTIONS;
|
|
||||||
const COLON = PHP_LexerGenerator_Regex_Parser::COLON;
|
|
||||||
const OPENPAREN = PHP_LexerGenerator_Regex_Parser::OPENPAREN;
|
|
||||||
const CLOSEPAREN = PHP_LexerGenerator_Regex_Parser::CLOSEPAREN;
|
|
||||||
const PATTERNNAME = PHP_LexerGenerator_Regex_Parser::PATTERNNAME;
|
|
||||||
const POSITIVELOOKBEHIND = PHP_LexerGenerator_Regex_Parser::POSITIVELOOKBEHIND;
|
|
||||||
const NEGATIVELOOKBEHIND = PHP_LexerGenerator_Regex_Parser::NEGATIVELOOKBEHIND;
|
|
||||||
const POSITIVELOOKAHEAD = PHP_LexerGenerator_Regex_Parser::POSITIVELOOKAHEAD;
|
|
||||||
const NEGATIVELOOKAHEAD = PHP_LexerGenerator_Regex_Parser::NEGATIVELOOKAHEAD;
|
|
||||||
const ONCEONLY = PHP_LexerGenerator_Regex_Parser::ONCEONLY;
|
|
||||||
const COMMENT = PHP_LexerGenerator_Regex_Parser::COMMENT;
|
|
||||||
const RECUR = PHP_LexerGenerator_Regex_Parser::RECUR;
|
|
||||||
const ESCAPEDBACKSLASH = PHP_LexerGenerator_Regex_Parser::ESCAPEDBACKSLASH;
|
|
||||||
private $input;
|
|
||||||
private $N;
|
|
||||||
public $token;
|
|
||||||
public $value;
|
|
||||||
public $line;
|
|
||||||
|
|
||||||
function __construct($data)
|
|
||||||
{
|
|
||||||
$this->input = $data;
|
|
||||||
$this->N = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function reset($data, $line)
|
|
||||||
{
|
|
||||||
$this->input = $data;
|
|
||||||
$this->N = 0;
|
|
||||||
// passed in from parent parser
|
|
||||||
$this->line = $line;
|
|
||||||
$this->yybegin(self::INITIAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!lex2php
|
|
||||||
%input {$this->input}
|
|
||||||
%counter {$this->N}
|
|
||||||
%token {$this->token}
|
|
||||||
%value {$this->value}
|
|
||||||
%line {$this->line}
|
|
||||||
NONESCAPE = /[^[\\^$.|()?*+{}]+/
|
|
||||||
NONESCAPECHARCLASS = /[^\-\\]/
|
|
||||||
ESCAPEDTHING = /\\[][{}*.^$|?()+]/
|
|
||||||
ESCAPEDCHARCLASSTHING = /\\[]\.\-\^]/
|
|
||||||
MULTIPLIER = /\*\?|\+\?|[*?+]|\{[0-9]+\}|\{[0-9]+,\}|\{[0-9]+,[0-9]+\}/
|
|
||||||
STRINGCHAR = /\\[frnt]|\\x[0-9a-fA-F][0-9a-fA-F]?|\\[0-7][0-7][0-7]|\\x\{[0-9a-fA-F]+\}/
|
|
||||||
CONTROLCHAR = /\\[abBGcedDsSwW0C]|\\c\\/
|
|
||||||
COULDBEBACKREF = /\\[0-9][0-9]/
|
|
||||||
CHARCLASSCONTROLCHAR = /\\[bacedDsSwW0C]|\\c\\|\\x\{[0-9a-fA-F]+\}|\\[0-7][0-7][0-7]|\\x[0-9a-fA-F][0-9a-fA-F]?/
|
|
||||||
SUBJECTEND = /\\[zZ]/
|
|
||||||
BACKREF = /\\[1-9]/
|
|
||||||
UNICODESTUFF = /\\p\{\^?..?\}|\\P\{..?\}|\\X/
|
|
||||||
PROPERTYCODES = /C[cfnos]?|L[lmotu]?|M[cen]?|N[dlo]?|P[cdefios]?|S[ckmo]?|Z[lps]?/
|
|
||||||
SIMPLEPROPERTYCODES = /[CLMNPSZ]/
|
|
||||||
INTERNALOPTIONS = /[imsxUX]+-[imsxUX]+|[imsxUX]+|-[imsxUX]+/
|
|
||||||
ANYTHING = /./
|
|
||||||
PATTERNNAME = /[^>]+/
|
|
||||||
COMMENT = /#[^)]+/
|
|
||||||
HYPHEN = /-(?!])/
|
|
||||||
*/
|
|
||||||
/*!lex2php
|
|
||||||
%statename INITIAL
|
|
||||||
"\\\\" {
|
|
||||||
$this->token = self::ESCAPEDBACKSLASH;
|
|
||||||
}
|
|
||||||
NONESCAPE {
|
|
||||||
$this->token = self::TEXT;
|
|
||||||
}
|
|
||||||
ESCAPEDTHING {
|
|
||||||
$this->token = self::CONTROLCHAR;
|
|
||||||
}
|
|
||||||
"[" {
|
|
||||||
$this->token = self::OPENCHARCLASS;
|
|
||||||
$this->yybegin(self::CHARACTERCLASSSTART);
|
|
||||||
}
|
|
||||||
"|" {
|
|
||||||
$this->token = self::BAR;
|
|
||||||
}
|
|
||||||
STRINGCHAR {
|
|
||||||
$this->token = self::TEXT;
|
|
||||||
}
|
|
||||||
COULDBEBACKREF {
|
|
||||||
$this->token = self::COULDBEBACKREF;
|
|
||||||
}
|
|
||||||
CONTROLCHAR {
|
|
||||||
$this->token = self::CONTROLCHAR;
|
|
||||||
}
|
|
||||||
"^" {
|
|
||||||
$this->token = self::MATCHSTART;
|
|
||||||
}
|
|
||||||
"\\A" {
|
|
||||||
$this->token = self::MATCHSTART;
|
|
||||||
}
|
|
||||||
")" {
|
|
||||||
$this->token = self::CLOSEPAREN;
|
|
||||||
$this->yybegin(self::INITIAL);
|
|
||||||
}
|
|
||||||
"$" {
|
|
||||||
$this->token = self::MATCHEND;
|
|
||||||
}
|
|
||||||
MULTIPLIER {
|
|
||||||
$this->token = self::MULTIPLIER;
|
|
||||||
}
|
|
||||||
SUBJECTEND {
|
|
||||||
$this->token = self::MATCHEND;
|
|
||||||
}
|
|
||||||
"(?" {
|
|
||||||
$this->token = self::OPENASSERTION;
|
|
||||||
$this->yybegin(self::ASSERTION);
|
|
||||||
}
|
|
||||||
"(" {
|
|
||||||
$this->token = self::OPENPAREN;
|
|
||||||
}
|
|
||||||
"." {
|
|
||||||
$this->token = self::FULLSTOP;
|
|
||||||
}
|
|
||||||
BACKREF {
|
|
||||||
$this->token = self::BACKREFERENCE;
|
|
||||||
}
|
|
||||||
UNICODESTUFF {
|
|
||||||
$this->token = self::CONTROLCHAR;
|
|
||||||
}
|
|
||||||
"\\p{" PROPERTYCODES "}" {
|
|
||||||
$this->token = self::CONTROLCHAR;
|
|
||||||
}
|
|
||||||
"\\p{^" PROPERTYCODES "}" {
|
|
||||||
$this->token = self::CONTROLCHAR;
|
|
||||||
}
|
|
||||||
"\\p" SIMPLEPROPERTYCODES {
|
|
||||||
$this->token = self::CONTROLCHAR;
|
|
||||||
}
|
|
||||||
"\\" {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*!lex2php
|
|
||||||
%statename CHARACTERCLASSSTART
|
|
||||||
"^" {
|
|
||||||
$this->token = self::NEGATE;
|
|
||||||
}
|
|
||||||
"]" {
|
|
||||||
$this->yybegin(self::CHARACTERCLASS);
|
|
||||||
$this->token = self::TEXT;
|
|
||||||
}
|
|
||||||
ANYTHING {
|
|
||||||
$this->yybegin(self::CHARACTERCLASS);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*!lex2php
|
|
||||||
%statename CHARACTERCLASS
|
|
||||||
"\\\\" {
|
|
||||||
$this->token = self::ESCAPEDBACKSLASH;
|
|
||||||
}
|
|
||||||
"]" {
|
|
||||||
$this->yybegin(self::INITIAL);
|
|
||||||
$this->token = self::CLOSECHARCLASS;
|
|
||||||
}
|
|
||||||
STRINGCHAR {
|
|
||||||
$this->token = self::TEXT;
|
|
||||||
}
|
|
||||||
CHARCLASSCONTROLCHAR {
|
|
||||||
$this->token = self::TEXT;
|
|
||||||
}
|
|
||||||
COULDBEBACKREF {
|
|
||||||
$this->token = self::COULDBEBACKREF;
|
|
||||||
}
|
|
||||||
BACKREF {
|
|
||||||
$this->token = self::BACKREFERENCE;
|
|
||||||
}
|
|
||||||
ESCAPEDCHARCLASSTHING {
|
|
||||||
$this->token = self::TEXT;
|
|
||||||
}
|
|
||||||
HYPHEN {
|
|
||||||
$this->token = self::HYPHEN;
|
|
||||||
$this->yybegin(self::RANGE);
|
|
||||||
}
|
|
||||||
NONESCAPECHARCLASS {
|
|
||||||
$this->token = self::TEXT;
|
|
||||||
}
|
|
||||||
"\\" {
|
|
||||||
return false; // ignore escaping of normal text
|
|
||||||
}
|
|
||||||
ANYTHING {
|
|
||||||
$this->token = self::TEXT;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*!lex2php
|
|
||||||
%statename RANGE
|
|
||||||
"\\\\" {
|
|
||||||
$this->token = self::ESCAPEDBACKSLASH;
|
|
||||||
}
|
|
||||||
"\\]" {
|
|
||||||
$this->token = self::TEXT;
|
|
||||||
$this->yybegin(self::CHARACTERCLASS);
|
|
||||||
}
|
|
||||||
CHARCLASSCONTROLCHAR {
|
|
||||||
$this->token = self::TEXT;
|
|
||||||
$this->yybegin(self::CHARACTERCLASS);
|
|
||||||
}
|
|
||||||
COULDBEBACKREF {
|
|
||||||
$this->token = self::COULDBEBACKREF;
|
|
||||||
}
|
|
||||||
BACKREF {
|
|
||||||
$this->token = self::BACKREFERENCE;
|
|
||||||
}
|
|
||||||
NONESCAPECHARCLASS {
|
|
||||||
$this->token = self::TEXT;
|
|
||||||
$this->yybegin(self::CHARACTERCLASS);
|
|
||||||
}
|
|
||||||
"\\" {
|
|
||||||
return false; // ignore escaping of normal text
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*!lex2php
|
|
||||||
%statename ASSERTION
|
|
||||||
INTERNALOPTIONS {
|
|
||||||
$this->token = self::INTERNALOPTIONS;
|
|
||||||
}
|
|
||||||
":" {
|
|
||||||
$this->token = self::COLON;
|
|
||||||
$this->yybegin(self::INITIAL);
|
|
||||||
}
|
|
||||||
")" {
|
|
||||||
$this->token = self::CLOSEPAREN;
|
|
||||||
$this->yybegin(self::INITIAL);
|
|
||||||
}
|
|
||||||
"P<" PATTERNNAME ">" {
|
|
||||||
$this->token = self::PATTERNNAME;
|
|
||||||
$this->yybegin(self::INITIAL);
|
|
||||||
}
|
|
||||||
"<=" {
|
|
||||||
$this->token = self::POSITIVELOOKBEHIND;
|
|
||||||
$this->yybegin(self::INITIAL);
|
|
||||||
}
|
|
||||||
"<!" {
|
|
||||||
$this->token = self::NEGATIVELOOKBEHIND;
|
|
||||||
$this->yybegin(self::INITIAL);
|
|
||||||
}
|
|
||||||
"=" {
|
|
||||||
$this->token = self::POSITIVELOOKAHEAD;
|
|
||||||
$this->yybegin(self::INITIAL);
|
|
||||||
}
|
|
||||||
"!" {
|
|
||||||
$this->token = self::NEGATIVELOOKAHEAD;
|
|
||||||
$this->yybegin(self::INITIAL);
|
|
||||||
}
|
|
||||||
">" {
|
|
||||||
$this->token = self::ONCEONLY;
|
|
||||||
$this->yybegin(self::INITIAL);
|
|
||||||
}
|
|
||||||
"(?" {
|
|
||||||
$this->token = self::OPENASSERTION;
|
|
||||||
}
|
|
||||||
COMMENT {
|
|
||||||
$this->token = self::COMMENT;
|
|
||||||
$this->yybegin(self::INITIAL);
|
|
||||||
}
|
|
||||||
"R" {
|
|
||||||
$this->token = self::RECUR;
|
|
||||||
}
|
|
||||||
ANYTHING {
|
|
||||||
$this->yybegin(self::INITIAL);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,477 +0,0 @@
|
|||||||
%name PHP_LexerGenerator_Regex_
|
|
||||||
%include {
|
|
||||||
require_once 'PHP/LexerGenerator/Exception.php';
|
|
||||||
}
|
|
||||||
%declare_class {class PHP_LexerGenerator_Regex_Parser}
|
|
||||||
%syntax_error {
|
|
||||||
/* ?><?php */
|
|
||||||
// we need to add auto-escaping of all stuff that needs it for result.
|
|
||||||
// and then validate the original regex only
|
|
||||||
echo "Syntax Error on line " . $this->_lex->line . ": token '" .
|
|
||||||
$this->_lex->value . "' while parsing rule:";
|
|
||||||
foreach ($this->yystack as $entry) {
|
|
||||||
echo $this->tokenName($entry->major) . ' ';
|
|
||||||
}
|
|
||||||
foreach ($this->yy_get_expected_tokens($yymajor) as $token) {
|
|
||||||
$expect[] = self::$yyTokenName[$token];
|
|
||||||
}
|
|
||||||
throw new Exception('Unexpected ' . $this->tokenName($yymajor) . '(' . $TOKEN
|
|
||||||
. '), expected one of: ' . implode(',', $expect));
|
|
||||||
}
|
|
||||||
%include_class {
|
|
||||||
private $_lex;
|
|
||||||
private $_subpatterns;
|
|
||||||
private $_updatePattern;
|
|
||||||
private $_patternIndex;
|
|
||||||
public $result;
|
|
||||||
function __construct($lex)
|
|
||||||
{
|
|
||||||
$this->result = new PHP_LexerGenerator_ParseryyToken('');
|
|
||||||
$this->_lex = $lex;
|
|
||||||
$this->_subpatterns = 0;
|
|
||||||
$this->_patternIndex = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function reset($patternIndex, $updatePattern = false)
|
|
||||||
{
|
|
||||||
$this->_updatePattern = $updatePattern;
|
|
||||||
$this->_patternIndex = $patternIndex;
|
|
||||||
$this->_subpatterns = 0;
|
|
||||||
$this->result = new PHP_LexerGenerator_ParseryyToken('');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
%left OPENPAREN OPENASSERTION BAR.
|
|
||||||
%right MULTIPLIER.
|
|
||||||
|
|
||||||
start ::= pattern(B). {
|
|
||||||
B->string = str_replace('"', '\\"', B->string);
|
|
||||||
$x = B->metadata;
|
|
||||||
$x['subpatterns'] = $this->_subpatterns;
|
|
||||||
B->metadata = $x;
|
|
||||||
$this->_subpatterns = 0;
|
|
||||||
$this->result = B;
|
|
||||||
}
|
|
||||||
|
|
||||||
pattern ::= MATCHSTART(B) basic_pattern MATCHEND(C). {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Cannot include start match "' .
|
|
||||||
B . '" or end match "' . C . '"');
|
|
||||||
}
|
|
||||||
pattern ::= MATCHSTART basic_pattern. {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Cannot include start match "' .
|
|
||||||
B . '"');
|
|
||||||
}
|
|
||||||
pattern ::= basic_pattern MATCHEND(C). {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Cannot include end match "' . C . '"');
|
|
||||||
}
|
|
||||||
pattern(A) ::= basic_pattern(B). {A = B;}
|
|
||||||
pattern(A) ::= pattern(B) BAR pattern(C). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(B->string . '|' . C->string, array(
|
|
||||||
'pattern' => B['pattern'] . '|' . C['pattern']));
|
|
||||||
}
|
|
||||||
|
|
||||||
basic_pattern(A) ::= basic_text(B). {A = B;}
|
|
||||||
basic_pattern(A) ::= character_class(B). {A = B;}
|
|
||||||
basic_pattern ::= assertion.
|
|
||||||
basic_pattern(A) ::= grouping(B). {A = B;}
|
|
||||||
basic_pattern(A) ::= lookahead(B). {A = B;}
|
|
||||||
basic_pattern ::= lookbehind.
|
|
||||||
basic_pattern(A) ::= subpattern(B). {A = B;}
|
|
||||||
basic_pattern(A) ::= onceonly(B). {A = B;}
|
|
||||||
basic_pattern(A) ::= comment(B). {A = B;}
|
|
||||||
basic_pattern(A) ::= recur(B). {A = B;}
|
|
||||||
basic_pattern(A) ::= conditional(B). {A = B;}
|
|
||||||
basic_pattern(A) ::= basic_pattern(P) basic_text(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(P->string . B->string, array(
|
|
||||||
'pattern' => P['pattern'] . B['pattern']));
|
|
||||||
}
|
|
||||||
basic_pattern(A) ::= basic_pattern(P) character_class(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(P->string . B->string, array(
|
|
||||||
'pattern' => P['pattern'] . B['pattern']));
|
|
||||||
}
|
|
||||||
basic_pattern ::= basic_pattern assertion.
|
|
||||||
basic_pattern(A) ::= basic_pattern(P) grouping(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(P->string . B->string, array(
|
|
||||||
'pattern' => P['pattern'] . B['pattern']));
|
|
||||||
}
|
|
||||||
basic_pattern(A) ::= basic_pattern(P) lookahead(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(P->string . B->string, array(
|
|
||||||
'pattern' => P['pattern'] . B['pattern']));
|
|
||||||
}
|
|
||||||
basic_pattern ::= basic_pattern lookbehind.
|
|
||||||
basic_pattern(A) ::= basic_pattern(P) subpattern(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(P->string . B->string, array(
|
|
||||||
'pattern' => P['pattern'] . B['pattern']));
|
|
||||||
}
|
|
||||||
basic_pattern(A) ::= basic_pattern(P) onceonly(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(P->string . B->string, array(
|
|
||||||
'pattern' => P['pattern'] . B['pattern']));
|
|
||||||
}
|
|
||||||
basic_pattern(A) ::= basic_pattern(P) comment(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(P->string . B->string, array(
|
|
||||||
'pattern' => P['pattern'] . B['pattern']));
|
|
||||||
}
|
|
||||||
basic_pattern(A) ::= basic_pattern(P) recur(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(P->string . B->string, array(
|
|
||||||
'pattern' => P['pattern'] . B['pattern']));
|
|
||||||
}
|
|
||||||
basic_pattern(A) ::= basic_pattern(P) conditional(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(P->string . B->string, array(
|
|
||||||
'pattern' => P['pattern'] . B['pattern']));
|
|
||||||
}
|
|
||||||
|
|
||||||
character_class(A) ::= OPENCHARCLASS character_class_contents(B) CLOSECHARCLASS. {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('[' . B->string . ']', array(
|
|
||||||
'pattern' => '[' . B['pattern'] . ']'));
|
|
||||||
}
|
|
||||||
character_class(A) ::= OPENCHARCLASS NEGATE character_class_contents(B) CLOSECHARCLASS. {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('[^' . B->string . ']', array(
|
|
||||||
'pattern' => '[^' . B['pattern'] . ']'));
|
|
||||||
}
|
|
||||||
character_class(A) ::= OPENCHARCLASS character_class_contents(B) CLOSECHARCLASS MULTIPLIER(M). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('[' . B->string . ']' . M, array(
|
|
||||||
'pattern' => '[' . B['pattern'] . ']' . M));
|
|
||||||
}
|
|
||||||
character_class(A) ::= OPENCHARCLASS NEGATE character_class_contents(B) CLOSECHARCLASS MULTIPLIER(M). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('[^' . B->string . ']' . M, array(
|
|
||||||
'pattern' => '[^' . B['pattern'] . ']' . M));
|
|
||||||
}
|
|
||||||
|
|
||||||
character_class_contents(A) ::= TEXT(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(B, array(
|
|
||||||
'pattern' => B));
|
|
||||||
}
|
|
||||||
character_class_contents(A) ::= ESCAPEDBACKSLASH(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('\\\\' . B, array(
|
|
||||||
'pattern' => B));
|
|
||||||
}
|
|
||||||
character_class_contents(A) ::= ESCAPEDBACKSLASH(B) HYPHEN TEXT(C). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('\\\\' . B . '-' . C, array(
|
|
||||||
'pattern' => B . '-' . C));
|
|
||||||
}
|
|
||||||
character_class_contents(A) ::= TEXT(B) HYPHEN TEXT(C). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(B . '-' . C, array(
|
|
||||||
'pattern' => B . '-' . C));
|
|
||||||
}
|
|
||||||
character_class_contents(A) ::= TEXT(B) HYPHEN ESCAPEDBACKSLASH(C). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(B . '-\\\\' . C, array(
|
|
||||||
'pattern' => B . '-' . C));
|
|
||||||
}
|
|
||||||
character_class_contents(A) ::= BACKREFERENCE(B). {
|
|
||||||
if (((int) substr(B, 1)) > $this->_subpatterns) {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Back-reference refers to non-existent ' .
|
|
||||||
'sub-pattern ' . substr(B, 1));
|
|
||||||
}
|
|
||||||
B = substr(B, 1);
|
|
||||||
// adjust back-reference for containing ()
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('\\\\' . (B + $this->_patternIndex), array(
|
|
||||||
'pattern' => '\\' . ($this->_updatePattern ? (B + $this->_patternIndex) : B)));
|
|
||||||
}
|
|
||||||
character_class_contents(A) ::= COULDBEBACKREF(B). {
|
|
||||||
if (((int) substr(B, 1)) > $this->_subpatterns) {
|
|
||||||
throw new PHP_LexerGenerator_Exception(B . ' will be interpreted as an invalid' .
|
|
||||||
' back-reference, use "\\0' . substr(B, 1) . ' for octal');
|
|
||||||
}
|
|
||||||
B = substr(B, 1);
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('\\\\' . (B + $this->_patternIndex), array(
|
|
||||||
'pattern' => '\\' . ($this->_updatePattern ? (B + $this->_patternIndex) : B)));
|
|
||||||
}
|
|
||||||
character_class_contents(A) ::= character_class_contents(D) CONTROLCHAR(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(D->string . '\\' . B, array(
|
|
||||||
'pattern' => D['pattern'] . B));
|
|
||||||
}
|
|
||||||
character_class_contents(A) ::= character_class_contents(D) ESCAPEDBACKSLASH(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(D->string . '\\\\' . B, array(
|
|
||||||
'pattern' => D['pattern'] . B));
|
|
||||||
}
|
|
||||||
character_class_contents(A) ::= character_class_contents(D) TEXT(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(D->string . B, array(
|
|
||||||
'pattern' => D['pattern'] . B));
|
|
||||||
}
|
|
||||||
character_class_contents(A) ::= character_class_contents(D) ESCAPEDBACKSLASH(B) HYPHEN CONTROLCHAR(C). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(D->string . '\\\\' . B . '-\\' . C, array(
|
|
||||||
'pattern' => D['pattern'] . B . '-' . C));
|
|
||||||
}
|
|
||||||
character_class_contents(A) ::= character_class_contents(D) ESCAPEDBACKSLASH(B) HYPHEN TEXT(C). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(D->string . '\\\\' . B . '-' . C, array(
|
|
||||||
'pattern' => D['pattern'] . B . '-' . C));
|
|
||||||
}
|
|
||||||
character_class_contents(A) ::= character_class_contents(D) TEXT(B) HYPHEN ESCAPEDBACKSLASH(C). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(D->string . B . '-\\\\' . C, array(
|
|
||||||
'pattern' => D['pattern'] . B . '-' . C));
|
|
||||||
}
|
|
||||||
character_class_contents(A) ::= character_class_contents(D) TEXT(B) HYPHEN TEXT(C). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(D->string . B . '-' . C, array(
|
|
||||||
'pattern' => D['pattern'] . B . '-' . C));
|
|
||||||
}
|
|
||||||
character_class_contents(A) ::= character_class_contents(P) BACKREFERENCE(B). {
|
|
||||||
if (((int) substr(B, 1)) > $this->_subpatterns) {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Back-reference refers to non-existent ' .
|
|
||||||
'sub-pattern ' . substr(B, 1));
|
|
||||||
}
|
|
||||||
B = substr(B, 1);
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(P->string . '\\\\' . (B + $this->_patternIndex), array(
|
|
||||||
'pattern' => P['pattern'] . '\\' . ($this->_updatePattern ? (B + $this->_patternIndex) : B)));
|
|
||||||
}
|
|
||||||
character_class_contents(A) ::= character_class_contents(P) COULDBEBACKREF(B). {
|
|
||||||
if (((int) substr(B, 1)) > $this->_subpatterns) {
|
|
||||||
throw new PHP_LexerGenerator_Exception(B . ' will be interpreted as an invalid' .
|
|
||||||
' back-reference, use "\\0' . substr(B, 1) . ' for octal');
|
|
||||||
}
|
|
||||||
B = substr(B, 1);
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(P->string . '\\\\' . (B + $this->_patternIndex), array(
|
|
||||||
'pattern' => P['pattern'] . '\\' . ($this->_updatePattern ? (B + $this->_patternIndex) : B)));
|
|
||||||
}
|
|
||||||
|
|
||||||
basic_text(A) ::= TEXT(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(B, array(
|
|
||||||
'pattern' => B));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= TEXT(B) MULTIPLIER(M). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(B . M, array(
|
|
||||||
'pattern' => B . M));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= FULLSTOP(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(B, array(
|
|
||||||
'pattern' => B));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= FULLSTOP(B) MULTIPLIER(M). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(B . M, array(
|
|
||||||
'pattern' => B . M));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= CONTROLCHAR(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('\\' . B, array(
|
|
||||||
'pattern' => B));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= CONTROLCHAR(B) MULTIPLIER(M). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('\\' . B . M, array(
|
|
||||||
'pattern' => B . M));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= ESCAPEDBACKSLASH(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('\\\\' . B, array(
|
|
||||||
'pattern' => B));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= ESCAPEDBACKSLASH(B) MULTIPLIER(M). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('\\\\' . B . M, array(
|
|
||||||
'pattern' => B . M));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= BACKREFERENCE(B). {
|
|
||||||
if (((int) substr(B, 1)) > $this->_subpatterns) {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Back-reference refers to non-existent ' .
|
|
||||||
'sub-pattern ' . substr(B, 1));
|
|
||||||
}
|
|
||||||
B = substr(B, 1);
|
|
||||||
// adjust back-reference for containing ()
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('\\\\' . (B + $this->_patternIndex), array(
|
|
||||||
'pattern' => '\\' . ($this->_updatePattern ? (B + $this->_patternIndex) : B)));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= BACKREFERENCE(B) MULTIPLIER(M). {
|
|
||||||
if (((int) substr(B, 1)) > $this->_subpatterns) {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Back-reference refers to non-existent ' .
|
|
||||||
'sub-pattern ' . substr(B, 1));
|
|
||||||
}
|
|
||||||
B = substr(B, 1);
|
|
||||||
// adjust back-reference for containing ()
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('\\\\' . (B + $this->_patternIndex) . M, array(
|
|
||||||
'pattern' => '\\' . ($this->_updatePattern ? (B + $this->_patternIndex) : B) . M));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= COULDBEBACKREF(B). {
|
|
||||||
if (((int) substr(B, 1)) > $this->_subpatterns) {
|
|
||||||
throw new PHP_LexerGenerator_Exception(B . ' will be interpreted as an invalid' .
|
|
||||||
' back-reference, use "\\0' . substr(B, 1) . ' for octal');
|
|
||||||
}
|
|
||||||
B = substr(B, 1);
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('\\\\' . (B + $this->_patternIndex), array(
|
|
||||||
'pattern' => '\\' . ($this->_updatePattern ? (B + $this->_patternIndex) : B)));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= COULDBEBACKREF(B) MULTIPLIER(M). {
|
|
||||||
if (((int) substr(B, 1)) > $this->_subpatterns) {
|
|
||||||
throw new PHP_LexerGenerator_Exception(B . ' will be interpreted as an invalid' .
|
|
||||||
' back-reference, use "\\0' . substr(B, 1) . ' for octal');
|
|
||||||
}
|
|
||||||
B = substr(B, 1);
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('\\\\' . (B + $this->_patternIndex) . M, array(
|
|
||||||
'pattern' => '\\' . ($this->_updatePattern ? (B + $this->_patternIndex) : B) . M));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= basic_text(T) TEXT(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(T->string . B, array(
|
|
||||||
'pattern' => T['pattern'] . B));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= basic_text(T) TEXT(B) MULTIPLIER(M). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(T->string . B . M, array(
|
|
||||||
'pattern' => T['pattern'] . B . M));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= basic_text(T) FULLSTOP(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(T->string . B, array(
|
|
||||||
'pattern' => T['pattern'] . B));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= basic_text(T) FULLSTOP(B) MULTIPLIER(M). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(T->string . B . M, array(
|
|
||||||
'pattern' => T['pattern'] . B . M));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= basic_text(T) CONTROLCHAR(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(T->string . '\\' . B, array(
|
|
||||||
'pattern' => T['pattern'] . B));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= basic_text(T) CONTROLCHAR(B) MULTIPLIER(M). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(T->string . '\\' . B . M, array(
|
|
||||||
'pattern' => T['pattern'] . B . M));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= basic_text(T) ESCAPEDBACKSLASH(B). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(T->string . '\\\\' . B, array(
|
|
||||||
'pattern' => T['pattern'] . B));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= basic_text(T) ESCAPEDBACKSLASH(B) MULTIPLIER(M). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(T->string . '\\\\' . B . M, array(
|
|
||||||
'pattern' => T['pattern'] . B . M));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= basic_text(P) BACKREFERENCE(B). {
|
|
||||||
if (((int) substr(B, 1)) > $this->_subpatterns) {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Back-reference refers to non-existent ' .
|
|
||||||
'sub-pattern ' . substr(B, 1));
|
|
||||||
}
|
|
||||||
B = substr(B, 1);
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(P->string . '\\\\' . (B + $this->_patternIndex), array(
|
|
||||||
'pattern' => P['pattern'] . '\\' . ($this->_updatePattern ? (B + $this->_patternIndex) : B)));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= basic_text(P) BACKREFERENCE(B) MULTIPLIER(M). {
|
|
||||||
if (((int) substr(B, 1)) > $this->_subpatterns) {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Back-reference refers to non-existent ' .
|
|
||||||
'sub-pattern ' . substr(B, 1));
|
|
||||||
}
|
|
||||||
B = substr(B, 1);
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(P->string . '\\\\' . (B + $this->_patternIndex) . M, array(
|
|
||||||
'pattern' => P['pattern'] . '\\' . ($this->_updatePattern ? (B + $this->_patternIndex) : B) . M));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= basic_text(P) COULDBEBACKREF(B). {
|
|
||||||
if (((int) substr(B, 1)) > $this->_subpatterns) {
|
|
||||||
throw new PHP_LexerGenerator_Exception(B . ' will be interpreted as an invalid' .
|
|
||||||
' back-reference, use "\\0' . substr(B, 1) . ' for octal');
|
|
||||||
}
|
|
||||||
B = substr(B, 1);
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(P->string . '\\\\' . (B + $this->_patternIndex), array(
|
|
||||||
'pattern' => P['pattern'] . '\\' . ($this->_updatePattern ? (B + $this->_patternIndex) : B)));
|
|
||||||
}
|
|
||||||
basic_text(A) ::= basic_text(P) COULDBEBACKREF(B) MULTIPLIER(M). {
|
|
||||||
if (((int) substr(B, 1)) > $this->_subpatterns) {
|
|
||||||
throw new PHP_LexerGenerator_Exception(B . ' will be interpreted as an invalid' .
|
|
||||||
' back-reference, use "\\0' . substr(B, 1) . ' for octal');
|
|
||||||
}
|
|
||||||
B = substr(B, 1);
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken(P->string . '\\\\' . (B + $this->_patternIndex) . M, array(
|
|
||||||
'pattern' => P['pattern'] . '\\' . ($this->_updatePattern ? (B + $this->_patternIndex) : B) . M));
|
|
||||||
}
|
|
||||||
|
|
||||||
assertion ::= OPENASSERTION(B) INTERNALOPTIONS(C) CLOSEPAREN(D). {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Error: cannot set preg options directly with "' .
|
|
||||||
B . C . D . '"');
|
|
||||||
}
|
|
||||||
assertion ::= OPENASSERTION(B) INTERNALOPTIONS(C) COLON(D) pattern(E) CLOSEPAREN(F). {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Error: cannot set preg options directly with "' .
|
|
||||||
B . C . D . E['pattern'] . F . '"');
|
|
||||||
}
|
|
||||||
|
|
||||||
grouping(A) ::= OPENASSERTION COLON pattern(B) CLOSEPAREN. {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('(?:' . B->string . ')', array(
|
|
||||||
'pattern' => '(?:' . B['pattern'] . ')'));
|
|
||||||
}
|
|
||||||
grouping(A) ::= OPENASSERTION COLON pattern(B) CLOSEPAREN MULTIPLIER(M). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('(?:' . B->string . ')' . M, array(
|
|
||||||
'pattern' => '(?:' . B['pattern'] . ')' . M));
|
|
||||||
}
|
|
||||||
|
|
||||||
conditional(A) ::= OPENASSERTION OPENPAREN TEXT(T) CLOSEPAREN pattern(B) CLOSEPAREN MULTIPLIER(M). {
|
|
||||||
if (T != 'R') {
|
|
||||||
if (!preg_match('/[1-9][0-9]*/', T)) {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Invalid sub-pattern conditional: "(?(' . T . ')"');
|
|
||||||
}
|
|
||||||
if (T > $this->_subpatterns) {
|
|
||||||
throw new PHP_LexerGenerator_Exception('sub-pattern conditional . "' . T . '" refers to non-existent sub-pattern');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Recursive conditional (?(' . T . ')" cannot work in this lexer');
|
|
||||||
}
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('(?(' . T . ')' . B->string . ')' . M, array(
|
|
||||||
'pattern' => '(?(' . T . ')' . B['pattern'] . ')' . M));
|
|
||||||
}
|
|
||||||
conditional(A) ::= OPENASSERTION OPENPAREN TEXT(T) CLOSEPAREN pattern(B) CLOSEPAREN. {
|
|
||||||
if (T != 'R') {
|
|
||||||
if (!preg_match('/[1-9][0-9]*/', T)) {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Invalid sub-pattern conditional: "(?(' . T . ')"');
|
|
||||||
}
|
|
||||||
if (T > $this->_subpatterns) {
|
|
||||||
throw new PHP_LexerGenerator_Exception('sub-pattern conditional . "' . T . '" refers to non-existent sub-pattern');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Recursive conditional (?(' . T . ')" cannot work in this lexer');
|
|
||||||
}
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('(?(' . T . ')' . B->string . ')', array(
|
|
||||||
'pattern' => '(?(' . T . ')' . B['pattern'] . ')'));
|
|
||||||
}
|
|
||||||
conditional(A) ::= OPENASSERTION lookahead(B) pattern(C) CLOSEPAREN. {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('(?' . B->string . C->string . ')', array(
|
|
||||||
'pattern' => '(?' . B['pattern'] . C['pattern'] . ')'));
|
|
||||||
}
|
|
||||||
conditional(A) ::= OPENASSERTION lookahead(B) pattern(C) CLOSEPAREN MULTIPLIER(M). {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('(?' . B->string . C->string . ')' . M, array(
|
|
||||||
'pattern' => '(?' . B['pattern'] . C['pattern'] . ')' . M));
|
|
||||||
}
|
|
||||||
conditional ::= OPENASSERTION lookbehind pattern(B) CLOSEPAREN. {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Look-behind assertions cannot be used: "(?<=' .
|
|
||||||
B['pattern'] . ')');
|
|
||||||
}
|
|
||||||
conditional ::= OPENASSERTION lookbehind pattern(B) CLOSEPAREN MULTIPLIER. {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Look-behind assertions cannot be used: "(?<=' .
|
|
||||||
B['pattern'] . ')');
|
|
||||||
}
|
|
||||||
|
|
||||||
lookahead(A) ::= OPENASSERTION POSITIVELOOKAHEAD pattern(B) CLOSEPAREN. {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('(?=' . B->string . ')', array(
|
|
||||||
'pattern '=> '(?=' . B['pattern'] . ')'));
|
|
||||||
}
|
|
||||||
lookahead(A) ::= OPENASSERTION NEGATIVELOOKAHEAD pattern(B) CLOSEPAREN. {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('(?!' . B->string . ')', array(
|
|
||||||
'pattern' => '(?!' . B['pattern'] . ')'));
|
|
||||||
}
|
|
||||||
|
|
||||||
lookbehind ::= OPENASSERTION POSITIVELOOKBEHIND pattern(B) CLOSEPAREN. {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Look-behind assertions cannot be used: "(?<=' .
|
|
||||||
B['pattern'] . ')');
|
|
||||||
}
|
|
||||||
lookbehind ::= OPENASSERTION NEGATIVELOOKBEHIND pattern(B) CLOSEPAREN. {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Look-behind assertions cannot be used: "(?<!' .
|
|
||||||
B['pattern'] . ')');
|
|
||||||
}
|
|
||||||
|
|
||||||
subpattern ::= OPENASSERTION PATTERNNAME(B) pattern CLOSEPAREN. {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Cannot use named sub-patterns: "(' .
|
|
||||||
B['pattern'] . ')');
|
|
||||||
}
|
|
||||||
subpattern ::= OPENASSERTION PATTERNNAME(B) pattern CLOSEPAREN MULTIPLIER. {
|
|
||||||
throw new PHP_LexerGenerator_Exception('Cannot use named sub-patterns: "(' .
|
|
||||||
B['pattern'] . ')');
|
|
||||||
}
|
|
||||||
subpattern(A) ::= OPENPAREN pattern(B) CLOSEPAREN. {
|
|
||||||
$this->_subpatterns++;
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('(' . B->string . ')', array(
|
|
||||||
'pattern' => '(' . B['pattern'] . ')'));
|
|
||||||
}
|
|
||||||
subpattern(A) ::= OPENPAREN pattern(B) CLOSEPAREN MULTIPLIER(M). {
|
|
||||||
$this->_subpatterns++;
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('(' . B->string . ')' . M, array(
|
|
||||||
'pattern' => '(' . B['pattern'] . ')' . M));
|
|
||||||
}
|
|
||||||
|
|
||||||
onceonly(A) ::= OPENASSERTION ONCEONLY pattern(B) CLOSEPAREN. {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('(?>' . B->string . ')', array(
|
|
||||||
'pattern' => '(?>' . B['pattern'] . ')'));
|
|
||||||
}
|
|
||||||
|
|
||||||
comment(A) ::= OPENASSERTION COMMENT(B) CLOSEPAREN. {
|
|
||||||
A = new PHP_LexerGenerator_ParseryyToken('(' . B->string . ')', array(
|
|
||||||
'pattern' => '(' . B['pattern'] . ')'));
|
|
||||||
}
|
|
||||||
|
|
||||||
recur ::= OPENASSERTION RECUR CLOSEPAREN. {
|
|
||||||
throw new Exception('(?R) cannot work in this lexer');
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once 'PHP/LexerGenerator.php';
|
|
||||||
$a = new PHP_LexerGenerator($_SERVER['argv'][1]);
|
|
||||||
?>
|
|
||||||
@@ -1,806 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* PHP_ParserGenerator, a php 5 parser generator.
|
|
||||||
*
|
|
||||||
* This is a direct port of the Lemon parser generator, found at
|
|
||||||
* {@link http://www.hwaci.com/sw/lemon/}
|
|
||||||
*
|
|
||||||
* There are a few PHP-specific changes to the lemon parser generator.
|
|
||||||
*
|
|
||||||
* - %extra_argument is removed, as class constructor can be used to
|
|
||||||
* pass in extra information
|
|
||||||
* - %token_type and company are irrelevant in PHP, and so are removed
|
|
||||||
* - %declare_class is added to define the parser class name and any
|
|
||||||
* implements/extends information
|
|
||||||
* - %include_class is added to allow insertion of extra class information
|
|
||||||
* such as constants, a class constructor, etc.
|
|
||||||
*
|
|
||||||
* Other changes make the parser more robust, and also make reporting
|
|
||||||
* syntax errors simpler. Detection of expected tokens eliminates some
|
|
||||||
* problematic edge cases where an unexpected token could cause the parser
|
|
||||||
* to simply accept input.
|
|
||||||
*
|
|
||||||
* Otherwise, the file format is identical to the Lemon parser generator
|
|
||||||
*
|
|
||||||
* PHP version 5
|
|
||||||
*
|
|
||||||
* LICENSE:
|
|
||||||
*
|
|
||||||
* Copyright (c) 2006, Gregory Beaver <cellog@php.net>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in
|
|
||||||
* the documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the PHP_ParserGenerator nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived
|
|
||||||
* from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
||||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
||||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version CVS: $Id: ParserGenerator.php 302382 2010-08-17 06:08:09Z jespino $
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since File available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
/**#@+
|
|
||||||
* Basic components of the parser generator
|
|
||||||
*/
|
|
||||||
require_once 'PHP/ParserGenerator/Action.php';
|
|
||||||
require_once 'PHP/ParserGenerator/ActionTable.php';
|
|
||||||
require_once 'PHP/ParserGenerator/Config.php';
|
|
||||||
require_once 'PHP/ParserGenerator/Data.php';
|
|
||||||
require_once 'PHP/ParserGenerator/Symbol.php';
|
|
||||||
require_once 'PHP/ParserGenerator/Rule.php';
|
|
||||||
require_once 'PHP/ParserGenerator/Parser.php';
|
|
||||||
require_once 'PHP/ParserGenerator/PropagationLink.php';
|
|
||||||
require_once 'PHP/ParserGenerator/State.php';
|
|
||||||
/**#@-*/
|
|
||||||
/**
|
|
||||||
* The basic home class for the parser generator
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version Release: @package_version@
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since Class available since Release 0.1.0
|
|
||||||
* @example Lempar.php
|
|
||||||
* @example examples/Parser.y Sample parser file format (PHP_LexerGenerator's parser)
|
|
||||||
* @example examples/Parser.php Sample parser file format PHP code (PHP_LexerGenerator's parser)
|
|
||||||
*/
|
|
||||||
class PHP_ParserGenerator
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Set this to 1 to turn on debugging of Lemon's parsing of
|
|
||||||
* grammar files.
|
|
||||||
*/
|
|
||||||
const DEBUG = 0;
|
|
||||||
const MAXRHS = 1000;
|
|
||||||
const OPT_FLAG = 1, OPT_INT = 2, OPT_DBL = 3, OPT_STR = 4,
|
|
||||||
OPT_FFLAG = 5, OPT_FINT = 6, OPT_FDBL = 7, OPT_FSTR = 8;
|
|
||||||
public $azDefine = array();
|
|
||||||
private static $_options = array(
|
|
||||||
'b' => array(
|
|
||||||
'type' => self::OPT_FLAG,
|
|
||||||
'arg' => 'basisflag',
|
|
||||||
'message' => 'Print only the basis in report.'
|
|
||||||
),
|
|
||||||
'c' => array(
|
|
||||||
'type' => self::OPT_FLAG,
|
|
||||||
'arg' => 'compress',
|
|
||||||
'message' => 'Don\'t compress the action table.'
|
|
||||||
),
|
|
||||||
'D' => array(
|
|
||||||
'type' => self::OPT_FSTR,
|
|
||||||
'arg' => 'handleDOption',
|
|
||||||
'message' => 'Define an %ifdef macro.'
|
|
||||||
),
|
|
||||||
'g' => array(
|
|
||||||
'type' => self::OPT_FLAG,
|
|
||||||
'arg' => 'rpflag',
|
|
||||||
'message' => 'Print grammar without actions.'
|
|
||||||
),
|
|
||||||
'm' => array(
|
|
||||||
'type' => self::OPT_FLAG,
|
|
||||||
'arg' => 'mhflag',
|
|
||||||
'message' => 'Output a makeheaders compatible file'
|
|
||||||
),
|
|
||||||
'q' => array(
|
|
||||||
'type' => self::OPT_FLAG,
|
|
||||||
'arg' => 'quiet',
|
|
||||||
'message' => '(Quiet) Don\'t print the report file.'
|
|
||||||
),
|
|
||||||
's' => array(
|
|
||||||
'type' => self::OPT_FLAG,
|
|
||||||
'arg' => 'statistics',
|
|
||||||
'message' => 'Print parser stats to standard output.'
|
|
||||||
),
|
|
||||||
'x' => array(
|
|
||||||
'type' => self::OPT_FLAG,
|
|
||||||
'arg' => 'version',
|
|
||||||
'message' => 'Print the version number.'
|
|
||||||
),
|
|
||||||
'T' => array(
|
|
||||||
'type' => self::OPT_STR,
|
|
||||||
'arg' => 'parser_template',
|
|
||||||
'message' => 'Use different parser template file.'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
private $_basisflag = 0;
|
|
||||||
private $_compress = 0;
|
|
||||||
private $_rpflag = 0;
|
|
||||||
private $_mhflag = 0;
|
|
||||||
private $_quiet = 0;
|
|
||||||
private $_statistics = 0;
|
|
||||||
private $_version = 0;
|
|
||||||
private $_size;
|
|
||||||
private $_parser_template = "";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process a flag command line argument.
|
|
||||||
*
|
|
||||||
* @param int $i
|
|
||||||
* @param array $argv
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
function handleflags($i, $argv)
|
|
||||||
{
|
|
||||||
if (!isset($argv[1]) || !isset(self::$_options[$argv[$i][1]])) {
|
|
||||||
throw new Exception('Command line syntax error: undefined option "' . $argv[$i] . '"');
|
|
||||||
}
|
|
||||||
$v = self::$_options[$argv[$i][1]] == '-';
|
|
||||||
if (self::$_options[$argv[$i][1]]['type'] == self::OPT_FLAG) {
|
|
||||||
$this->{self::$_options[$argv[$i][1]]['arg']} = 1;
|
|
||||||
} elseif (self::$_options[$argv[$i][1]]['type'] == self::OPT_FFLAG) {
|
|
||||||
$this->{self::$_options[$argv[$i][1]]['arg']}($v);
|
|
||||||
} elseif (self::$_options[$argv[$i][1]]['type'] == self::OPT_FSTR) {
|
|
||||||
$this->{self::$_options[$argv[$i][1]]['arg']}(substr($v, 2));
|
|
||||||
} else {
|
|
||||||
throw new Exception('Command line syntax error: missing argument on switch: "' . $argv[$i] . '"');
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process a command line switch which has an argument.
|
|
||||||
*
|
|
||||||
* @param int $i
|
|
||||||
* @param array $argv
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
function handleswitch($i, $argv)
|
|
||||||
{
|
|
||||||
$lv = 0;
|
|
||||||
$dv = 0.0;
|
|
||||||
$sv = $end = $cp = '';
|
|
||||||
$j; // int
|
|
||||||
$errcnt = 0;
|
|
||||||
$cp = strstr($argv[$i], '=');
|
|
||||||
if (!$cp) {
|
|
||||||
throw new Exception('INTERNAL ERROR: handleswitch passed bad argument, no "=" in arg');
|
|
||||||
}
|
|
||||||
$argv[$i] = substr($argv[$i], 0, strlen($argv[$i]) - strlen($cp));
|
|
||||||
if (!isset(self::$_options[$argv[$i]])) {
|
|
||||||
throw new Exception('Command line syntax error: undefined option "' . $argv[$i] .
|
|
||||||
$cp . '"');
|
|
||||||
}
|
|
||||||
$cp = substr($cp, 1);
|
|
||||||
switch (self::$_options[$argv[$i]]['type']) {
|
|
||||||
case self::OPT_FLAG:
|
|
||||||
case self::OPT_FFLAG:
|
|
||||||
throw new Exception('Command line syntax error: option requires an argument "' .
|
|
||||||
$argv[$i] . '=' . $cp . '"');
|
|
||||||
case self::OPT_DBL:
|
|
||||||
case self::OPT_FDBL:
|
|
||||||
$dv = (double) $cp;
|
|
||||||
break;
|
|
||||||
case self::OPT_INT:
|
|
||||||
case self::OPT_FINT:
|
|
||||||
$lv = (int) $cp;
|
|
||||||
break;
|
|
||||||
case self::OPT_STR:
|
|
||||||
case self::OPT_FSTR:
|
|
||||||
$sv = $cp;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch(self::$_options[$argv[$i]]['type']) {
|
|
||||||
case self::OPT_FLAG:
|
|
||||||
case self::OPT_FFLAG:
|
|
||||||
break;
|
|
||||||
case self::OPT_DBL:
|
|
||||||
$this->{self::$_options[$argv[$i]]['arg']} = $dv;
|
|
||||||
break;
|
|
||||||
case self::OPT_FDBL:
|
|
||||||
$this->{self::$_options[$argv[$i]]['arg']}($dv);
|
|
||||||
break;
|
|
||||||
case self::OPT_INT:
|
|
||||||
$this->{self::$_options[$argv[$i]]['arg']} = $lv;
|
|
||||||
break;
|
|
||||||
case self::OPT_FINT:
|
|
||||||
$this->{self::$_options[$argv[$i]]['arg']}($lv);
|
|
||||||
break;
|
|
||||||
case self::OPT_STR:
|
|
||||||
$this->{self::$_options[$argv[$i]]['arg']} = $sv;
|
|
||||||
break;
|
|
||||||
case self::OPT_FSTR:
|
|
||||||
$this->{self::$_options[$argv[$i]]['arg']}($sv);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OptInit
|
|
||||||
*
|
|
||||||
* @param array $a Arguments
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
function OptInit($a)
|
|
||||||
{
|
|
||||||
$errcnt = 0;
|
|
||||||
$argv = $a;
|
|
||||||
try {
|
|
||||||
if (is_array($argv) && count($argv) && self::$_options) {
|
|
||||||
for ($i = 1; $i < count($argv); $i++) {
|
|
||||||
if ($argv[$i][0] == '+' || $argv[$i][0] == '-') {
|
|
||||||
$errcnt += $this->handleflags($i, $argv);
|
|
||||||
} elseif (strstr($argv[$i], '=')) {
|
|
||||||
$errcnt += $this->handleswitch($i, $argv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$this->OptPrint();
|
|
||||||
echo $e->getMessage()."\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the index of the N-th non-switch argument. Return -1
|
|
||||||
* if N is out of range.
|
|
||||||
*
|
|
||||||
* @param int $n
|
|
||||||
* @param int $a
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
private function _argindex($n, $a)
|
|
||||||
{
|
|
||||||
$dashdash = 0;
|
|
||||||
if (!is_array($a) || !count($a)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
for ($i=1; $i < count($a); $i++) {
|
|
||||||
if ($dashdash || !($a[$i][0] == '-' || $a[$i][0] == '+' || strchr($a[$i], '='))) {
|
|
||||||
if ($n == 0) {
|
|
||||||
return $i;
|
|
||||||
}
|
|
||||||
$n--;
|
|
||||||
}
|
|
||||||
if ($_SERVER['argv'][$i] == '--') {
|
|
||||||
$dashdash = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the value of the non-option argument as indexed by $i
|
|
||||||
*
|
|
||||||
* @param int $i
|
|
||||||
* @param array $a the value of $argv
|
|
||||||
*
|
|
||||||
* @return 0|string
|
|
||||||
*/
|
|
||||||
private function _optArg($i, $a)
|
|
||||||
{
|
|
||||||
if (-1 == ($ind = $this->_argindex($i, $a))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return $a[$ind];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $a
|
|
||||||
*
|
|
||||||
* @return int number of arguments
|
|
||||||
*/
|
|
||||||
function OptNArgs($a)
|
|
||||||
{
|
|
||||||
$cnt = $dashdash = 0;
|
|
||||||
if (is_array($a) && count($a)) {
|
|
||||||
for ($i = 1; $i < count($a); $i++) {
|
|
||||||
if ($dashdash
|
|
||||||
|| !($a[$i][0] == '-' || $a[$i][0] == '+' || strchr($a[$i], '='))
|
|
||||||
) {
|
|
||||||
$cnt++;
|
|
||||||
}
|
|
||||||
if ($a[$i] == "--") {
|
|
||||||
$dashdash = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $cnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Print out command-line options
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
function OptPrint()
|
|
||||||
{
|
|
||||||
$max = 0;
|
|
||||||
foreach (self::$_options as $label => $info) {
|
|
||||||
$len = strlen($label) + 1;
|
|
||||||
switch ($info['type']) {
|
|
||||||
case self::OPT_FLAG:
|
|
||||||
case self::OPT_FFLAG:
|
|
||||||
break;
|
|
||||||
case self::OPT_INT:
|
|
||||||
case self::OPT_FINT:
|
|
||||||
$len += 9; /* length of "<integer>" */
|
|
||||||
break;
|
|
||||||
case self::OPT_DBL:
|
|
||||||
case self::OPT_FDBL:
|
|
||||||
$len += 6; /* length of "<real>" */
|
|
||||||
break;
|
|
||||||
case self::OPT_STR:
|
|
||||||
case self::OPT_FSTR:
|
|
||||||
$len += 8; /* length of "<string>" */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ($len > $max) {
|
|
||||||
$max = $len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach (self::$_options as $label => $info) {
|
|
||||||
switch ($info['type']) {
|
|
||||||
case self::OPT_FLAG:
|
|
||||||
case self::OPT_FFLAG:
|
|
||||||
echo " -$label";
|
|
||||||
echo str_repeat(' ', $max - strlen($label));
|
|
||||||
echo " $info[message]\n";
|
|
||||||
break;
|
|
||||||
case self::OPT_INT:
|
|
||||||
case self::OPT_FINT:
|
|
||||||
echo " $label=<integer>" . str_repeat(' ', $max - strlen($label) - 9);
|
|
||||||
echo " $info[message]\n";
|
|
||||||
break;
|
|
||||||
case self::OPT_DBL:
|
|
||||||
case self::OPT_FDBL:
|
|
||||||
echo " $label=<real>" . str_repeat(' ', $max - strlen($label) - 6);
|
|
||||||
echo " $info[message]\n";
|
|
||||||
break;
|
|
||||||
case self::OPT_STR:
|
|
||||||
case self::OPT_FSTR:
|
|
||||||
echo " $label=<string>" . str_repeat(' ', $max - strlen($label) - 8);
|
|
||||||
echo " $info[message]\n";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This routine is called with the argument to each -D command-line option.
|
|
||||||
* Add the macro defined to the azDefine array.
|
|
||||||
*
|
|
||||||
* @param string $z
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function _handleDOption($z)
|
|
||||||
{
|
|
||||||
if ($a = strstr($z, '=')) {
|
|
||||||
$z = substr($a, 1); // strip first =
|
|
||||||
}
|
|
||||||
$this->azDefine[] = $z;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************** From the file "main.c" ************************************/
|
|
||||||
/*
|
|
||||||
** Main program file for the LEMON parser generator.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main program. Parse the command line and do it...
|
|
||||||
*
|
|
||||||
* @return int Number of error and conflicts
|
|
||||||
*/
|
|
||||||
function main()
|
|
||||||
{
|
|
||||||
$lem = new PHP_ParserGenerator_Data;
|
|
||||||
|
|
||||||
$this->OptInit($_SERVER['argv']);
|
|
||||||
if ($this->_version) {
|
|
||||||
echo "Lemon version 1.0/PHP_ParserGenerator port version @package_version@\n";
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
if ($this->OptNArgs($_SERVER['argv']) != 1) {
|
|
||||||
echo "Exactly one filename argument is required.\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
$lem->errorcnt = 0;
|
|
||||||
$lem->parser_template = $this->_parser_template;
|
|
||||||
|
|
||||||
/* Initialize the machine */
|
|
||||||
$lem->argv0 = $_SERVER['argv'][0];
|
|
||||||
$lem->filename = $this->_optArg(0, $_SERVER['argv']);
|
|
||||||
$a = pathinfo($lem->filename);
|
|
||||||
if (isset($a['extension'])) {
|
|
||||||
$ext = '.' . $a['extension'];
|
|
||||||
$lem->filenosuffix = substr($lem->filename, 0, strlen($lem->filename) - strlen($ext));
|
|
||||||
} else {
|
|
||||||
$lem->filenosuffix = $lem->filename;
|
|
||||||
}
|
|
||||||
$lem->basisflag = $this->_basisflag;
|
|
||||||
$lem->has_fallback = 0;
|
|
||||||
$lem->nconflict = 0;
|
|
||||||
$lem->name = $lem->include_code = $lem->include_classcode = $lem->arg = $lem->tokentype = $lem->start = 0;
|
|
||||||
$lem->vartype = 0;
|
|
||||||
$lem->stacksize = 0;
|
|
||||||
$lem->error = $lem->overflow = $lem->failure = $lem->accept = $lem->tokendest = $lem->tokenprefix = $lem->outname = $lem->extracode = 0;
|
|
||||||
$lem->vardest = 0;
|
|
||||||
$lem->tablesize = 0;
|
|
||||||
PHP_ParserGenerator_Symbol::Symbol_new("$");
|
|
||||||
$lem->errsym = PHP_ParserGenerator_Symbol::Symbol_new("error");
|
|
||||||
|
|
||||||
/* Parse the input file */
|
|
||||||
$parser = new PHP_ParserGenerator_Parser($this);
|
|
||||||
$parser->Parse($lem);
|
|
||||||
if ($lem->errorcnt) {
|
|
||||||
exit($lem->errorcnt);
|
|
||||||
}
|
|
||||||
if ($lem->rule === 0) {
|
|
||||||
printf("Empty grammar.\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Count and index the symbols of the grammar */
|
|
||||||
$lem->nsymbol = PHP_ParserGenerator_Symbol::Symbol_count();
|
|
||||||
PHP_ParserGenerator_Symbol::Symbol_new("{default}");
|
|
||||||
$lem->symbols = PHP_ParserGenerator_Symbol::Symbol_arrayof();
|
|
||||||
for ($i = 0; $i <= $lem->nsymbol; $i++) {
|
|
||||||
$lem->symbols[$i]->index = $i;
|
|
||||||
}
|
|
||||||
usort($lem->symbols, array('PHP_ParserGenerator_Symbol', 'sortSymbols'));
|
|
||||||
for ($i = 0; $i <= $lem->nsymbol; $i++) {
|
|
||||||
$lem->symbols[$i]->index = $i;
|
|
||||||
}
|
|
||||||
// find the first lower-case symbol
|
|
||||||
for ($i = 1; ord($lem->symbols[$i]->name[0]) <= ord('Z'); $i++);
|
|
||||||
$lem->nterminal = $i;
|
|
||||||
|
|
||||||
/* Generate a reprint of the grammar, if requested on the command line */
|
|
||||||
if ($this->_rpflag) {
|
|
||||||
$this->Reprint();
|
|
||||||
} else {
|
|
||||||
/* Initialize the size for all follow and first sets */
|
|
||||||
$this->SetSize($lem->nterminal);
|
|
||||||
|
|
||||||
/* Find the precedence for every production rule (that has one) */
|
|
||||||
$lem->FindRulePrecedences();
|
|
||||||
|
|
||||||
/* Compute the lambda-nonterminals and the first-sets for every
|
|
||||||
** nonterminal */
|
|
||||||
$lem->FindFirstSets();
|
|
||||||
|
|
||||||
/* Compute all LR(0) states. Also record follow-set propagation
|
|
||||||
** links so that the follow-set can be computed later */
|
|
||||||
$lem->nstate = 0;
|
|
||||||
$lem->FindStates();
|
|
||||||
$lem->sorted = PHP_ParserGenerator_State::State_arrayof();
|
|
||||||
|
|
||||||
/* Tie up loose ends on the propagation links */
|
|
||||||
$lem->FindLinks();
|
|
||||||
|
|
||||||
/* Compute the follow set of every reducible configuration */
|
|
||||||
$lem->FindFollowSets();
|
|
||||||
|
|
||||||
/* Compute the action tables */
|
|
||||||
$lem->FindActions();
|
|
||||||
|
|
||||||
/* Compress the action tables */
|
|
||||||
if ($this->_compress===0) {
|
|
||||||
$lem->CompressTables();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reorder and renumber the states so that states with fewer choices
|
|
||||||
** occur at the end. */
|
|
||||||
$lem->ResortStates();
|
|
||||||
|
|
||||||
/* Generate a report of the parser generated. (the "y.output" file) */
|
|
||||||
if (!$this->_quiet) {
|
|
||||||
$lem->ReportOutput();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Generate the source code for the parser */
|
|
||||||
$lem->ReportTable($this->_mhflag);
|
|
||||||
|
|
||||||
/* Produce a header file for use by the scanner. (This step is
|
|
||||||
** omitted if the "-m" option is used because makeheaders will
|
|
||||||
** generate the file for us.) */
|
|
||||||
//if (!$this->_mhflag) {
|
|
||||||
// $this->ReportHeader();
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
if ($this->_statistics) {
|
|
||||||
printf(
|
|
||||||
"Parser statistics: %d terminals, %d nonterminals, %d rules\n",
|
|
||||||
$lem->nterminal,
|
|
||||||
$lem->nsymbol - $lem->nterminal,
|
|
||||||
$lem->nrule
|
|
||||||
);
|
|
||||||
printf(
|
|
||||||
" %d states, %d parser table entries, %d conflicts\n",
|
|
||||||
$lem->nstate,
|
|
||||||
$lem->tablesize,
|
|
||||||
$lem->nconflict
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if ($lem->nconflict) {
|
|
||||||
printf("%d parsing conflicts.\n", $lem->nconflict);
|
|
||||||
}
|
|
||||||
exit($lem->errorcnt + $lem->nconflict);
|
|
||||||
return ($lem->errorcnt + $lem->nconflict);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SetSize
|
|
||||||
*
|
|
||||||
* @param int $n
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
function SetSize($n)
|
|
||||||
{
|
|
||||||
$this->_size = $n + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Merge in a merge sort for a linked list
|
|
||||||
*
|
|
||||||
* Side effects:
|
|
||||||
* The "next" pointers for elements in the lists a and b are
|
|
||||||
* changed.
|
|
||||||
*
|
|
||||||
* @param mixed $a A sorted, null-terminated linked list. (May be null).
|
|
||||||
* @param mixed $b A sorted, null-terminated linked list. (May be null).
|
|
||||||
* @param function $cmp A pointer to the comparison function.
|
|
||||||
* @param integer $offset Offset in the structure to the "next" field.
|
|
||||||
*
|
|
||||||
* @return mixed A pointer to the head of a sorted list containing the
|
|
||||||
* elements of both a and b.
|
|
||||||
*/
|
|
||||||
static function merge($a, $b, $cmp, $offset)
|
|
||||||
{
|
|
||||||
if ($a === 0) {
|
|
||||||
$head = $b;
|
|
||||||
} elseif ($b === 0) {
|
|
||||||
$head = $a;
|
|
||||||
} else {
|
|
||||||
if (call_user_func($cmp, $a, $b) < 0) {
|
|
||||||
$ptr = $a;
|
|
||||||
$a = $a->$offset;
|
|
||||||
} else {
|
|
||||||
$ptr = $b;
|
|
||||||
$b = $b->$offset;
|
|
||||||
}
|
|
||||||
$head = $ptr;
|
|
||||||
while ($a && $b) {
|
|
||||||
if (call_user_func($cmp, $a, $b) < 0) {
|
|
||||||
$ptr->$offset = $a;
|
|
||||||
$ptr = $a;
|
|
||||||
$a = $a->$offset;
|
|
||||||
} else {
|
|
||||||
$ptr->$offset = $b;
|
|
||||||
$ptr = $b;
|
|
||||||
$b = $b->$offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($a !== 0) {
|
|
||||||
$ptr->$offset = $a;
|
|
||||||
} else {
|
|
||||||
$ptr->$offset = $b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $head;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LISTSIZE 30
|
|
||||||
/**
|
|
||||||
* Side effects:
|
|
||||||
* The "next" pointers for elements in list are changed.
|
|
||||||
*
|
|
||||||
* @param mixed $list Pointer to a singly-linked list of structures.
|
|
||||||
* @param mixed $next Pointer to pointer to the second element of the list.
|
|
||||||
* @param function $cmp A comparison function.
|
|
||||||
*
|
|
||||||
* @return mixed A pointer to the head of a sorted list containing the
|
|
||||||
* elements orginally in list.
|
|
||||||
*/
|
|
||||||
static function msort($list, $next, $cmp)
|
|
||||||
{
|
|
||||||
if ($list === 0) {
|
|
||||||
return $list;
|
|
||||||
}
|
|
||||||
if ($list->$next === 0) {
|
|
||||||
return $list;
|
|
||||||
}
|
|
||||||
$set = array_fill(0, 30, 0);
|
|
||||||
while ($list) {
|
|
||||||
$ep = $list;
|
|
||||||
$list = $list->$next;
|
|
||||||
$ep->$next = 0;
|
|
||||||
for ($i = 0; $i < 29 && $set[$i] !== 0; $i++) {
|
|
||||||
$ep = self::merge($ep, $set[$i], $cmp, $next);
|
|
||||||
$set[$i] = 0;
|
|
||||||
}
|
|
||||||
$set[$i] = $ep;
|
|
||||||
}
|
|
||||||
$ep = 0;
|
|
||||||
for ($i = 0; $i < 30; $i++) {
|
|
||||||
if ($set[$i] !== 0) {
|
|
||||||
$ep = self::merge($ep, $set[$i], $cmp, $next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $ep;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find a good place to break "msg" so that its length is at least "min"
|
|
||||||
** but no more than "max". Make the point as close to max as possible.
|
|
||||||
*/
|
|
||||||
static function findbreak($msg, $min, $max)
|
|
||||||
{
|
|
||||||
if ($min >= strlen($msg)) {
|
|
||||||
return strlen($msg);
|
|
||||||
}
|
|
||||||
for ($i = $spot = $min; $i <= $max && $i < strlen($msg); $i++) {
|
|
||||||
$c = $msg[$i];
|
|
||||||
if ($c == '-' && $i < $max - 1) {
|
|
||||||
$spot = $i + 1;
|
|
||||||
}
|
|
||||||
if ($c == ' ') {
|
|
||||||
$spot = $i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $spot;
|
|
||||||
}
|
|
||||||
|
|
||||||
static function ErrorMsg($filename, $lineno, $format)
|
|
||||||
{
|
|
||||||
/* Prepare a prefix to be prepended to every output line */
|
|
||||||
if ($lineno > 0) {
|
|
||||||
$prefix = sprintf("%20s:%d: ", $filename, $lineno);
|
|
||||||
} else {
|
|
||||||
$prefix = sprintf("%20s: ", $filename);
|
|
||||||
}
|
|
||||||
$prefixsize = strlen($prefix);
|
|
||||||
$availablewidth = 79 - $prefixsize;
|
|
||||||
|
|
||||||
/* Generate the error message */
|
|
||||||
$ap = func_get_args();
|
|
||||||
array_shift($ap); // $filename
|
|
||||||
array_shift($ap); // $lineno
|
|
||||||
array_shift($ap); // $format
|
|
||||||
$errmsg = vsprintf($format, $ap);
|
|
||||||
$linewidth = strlen($errmsg);
|
|
||||||
/* Remove trailing "\n"s from the error message. */
|
|
||||||
while ($linewidth > 0
|
|
||||||
&& in_array($errmsg[$linewidth-1], array("\n", "\r"), true)
|
|
||||||
) {
|
|
||||||
--$linewidth;
|
|
||||||
$errmsg = substr($errmsg, 0, strlen($errmsg) - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Print the error message */
|
|
||||||
$base = 0;
|
|
||||||
$errmsg = str_replace(
|
|
||||||
array("\r", "\n", "\t"),
|
|
||||||
array(' ', ' ', ' '),
|
|
||||||
$errmsg
|
|
||||||
);
|
|
||||||
while (strlen($errmsg)) {
|
|
||||||
$end = $restart = self::findbreak($errmsg, 0, $availablewidth);
|
|
||||||
if (strlen($errmsg) <= 79 && $end < strlen($errmsg) && $end <= 79) {
|
|
||||||
$end = $restart = strlen($errmsg);
|
|
||||||
}
|
|
||||||
while (isset($errmsg[$restart]) && $errmsg[$restart] == ' ') {
|
|
||||||
$restart++;
|
|
||||||
}
|
|
||||||
printf("%s%.${end}s\n", $prefix, $errmsg);
|
|
||||||
$errmsg = substr($errmsg, $restart);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Duplicate the input file without comments and without actions
|
|
||||||
* on rules
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
function Reprint()
|
|
||||||
{
|
|
||||||
printf("// Reprint of input file \"%s\".\n// Symbols:\n", $this->filename);
|
|
||||||
$maxlen = 10;
|
|
||||||
for ($i = 0; $i < $this->nsymbol; $i++) {
|
|
||||||
$sp = $this->symbols[$i];
|
|
||||||
$len = strlen($sp->name);
|
|
||||||
if ($len > $maxlen ) {
|
|
||||||
$maxlen = $len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$ncolumns = 76 / ($maxlen + 5);
|
|
||||||
if ($ncolumns < 1) {
|
|
||||||
$ncolumns = 1;
|
|
||||||
}
|
|
||||||
$skip = ($this->nsymbol + $ncolumns - 1) / $ncolumns;
|
|
||||||
for ($i = 0; $i < $skip; $i++) {
|
|
||||||
print "//";
|
|
||||||
for ($j = $i; $j < $this->nsymbol; $j += $skip) {
|
|
||||||
$sp = $this->symbols[$j];
|
|
||||||
//assert( sp->index==j );
|
|
||||||
printf(" %3d %-${maxlen}.${maxlen}s", $j, $sp->name);
|
|
||||||
}
|
|
||||||
print "\n";
|
|
||||||
}
|
|
||||||
for ($rp = $this->rule; $rp; $rp = $rp->next) {
|
|
||||||
printf("%s", $rp->lhs->name);
|
|
||||||
/*if ($rp->lhsalias) {
|
|
||||||
printf("(%s)", $rp->lhsalias);
|
|
||||||
}*/
|
|
||||||
print " ::=";
|
|
||||||
for ($i = 0; $i < $rp->nrhs; $i++) {
|
|
||||||
$sp = $rp->rhs[$i];
|
|
||||||
printf(" %s", $sp->name);
|
|
||||||
if ($sp->type == PHP_ParserGenerator_Symbol::MULTITERMINAL) {
|
|
||||||
for ($j = 1; $j < $sp->nsubsym; $j++) {
|
|
||||||
printf("|%s", $sp->subsym[$j]->name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*if ($rp->rhsalias[$i]) {
|
|
||||||
printf("(%s)", $rp->rhsalias[$i]);
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
print ".";
|
|
||||||
if ($rp->precsym) {
|
|
||||||
printf(" [%s]", $rp->precsym->name);
|
|
||||||
}
|
|
||||||
/*if ($rp->code) {
|
|
||||||
print "\n " . $rp->code);
|
|
||||||
}*/
|
|
||||||
print "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,257 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* PHP_ParserGenerator, a php 5 parser generator.
|
|
||||||
*
|
|
||||||
* This is a direct port of the Lemon parser generator, found at
|
|
||||||
* {@link http://www.hwaci.com/sw/lemon/}
|
|
||||||
*
|
|
||||||
* PHP version 5
|
|
||||||
*
|
|
||||||
* LICENSE:
|
|
||||||
*
|
|
||||||
* Copyright (c) 2006, Gregory Beaver <cellog@php.net>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in
|
|
||||||
* the documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the PHP_ParserGenerator nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived
|
|
||||||
* from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
||||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
||||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version CVS: $Id: Action.php 302382 2010-08-17 06:08:09Z jespino $
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since File available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Every shift or reduce operation is stored as one of the following objects.
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version Release: @package_version@
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since Class available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
class PHP_ParserGenerator_Action
|
|
||||||
{
|
|
||||||
const SHIFT = 1,
|
|
||||||
ACCEPT = 2,
|
|
||||||
REDUCE = 3,
|
|
||||||
ERROR = 4,
|
|
||||||
/**
|
|
||||||
* Was a reduce, but part of a conflict
|
|
||||||
*/
|
|
||||||
CONFLICT = 5,
|
|
||||||
/**
|
|
||||||
* Was a shift. Precedence resolved conflict
|
|
||||||
*/
|
|
||||||
SH_RESOLVED = 6,
|
|
||||||
/**
|
|
||||||
* Was a reduce. Precedence resolved conflict
|
|
||||||
*/
|
|
||||||
RD_RESOLVED = 7,
|
|
||||||
/**
|
|
||||||
* Deleted by compression
|
|
||||||
* @see PHP_ParserGenerator::CompressTables()
|
|
||||||
*/
|
|
||||||
NOT_USED = 8;
|
|
||||||
/**
|
|
||||||
* The look-ahead symbol that triggers this action
|
|
||||||
* @var PHP_ParserGenerator_Symbol
|
|
||||||
*/
|
|
||||||
public $sp; /* The look-ahead symbol */
|
|
||||||
/**
|
|
||||||
* This defines the kind of action, and must be one
|
|
||||||
* of the class constants.
|
|
||||||
*
|
|
||||||
* - {@link PHP_ParserGenerator_Action::SHIFT}
|
|
||||||
* - {@link PHP_ParserGenerator_Action::ACCEPT}
|
|
||||||
* - {@link PHP_ParserGenerator_Action::REDUCE}
|
|
||||||
* - {@link PHP_ParserGenerator_Action::ERROR}
|
|
||||||
* - {@link PHP_ParserGenerator_Action::CONFLICT}
|
|
||||||
* - {@link PHP_ParserGenerator_Action::SH_RESOLVED}
|
|
||||||
* - {@link PHP_ParserGenerator_Action:: RD_RESOLVED}
|
|
||||||
* - {@link PHP_ParserGenerator_Action::NOT_USED}
|
|
||||||
*/
|
|
||||||
public $type;
|
|
||||||
/**
|
|
||||||
* The new state, if this is a shift,
|
|
||||||
* the parser rule index, if this is a reduce.
|
|
||||||
*
|
|
||||||
* @var PHP_ParserGenerator_State|PHP_ParserGenerator_Rule
|
|
||||||
*/
|
|
||||||
public $x;
|
|
||||||
/**
|
|
||||||
* The next action for this state.
|
|
||||||
* @var PHP_ParserGenerator_Action
|
|
||||||
*/
|
|
||||||
public $next;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare two actions
|
|
||||||
*
|
|
||||||
* This is used by {@link Action_sort()} to compare actions
|
|
||||||
*/
|
|
||||||
static function actioncmp(PHP_ParserGenerator_Action $ap1, PHP_ParserGenerator_Action $ap2)
|
|
||||||
{
|
|
||||||
$rc = $ap1->sp->index - $ap2->sp->index;
|
|
||||||
if ($rc === 0) {
|
|
||||||
$rc = $ap1->type - $ap2->type;
|
|
||||||
}
|
|
||||||
if ($rc === 0) {
|
|
||||||
if ($ap1->type == self::SHIFT) {
|
|
||||||
if ($ap1->x->statenum != $ap2->x->statenum) {
|
|
||||||
throw new Exception('Shift conflict: ' . $ap1->sp->name .
|
|
||||||
' shifts both to state ' . $ap1->x->statenum . ' (rule ' .
|
|
||||||
$ap1->x->cfp->rp->lhs->name . ' on line ' .
|
|
||||||
$ap1->x->cfp->rp->ruleline . ') and to state ' .
|
|
||||||
$ap2->x->statenum . ' (rule ' .
|
|
||||||
$ap2->x->cfp->rp->lhs->name . ' on line ' .
|
|
||||||
$ap2->x->cfp->rp->ruleline . ')');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($ap1->type != self::REDUCE
|
|
||||||
&& $ap1->type != self::RD_RESOLVED
|
|
||||||
&& $ap1->type != self::CONFLICT
|
|
||||||
) {
|
|
||||||
throw new Exception('action has not been processed: ' .
|
|
||||||
$ap1->sp->name . ' on line ' . $ap1->x->cfp->rp->ruleline .
|
|
||||||
', rule ' . $ap1->x->cfp->rp->lhs->name);
|
|
||||||
}
|
|
||||||
if ($ap2->type != self::REDUCE
|
|
||||||
&& $ap2->type != self::RD_RESOLVED
|
|
||||||
&& $ap2->type != self::CONFLICT
|
|
||||||
) {
|
|
||||||
throw new Exception('action has not been processed: ' .
|
|
||||||
$ap2->sp->name . ' on line ' . $ap2->x->cfp->rp->ruleline .
|
|
||||||
', rule ' . $ap2->x->cfp->rp->lhs->name);
|
|
||||||
}
|
|
||||||
$rc = $ap1->x->index - $ap2->x->index;
|
|
||||||
}
|
|
||||||
return $rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
function display($processed = false)
|
|
||||||
{
|
|
||||||
$map = array(
|
|
||||||
self::ACCEPT => 'ACCEPT',
|
|
||||||
self::CONFLICT => 'CONFLICT',
|
|
||||||
self::REDUCE => 'REDUCE',
|
|
||||||
self::SHIFT => 'SHIFT'
|
|
||||||
);
|
|
||||||
echo $map[$this->type] . ' for ' . $this->sp->name;
|
|
||||||
if ($this->type == self::REDUCE) {
|
|
||||||
echo ' - rule ' . $this->x->lhs->name . "\n";
|
|
||||||
} elseif ($this->type == self::SHIFT) {
|
|
||||||
echo ' - state ' . $this->x->statenum . ', basis ' . $this->x->cfp->rp->lhs->name . "\n";
|
|
||||||
} else {
|
|
||||||
echo "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create linked list of PHP_ParserGenerator_Actions
|
|
||||||
*
|
|
||||||
* @param PHP_ParserGenerator_Action|null $app
|
|
||||||
* @param int $type one of the class constants from PHP_ParserGenerator_Action
|
|
||||||
* @param PHP_ParserGenerator_Symbol $sp
|
|
||||||
* @param PHP_ParserGenerator_State|PHP_ParserGenerator_Rule $arg
|
|
||||||
*/
|
|
||||||
static function Action_add(&$app, $type, PHP_ParserGenerator_Symbol $sp, $arg)
|
|
||||||
{
|
|
||||||
$new = new PHP_ParserGenerator_Action;
|
|
||||||
$new->next = $app;
|
|
||||||
$app = $new;
|
|
||||||
$new->type = $type;
|
|
||||||
$new->sp = $sp;
|
|
||||||
$new->x = $arg;
|
|
||||||
echo ' Adding ';
|
|
||||||
$new->display();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sort parser actions
|
|
||||||
*
|
|
||||||
* @param PHP_ParserGenerator_Action $ap a parser action
|
|
||||||
*
|
|
||||||
* @see PHP_ParserGenerator_Data::FindActions()
|
|
||||||
*
|
|
||||||
* @return PHP_ParserGenerator_Action
|
|
||||||
*/
|
|
||||||
static function Action_sort(PHP_ParserGenerator_Action $ap)
|
|
||||||
{
|
|
||||||
$ap = PHP_ParserGenerator::msort($ap, 'next', array('PHP_ParserGenerator_Action', 'actioncmp'));
|
|
||||||
return $ap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Print an action to the given file descriptor. Return FALSE if
|
|
||||||
* nothing was actually printed.
|
|
||||||
*
|
|
||||||
* @param resource $fp File descriptor to print on
|
|
||||||
* @param integer $indent Number of indents
|
|
||||||
*
|
|
||||||
* @see PHP_ParserGenerator_Data::ReportOutput()
|
|
||||||
*
|
|
||||||
* @return int|false
|
|
||||||
*/
|
|
||||||
function PrintAction($fp, $indent)
|
|
||||||
{
|
|
||||||
if (!$fp) {
|
|
||||||
$fp = STDOUT;
|
|
||||||
}
|
|
||||||
$result = 1;
|
|
||||||
switch ($this->type)
|
|
||||||
{
|
|
||||||
case self::SHIFT:
|
|
||||||
fprintf($fp, "%${indent}s shift %d", $this->sp->name, $this->x->statenum);
|
|
||||||
break;
|
|
||||||
case self::REDUCE:
|
|
||||||
fprintf($fp, "%${indent}s reduce %d", $this->sp->name, $this->x->index);
|
|
||||||
break;
|
|
||||||
case self::ACCEPT:
|
|
||||||
fprintf($fp, "%${indent}s accept", $this->sp->name);
|
|
||||||
break;
|
|
||||||
case self::ERROR:
|
|
||||||
fprintf($fp, "%${indent}s error", $this->sp->name);
|
|
||||||
break;
|
|
||||||
case self::CONFLICT:
|
|
||||||
fprintf($fp, "%${indent}s reduce %-3d ** Parsing conflict **", $this->sp->name, $this->x->index);
|
|
||||||
break;
|
|
||||||
case self::SH_RESOLVED:
|
|
||||||
case self::RD_RESOLVED:
|
|
||||||
case self::NOT_USED:
|
|
||||||
$result = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@@ -1,299 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* PHP_ParserGenerator, a php 5 parser generator.
|
|
||||||
*
|
|
||||||
* This is a direct port of the Lemon parser generator, found at
|
|
||||||
* {@link http://www.hwaci.com/sw/lemon/}
|
|
||||||
*
|
|
||||||
* PHP version 5
|
|
||||||
*
|
|
||||||
* LICENSE:
|
|
||||||
*
|
|
||||||
* Copyright (c) 2006, Gregory Beaver <cellog@php.net>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in
|
|
||||||
* the documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the PHP_ParserGenerator nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived
|
|
||||||
* from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
||||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
||||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version CVS: $Id: ActionTable.php 302382 2010-08-17 06:08:09Z jespino $
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since File available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* The state of the yy_action table under construction is an instance of
|
|
||||||
* the following structure
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version Release: @package_version@
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since Class available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
class PHP_ParserGenerator_ActionTable
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Number of used slots in {@link $aAction}
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $nAction = 0;
|
|
||||||
/**
|
|
||||||
* The $yy_action table under construction.
|
|
||||||
*
|
|
||||||
* Each entry is of format:
|
|
||||||
* <code>
|
|
||||||
* array(
|
|
||||||
* 'lookahead' => -1, // Value of the lookahead token (symbol index)
|
|
||||||
* 'action' => -1 // Action to take on the given lookahead (action index)
|
|
||||||
* );
|
|
||||||
* </code>
|
|
||||||
* @see PHP_ParserGenerator_Data::compute_action()
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $aAction = array(
|
|
||||||
array(
|
|
||||||
'lookahead' => -1,
|
|
||||||
'action' => -1
|
|
||||||
)
|
|
||||||
);
|
|
||||||
/**
|
|
||||||
* A single new transaction set.
|
|
||||||
*
|
|
||||||
* @see $aAction format of the internal array is described here
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $aLookahead = array(
|
|
||||||
array(
|
|
||||||
'lookahead' => 0,
|
|
||||||
'action' => 0
|
|
||||||
)
|
|
||||||
);
|
|
||||||
/**
|
|
||||||
* The smallest (minimum) value of any lookahead token in {@link $aLookahead}
|
|
||||||
*
|
|
||||||
* The lowest non-terminal is always introduced earlier in the parser file,
|
|
||||||
* and is therefore a more significant token.
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $mnLookahead = 0;
|
|
||||||
/**
|
|
||||||
* The action associated with the smallest lookahead token.
|
|
||||||
* @see $mnLookahead
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $mnAction = 0;
|
|
||||||
/**
|
|
||||||
* The largest (maximum) value of any lookahead token in {@link $aLookahead}
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $mxLookahead = 0;
|
|
||||||
/**
|
|
||||||
* The number of slots used in {@link $aLookahead}.
|
|
||||||
*
|
|
||||||
* This is the same as count($aLookahead), but there was no pressing reason
|
|
||||||
* to change this when porting from C.
|
|
||||||
* @see $mnLookahead
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $nLookahead = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a new action to the current transaction set
|
|
||||||
*
|
|
||||||
* @param int $lookahead
|
|
||||||
* @param int $action
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
function acttab_action($lookahead, $action)
|
|
||||||
{
|
|
||||||
if ($this->nLookahead === 0) {
|
|
||||||
$this->aLookahead = array();
|
|
||||||
$this->mxLookahead = $lookahead;
|
|
||||||
$this->mnLookahead = $lookahead;
|
|
||||||
$this->mnAction = $action;
|
|
||||||
} else {
|
|
||||||
if ($this->mxLookahead < $lookahead) {
|
|
||||||
$this->mxLookahead = $lookahead;
|
|
||||||
}
|
|
||||||
if ($this->mnLookahead > $lookahead) {
|
|
||||||
$this->mnLookahead = $lookahead;
|
|
||||||
$this->mnAction = $action;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->aLookahead[$this->nLookahead] = array(
|
|
||||||
'lookahead' => $lookahead,
|
|
||||||
'action' => $action);
|
|
||||||
$this->nLookahead++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the transaction set built up with prior calls to acttab_action()
|
|
||||||
* into the current action table. Then reset the transaction set back
|
|
||||||
* to an empty set in preparation for a new round of acttab_action() calls.
|
|
||||||
*
|
|
||||||
* Return the offset into the action table of the new transaction.
|
|
||||||
*
|
|
||||||
* @return int Return the offset that should be added to the lookahead in
|
|
||||||
* order to get the index into $yy_action of the action. This will be used
|
|
||||||
* in generation of $yy_ofst tables (reduce and shift)
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
function acttab_insert()
|
|
||||||
{
|
|
||||||
if ($this->nLookahead <= 0) {
|
|
||||||
throw new Exception('nLookahead is not set up?');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scan the existing action table looking for an offset where we can
|
|
||||||
** insert the current transaction set. Fall out of the loop when that
|
|
||||||
** offset is found. In the worst case, we fall out of the loop when
|
|
||||||
** i reaches $this->nAction, which means we append the new transaction set.
|
|
||||||
**
|
|
||||||
** i is the index in $this->aAction[] where $this->mnLookahead is inserted.
|
|
||||||
*/
|
|
||||||
for ($i = 0; $i < $this->nAction + $this->mnLookahead; $i++) {
|
|
||||||
if (!isset($this->aAction[$i])) {
|
|
||||||
$this->aAction[$i] = array(
|
|
||||||
'lookahead' => -1,
|
|
||||||
'action' => -1,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if ($this->aAction[$i]['lookahead'] < 0) {
|
|
||||||
for ($j = 0; $j < $this->nLookahead; $j++) {
|
|
||||||
if (!isset($this->aLookahead[$j])) {
|
|
||||||
$this->aLookahead[$j] = array(
|
|
||||||
'lookahead' => 0,
|
|
||||||
'action' => 0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$k = $this->aLookahead[$j]['lookahead'] -
|
|
||||||
$this->mnLookahead + $i;
|
|
||||||
if ($k < 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!isset($this->aAction[$k])) {
|
|
||||||
$this->aAction[$k] = array(
|
|
||||||
'lookahead' => -1,
|
|
||||||
'action' => -1,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if ($this->aAction[$k]['lookahead'] >= 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($j < $this->nLookahead ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for ($j = 0; $j < $this->nAction; $j++) {
|
|
||||||
if (!isset($this->aAction[$j])) {
|
|
||||||
$this->aAction[$j] = array(
|
|
||||||
'lookahead' => -1,
|
|
||||||
'action' => -1,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if ($this->aAction[$j]['lookahead'] == $j + $this->mnLookahead - $i) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($j == $this->nAction) {
|
|
||||||
break; /* Fits in empty slots */
|
|
||||||
}
|
|
||||||
} elseif ($this->aAction[$i]['lookahead'] == $this->mnLookahead) {
|
|
||||||
if ($this->aAction[$i]['action'] != $this->mnAction) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for ($j = 0; $j < $this->nLookahead; $j++) {
|
|
||||||
$k = $this->aLookahead[$j]['lookahead'] -
|
|
||||||
$this->mnLookahead + $i;
|
|
||||||
if ($k < 0 || $k >= $this->nAction) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!isset($this->aAction[$k])) {
|
|
||||||
$this->aAction[$k] = array(
|
|
||||||
'lookahead' => -1,
|
|
||||||
'action' => -1,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if ($this->aLookahead[$j]['lookahead'] != $this->aAction[$k]['lookahead']) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ($this->aLookahead[$j]['action'] != $this->aAction[$k]['action']) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($j < $this->nLookahead) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$n = 0;
|
|
||||||
for ($j = 0; $j < $this->nAction; $j++) {
|
|
||||||
if (!isset($this->aAction[$j])) {
|
|
||||||
$this->aAction[$j] = array(
|
|
||||||
'lookahead' => -1,
|
|
||||||
'action' => -1,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if ($this->aAction[$j]['lookahead'] < 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($this->aAction[$j]['lookahead'] == $j + $this->mnLookahead - $i) {
|
|
||||||
$n++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($n == $this->nLookahead) {
|
|
||||||
break; /* Same as a prior transaction set */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Insert transaction set at index i. */
|
|
||||||
for ($j = 0; $j < $this->nLookahead; $j++) {
|
|
||||||
if (!isset($this->aLookahead[$j])) {
|
|
||||||
$this->aLookahead[$j] = array(
|
|
||||||
'lookahead' => 0,
|
|
||||||
'action' => 0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$k = $this->aLookahead[$j]['lookahead'] - $this->mnLookahead + $i;
|
|
||||||
$this->aAction[$k] = $this->aLookahead[$j];
|
|
||||||
if ($k >= $this->nAction) {
|
|
||||||
$this->nAction = $k + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->nLookahead = 0;
|
|
||||||
$this->aLookahead = array();
|
|
||||||
|
|
||||||
/* Return the offset that is added to the lookahead in order to get the
|
|
||||||
** index into yy_action of the action */
|
|
||||||
return $i - $this->mnLookahead;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@@ -1,574 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* PHP_ParserGenerator, a php 5 parser generator.
|
|
||||||
*
|
|
||||||
* This is a direct port of the Lemon parser generator, found at
|
|
||||||
* {@link http://www.hwaci.com/sw/lemon/}
|
|
||||||
*
|
|
||||||
* PHP version 5
|
|
||||||
*
|
|
||||||
* LICENSE:
|
|
||||||
*
|
|
||||||
* Copyright (c) 2006, Gregory Beaver <cellog@php.net>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in
|
|
||||||
* the documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the PHP_ParserGenerator nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived
|
|
||||||
* from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
||||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
||||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version CVS: $Id: Config.php 302382 2010-08-17 06:08:09Z jespino $
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since File available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
/** A configuration is a production rule of the grammar together with
|
|
||||||
* a mark (dot) showing how much of that rule has been processed so far.
|
|
||||||
*
|
|
||||||
* Configurations also contain a follow-set which is a list of terminal
|
|
||||||
* symbols which are allowed to immediately follow the end of the rule.
|
|
||||||
* Every configuration is recorded as an instance of the following class.
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version Release: @package_version@
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since Class available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
class PHP_ParserGenerator_Config
|
|
||||||
{
|
|
||||||
const COMPLETE = 1;
|
|
||||||
const INCOMPLETE = 2;
|
|
||||||
/**
|
|
||||||
* The parser rule upon with the configuration is based.
|
|
||||||
*
|
|
||||||
* A parser rule is something like:
|
|
||||||
* <pre>
|
|
||||||
* blah ::= FOO bar.
|
|
||||||
* </pre>
|
|
||||||
* @var PHP_ParserGenerator_Rule
|
|
||||||
*/
|
|
||||||
public $rp;
|
|
||||||
/**
|
|
||||||
* The parse point.
|
|
||||||
*
|
|
||||||
* This is the index into the right-hand side of a rule that is
|
|
||||||
* represented by this configuration. In other words, possible
|
|
||||||
* dots for this rule:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* blah ::= FOO bar.
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* are (represented by "[here]"):
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* blah ::= [here] FOO bar.
|
|
||||||
* blah ::= FOO [here] bar.
|
|
||||||
* blah ::= FOO bar [here].
|
|
||||||
* </pre>
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $dot;
|
|
||||||
/**
|
|
||||||
* Follow-set for this configuration only
|
|
||||||
*
|
|
||||||
* This is the list of terminals and non-terminals that
|
|
||||||
* can follow this configuration.
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $fws;
|
|
||||||
/**
|
|
||||||
* Follow-set forward propagation links.
|
|
||||||
* @var PHP_ParserGenerator_PropagationLink
|
|
||||||
*/
|
|
||||||
public $fplp;
|
|
||||||
/**
|
|
||||||
* Follow-set backwards propagation links
|
|
||||||
* @var PHP_ParserGenerator_PropagationLink
|
|
||||||
*/
|
|
||||||
public $bplp;
|
|
||||||
/**
|
|
||||||
* State that contains this configuration
|
|
||||||
* @var PHP_ParserGenerator_State
|
|
||||||
*/
|
|
||||||
public $stp;
|
|
||||||
/* enum {
|
|
||||||
COMPLETE, /* The status is used during followset and
|
|
||||||
INCOMPLETE /* shift computations
|
|
||||||
} */
|
|
||||||
/**
|
|
||||||
* Status during followset and shift computations.
|
|
||||||
*
|
|
||||||
* One of PHP_ParserGenerator_Config::COMPLETE or
|
|
||||||
* PHP_ParserGenerator_Config::INCOMPLETE.
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $status;
|
|
||||||
/**
|
|
||||||
* Next configuration in the state.
|
|
||||||
*
|
|
||||||
* Index of next PHP_ParserGenerator_Config object.
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $next;
|
|
||||||
/**
|
|
||||||
* Index of the next basis configuration PHP_ParserGenerator_Config object
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $bp;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Top of the list of configurations for the current state.
|
|
||||||
* @var PHP_ParserGenerator_Config
|
|
||||||
*/
|
|
||||||
static public $current;
|
|
||||||
/**
|
|
||||||
* Last on the list of configurations for the current state.
|
|
||||||
* @var PHP_ParserGenerator_Config
|
|
||||||
*/
|
|
||||||
static public $currentend;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Top of the list of basis configurations for the current state.
|
|
||||||
* @var PHP_ParserGenerator_Config
|
|
||||||
*/
|
|
||||||
static public $basis;
|
|
||||||
/**
|
|
||||||
* Last on the list of basis configurations for the current state.
|
|
||||||
* @var PHP_ParserGenerator_Config
|
|
||||||
*/
|
|
||||||
static public $basisend;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Associative array representation of the linked list of configurations
|
|
||||||
* found in {@link $current}
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
static public $x4a = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a pointer to a new configuration
|
|
||||||
* @return PHP_ParserGenerator_Config
|
|
||||||
*/
|
|
||||||
private static function newconfig()
|
|
||||||
{
|
|
||||||
return new PHP_ParserGenerator_Config;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the current configuration for the .out file
|
|
||||||
*
|
|
||||||
* @param PHP_ParserGenerator_Config $cfp
|
|
||||||
* @see PHP_ParserGenerator_Data::ReportOutput()
|
|
||||||
*/
|
|
||||||
static function Configshow(PHP_ParserGenerator_Config $cfp)
|
|
||||||
{
|
|
||||||
$fp = fopen('php://output', 'w');
|
|
||||||
while ($cfp) {
|
|
||||||
if ($cfp->dot == $cfp->rp->nrhs) {
|
|
||||||
$buf = sprintf('(%d)', $cfp->rp->index);
|
|
||||||
fprintf($fp, ' %5s ', $buf);
|
|
||||||
} else {
|
|
||||||
fwrite($fp,' ');
|
|
||||||
}
|
|
||||||
$cfp->ConfigPrint($fp);
|
|
||||||
fwrite($fp, "\n");
|
|
||||||
if (0) {
|
|
||||||
//SetPrint(fp,cfp->fws,$this);
|
|
||||||
//PlinkPrint(fp,cfp->fplp,"To ");
|
|
||||||
//PlinkPrint(fp,cfp->bplp,"From");
|
|
||||||
}
|
|
||||||
$cfp = $cfp->next;
|
|
||||||
}
|
|
||||||
fwrite($fp, "\n");
|
|
||||||
fclose($fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the configuration list builder for a new state.
|
|
||||||
*/
|
|
||||||
static function Configlist_init()
|
|
||||||
{
|
|
||||||
self::$current = 0;
|
|
||||||
self::$currentend = &self::$current;
|
|
||||||
self::$basis = 0;
|
|
||||||
self::$basisend = &self::$basis;
|
|
||||||
self::$x4a = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all data from the table.
|
|
||||||
*
|
|
||||||
* Pass each data to the function $f as it is removed if
|
|
||||||
* $f is a valid callback.
|
|
||||||
* @param callback|null
|
|
||||||
* @see Configtable_clear()
|
|
||||||
*/
|
|
||||||
static function Configtable_reset($f)
|
|
||||||
{
|
|
||||||
self::$current = 0;
|
|
||||||
self::$currentend = &self::$current;
|
|
||||||
self::$basis = 0;
|
|
||||||
self::$basisend = &self::$basis;
|
|
||||||
self::Configtable_clear(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all data from the associative array representation
|
|
||||||
* of configurations.
|
|
||||||
*
|
|
||||||
* Pass each data to the function $f as it is removed if
|
|
||||||
* $f is a valid callback.
|
|
||||||
* @param callback|null
|
|
||||||
*/
|
|
||||||
static function Configtable_clear($f)
|
|
||||||
{
|
|
||||||
if (!count(self::$x4a)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ($f) {
|
|
||||||
for ($i = 0; $i < count(self::$x4a); $i++) {
|
|
||||||
call_user_func($f, self::$x4a[$i]->data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self::$x4a = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the configuration list builder for a new state.
|
|
||||||
* @see Configtable_clear()
|
|
||||||
*/
|
|
||||||
static function Configlist_reset()
|
|
||||||
{
|
|
||||||
self::Configtable_clear(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add another configuration to the configuration list for this parser state.
|
|
||||||
* @param PHP_ParserGenerator_Rule the rule
|
|
||||||
* @param int Index into the right-hand side of the rule where the dot goes
|
|
||||||
* @return PHP_ParserGenerator_Config
|
|
||||||
*/
|
|
||||||
static function Configlist_add($rp, $dot)
|
|
||||||
{
|
|
||||||
$model = new PHP_ParserGenerator_Config;
|
|
||||||
$model->rp = $rp;
|
|
||||||
$model->dot = $dot;
|
|
||||||
$cfp = self::Configtable_find($model);
|
|
||||||
if ($cfp === 0) {
|
|
||||||
$cfp = self::newconfig();
|
|
||||||
$cfp->rp = $rp;
|
|
||||||
$cfp->dot = $dot;
|
|
||||||
$cfp->fws = array();
|
|
||||||
$cfp->stp = 0;
|
|
||||||
$cfp->fplp = $cfp->bplp = 0;
|
|
||||||
$cfp->next = 0;
|
|
||||||
$cfp->bp = 0;
|
|
||||||
self::$currentend = $cfp;
|
|
||||||
self::$currentend = &$cfp->next;
|
|
||||||
self::Configtable_insert($cfp);
|
|
||||||
}
|
|
||||||
return $cfp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a basis configuration to the configuration list for this parser state.
|
|
||||||
*
|
|
||||||
* Basis configurations are the root for a configuration. This method also
|
|
||||||
* inserts the configuration into the regular list of configurations for this
|
|
||||||
* reason.
|
|
||||||
* @param PHP_ParserGenerator_Rule the rule
|
|
||||||
* @param int Index into the right-hand side of the rule where the dot goes
|
|
||||||
* @return PHP_ParserGenerator_Config
|
|
||||||
*/
|
|
||||||
static function Configlist_addbasis($rp, $dot)
|
|
||||||
{
|
|
||||||
$model = new PHP_ParserGenerator_Config;
|
|
||||||
$model->rp = $rp;
|
|
||||||
$model->dot = $dot;
|
|
||||||
$cfp = self::Configtable_find($model);
|
|
||||||
if ($cfp === 0) {
|
|
||||||
$cfp = self::newconfig();
|
|
||||||
$cfp->rp = $rp;
|
|
||||||
$cfp->dot = $dot;
|
|
||||||
$cfp->fws = array();
|
|
||||||
$cfp->stp = 0;
|
|
||||||
$cfp->fplp = $cfp->bplp = 0;
|
|
||||||
$cfp->next = 0;
|
|
||||||
$cfp->bp = 0;
|
|
||||||
self::$currentend = $cfp;
|
|
||||||
self::$currentend = &$cfp->next;
|
|
||||||
self::$basisend = $cfp;
|
|
||||||
self::$basisend = &$cfp->bp;
|
|
||||||
self::Configtable_insert($cfp);
|
|
||||||
}
|
|
||||||
return $cfp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute the closure of the configuration list.
|
|
||||||
*
|
|
||||||
* This calculates all of the possible continuations of
|
|
||||||
* each configuration, ensuring that each state accounts
|
|
||||||
* for every configuration that could arrive at that state.
|
|
||||||
*/
|
|
||||||
static function Configlist_closure(PHP_ParserGenerator_Data $lemp)
|
|
||||||
{
|
|
||||||
for ($cfp = self::$current; $cfp; $cfp = $cfp->next) {
|
|
||||||
$rp = $cfp->rp;
|
|
||||||
$dot = $cfp->dot;
|
|
||||||
if ($dot >= $rp->nrhs) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$sp = $rp->rhs[$dot];
|
|
||||||
if ($sp->type == PHP_ParserGenerator_Symbol::NONTERMINAL) {
|
|
||||||
if ($sp->rule === 0 && $sp !== $lemp->errsym) {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($lemp->filename, $rp->line,
|
|
||||||
"Nonterminal \"%s\" has no rules.", $sp->name);
|
|
||||||
$lemp->errorcnt++;
|
|
||||||
}
|
|
||||||
for ($newrp = $sp->rule; $newrp; $newrp = $newrp->nextlhs) {
|
|
||||||
$newcfp = self::Configlist_add($newrp, 0);
|
|
||||||
for ($i = $dot + 1; $i < $rp->nrhs; $i++) {
|
|
||||||
$xsp = $rp->rhs[$i];
|
|
||||||
if ($xsp->type == PHP_ParserGenerator_Symbol::TERMINAL) {
|
|
||||||
$newcfp->fws[$xsp->index] = 1;
|
|
||||||
break;
|
|
||||||
} elseif ($xsp->type == PHP_ParserGenerator_Symbol::MULTITERMINAL) {
|
|
||||||
for ($k = 0; $k < $xsp->nsubsym; $k++) {
|
|
||||||
$newcfp->fws[$xsp->subsym[$k]->index] = 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
$a = array_diff_key($xsp->firstset, $newcfp->fws);
|
|
||||||
$newcfp->fws += $a;
|
|
||||||
if ($xsp->lambda === false) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($i == $rp->nrhs) {
|
|
||||||
PHP_ParserGenerator_PropagationLink::Plink_add($cfp->fplp, $newcfp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sort the configuration list
|
|
||||||
* @uses Configcmp()
|
|
||||||
*/
|
|
||||||
static function Configlist_sort()
|
|
||||||
{
|
|
||||||
$a = 0;
|
|
||||||
//self::Configshow(self::$current);
|
|
||||||
self::$current = PHP_ParserGenerator::msort(self::$current,'next', array('PHP_ParserGenerator_Config', 'Configcmp'));
|
|
||||||
//self::Configshow(self::$current);
|
|
||||||
self::$currentend = &$a;
|
|
||||||
self::$currentend = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sort the configuration list
|
|
||||||
* @uses Configcmp
|
|
||||||
*/
|
|
||||||
static function Configlist_sortbasis()
|
|
||||||
{
|
|
||||||
$a = 0;
|
|
||||||
self::$basis = PHP_ParserGenerator::msort(self::$current,'bp', array('PHP_ParserGenerator_Config', 'Configcmp'));
|
|
||||||
self::$basisend = &$a;
|
|
||||||
self::$basisend = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a pointer to the head of the configuration list and
|
|
||||||
* reset the list
|
|
||||||
* @see $current
|
|
||||||
* @return PHP_ParserGenerator_Config
|
|
||||||
*/
|
|
||||||
static function Configlist_return()
|
|
||||||
{
|
|
||||||
$old = self::$current;
|
|
||||||
self::$current = 0;
|
|
||||||
self::$currentend = &self::$current;
|
|
||||||
return $old;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a pointer to the head of the basis list and
|
|
||||||
* reset the list
|
|
||||||
* @see $basis
|
|
||||||
* @return PHP_ParserGenerator_Config
|
|
||||||
*/
|
|
||||||
static function Configlist_basis()
|
|
||||||
{
|
|
||||||
$old = self::$basis;
|
|
||||||
self::$basis = 0;
|
|
||||||
self::$basisend = &self::$basis;
|
|
||||||
return $old;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free all elements of the given configuration list
|
|
||||||
* @param PHP_ParserGenerator_Config
|
|
||||||
*/
|
|
||||||
static function Configlist_eat($cfp)
|
|
||||||
{
|
|
||||||
for (; $cfp; $cfp = $nextcfp) {
|
|
||||||
$nextcfp = $cfp->next;
|
|
||||||
if ($cfp->fplp !=0) {
|
|
||||||
throw new Exception('fplp of configuration non-zero?');
|
|
||||||
}
|
|
||||||
if ($cfp->bplp !=0) {
|
|
||||||
throw new Exception('bplp of configuration non-zero?');
|
|
||||||
}
|
|
||||||
if ($cfp->fws) {
|
|
||||||
$cfp->fws = array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare two configurations for sorting purposes.
|
|
||||||
*
|
|
||||||
* Configurations based on higher precedence rules
|
|
||||||
* (those earlier in the file) are chosen first. Two
|
|
||||||
* configurations that are the same rule are sorted by
|
|
||||||
* dot (see {@link $dot}), and those configurations
|
|
||||||
* with a dot closer to the left-hand side are chosen first.
|
|
||||||
* @param unknown_type $a
|
|
||||||
* @param unknown_type $b
|
|
||||||
* @return unknown
|
|
||||||
*/
|
|
||||||
static function Configcmp($a, $b)
|
|
||||||
{
|
|
||||||
$x = $a->rp->index - $b->rp->index;
|
|
||||||
if (!$x) {
|
|
||||||
$x = $a->dot - $b->dot;
|
|
||||||
}
|
|
||||||
return $x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Print out information on this configuration.
|
|
||||||
*
|
|
||||||
* @param resource $fp
|
|
||||||
* @see PHP_ParserGenerator_Data::ReportOutput()
|
|
||||||
*/
|
|
||||||
function ConfigPrint($fp)
|
|
||||||
{
|
|
||||||
$rp = $this->rp;
|
|
||||||
fprintf($fp, "%s ::=", $rp->lhs->name);
|
|
||||||
for ($i = 0; $i <= $rp->nrhs; $i++) {
|
|
||||||
if ($i === $this->dot) {
|
|
||||||
fwrite($fp,' *');
|
|
||||||
}
|
|
||||||
if ($i === $rp->nrhs) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$sp = $rp->rhs[$i];
|
|
||||||
fprintf($fp,' %s', $sp->name);
|
|
||||||
if ($sp->type == PHP_ParserGenerator_Symbol::MULTITERMINAL) {
|
|
||||||
for ($j = 1; $j < $sp->nsubsym; $j++) {
|
|
||||||
fprintf($fp, '|%s', $sp->subsym[$j]->name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hash a configuration for the associative array {@link $x4a}
|
|
||||||
*/
|
|
||||||
private static function confighash(PHP_ParserGenerator_Config $a)
|
|
||||||
{
|
|
||||||
$h = 0;
|
|
||||||
$h = $h * 571 + $a->rp->index * 37 + $a->dot;
|
|
||||||
return $h;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert a new record into the array. Return TRUE if successful.
|
|
||||||
* Prior data with the same key is NOT overwritten
|
|
||||||
*/
|
|
||||||
static function Configtable_insert(PHP_ParserGenerator_Config $data)
|
|
||||||
{
|
|
||||||
$h = self::confighash($data);
|
|
||||||
if (isset(self::$x4a[$h])) {
|
|
||||||
$np = self::$x4a[$h];
|
|
||||||
} else {
|
|
||||||
$np = 0;
|
|
||||||
}
|
|
||||||
while ($np) {
|
|
||||||
if (self::Configcmp($np->data, $data) == 0) {
|
|
||||||
/* An existing entry with the same key is found. */
|
|
||||||
/* Fail because overwrite is not allows. */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
$np = $np->next;
|
|
||||||
}
|
|
||||||
/* Insert the new data */
|
|
||||||
$np = array('data' => $data, 'next' => 0, 'from' => 0);
|
|
||||||
$np = new PHP_ParserGenerator_StateNode;
|
|
||||||
$np->data = $data;
|
|
||||||
if (isset(self::$x4a[$h])) {
|
|
||||||
self::$x4a[$h]->from = $np->next;
|
|
||||||
$np->next = self::$x4a[$h];
|
|
||||||
}
|
|
||||||
$np->from = $np;
|
|
||||||
self::$x4a[$h] = $np;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a pointer to data assigned to the given key. Return NULL
|
|
||||||
* if no such key.
|
|
||||||
* @return PHP_ParserGenerator_Config|0
|
|
||||||
*/
|
|
||||||
static function Configtable_find(PHP_ParserGenerator_Config $key)
|
|
||||||
{
|
|
||||||
$h = self::confighash($key);
|
|
||||||
if (!isset(self::$x4a[$h])) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
$np = self::$x4a[$h];
|
|
||||||
while ($np) {
|
|
||||||
if (self::Configcmp($np->data, $key) == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$np = $np->next;
|
|
||||||
}
|
|
||||||
return $np ? $np->data : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,851 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* PHP_ParserGenerator, a php 5 parser generator.
|
|
||||||
*
|
|
||||||
* This is a direct port of the Lemon parser generator, found at
|
|
||||||
* {@link http://www.hwaci.com/sw/lemon/}
|
|
||||||
*
|
|
||||||
* PHP version 5
|
|
||||||
*
|
|
||||||
* LICENSE: This source file is subject to version 3.01 of the PHP license
|
|
||||||
* that is available through the world-wide-web at the following URI:
|
|
||||||
* http://www.php.net/license/3_01.txt. If you did not receive a copy of
|
|
||||||
* the PHP License and are unable to obtain it through the web, please
|
|
||||||
* send a note to license@php.net so we can mail you a copy immediately.
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.php.net/license/3_01.txt PHP License 3.01
|
|
||||||
* @version CVS: $Id: Parser.php 302382 2010-08-17 06:08:09Z jespino $
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since File available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* The grammar parser for lemon grammar files.
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.php.net/license/3_01.txt PHP License 3.01
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since Class available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
class PHP_ParserGenerator_Parser
|
|
||||||
{
|
|
||||||
const INITIALIZE = 1;
|
|
||||||
const WAITING_FOR_DECL_OR_RULE = 2;
|
|
||||||
const WAITING_FOR_DECL_KEYWORD = 3;
|
|
||||||
const WAITING_FOR_DECL_ARG = 4;
|
|
||||||
const WAITING_FOR_PRECEDENCE_SYMBOL = 5;
|
|
||||||
const WAITING_FOR_ARROW = 6;
|
|
||||||
const IN_RHS = 7;
|
|
||||||
const LHS_ALIAS_1 = 8;
|
|
||||||
const LHS_ALIAS_2 = 9;
|
|
||||||
const LHS_ALIAS_3 = 10;
|
|
||||||
const RHS_ALIAS_1 = 11;
|
|
||||||
const RHS_ALIAS_2 = 12;
|
|
||||||
const PRECEDENCE_MARK_1 = 13;
|
|
||||||
const PRECEDENCE_MARK_2 = 14;
|
|
||||||
const RESYNC_AFTER_RULE_ERROR = 15;
|
|
||||||
const RESYNC_AFTER_DECL_ERROR = 16;
|
|
||||||
const WAITING_FOR_DESTRUCTOR_SYMBOL = 17;
|
|
||||||
const WAITING_FOR_DATATYPE_SYMBOL = 18;
|
|
||||||
const WAITING_FOR_FALLBACK_ID = 19;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Name of the input file
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $filename;
|
|
||||||
/**
|
|
||||||
* Linenumber at which current token starts
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $tokenlineno;
|
|
||||||
/**
|
|
||||||
* Number of parsing errors so far
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $errorcnt;
|
|
||||||
/**
|
|
||||||
* Index of current token within the input string
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $tokenstart;
|
|
||||||
/**
|
|
||||||
* Global state vector
|
|
||||||
* @var PHP_ParserGenerator_Data
|
|
||||||
*/
|
|
||||||
public $gp;
|
|
||||||
/**
|
|
||||||
* Parser state (one of the class constants for this class)
|
|
||||||
*
|
|
||||||
* - PHP_ParserGenerator_Parser::INITIALIZE,
|
|
||||||
* - PHP_ParserGenerator_Parser::WAITING_FOR_DECL_OR_RULE,
|
|
||||||
* - PHP_ParserGenerator_Parser::WAITING_FOR_DECL_KEYWORD,
|
|
||||||
* - PHP_ParserGenerator_Parser::WAITING_FOR_DECL_ARG,
|
|
||||||
* - PHP_ParserGenerator_Parser::WAITING_FOR_PRECEDENCE_SYMBOL,
|
|
||||||
* - PHP_ParserGenerator_Parser::WAITING_FOR_ARROW,
|
|
||||||
* - PHP_ParserGenerator_Parser::IN_RHS,
|
|
||||||
* - PHP_ParserGenerator_Parser::LHS_ALIAS_1,
|
|
||||||
* - PHP_ParserGenerator_Parser::LHS_ALIAS_2,
|
|
||||||
* - PHP_ParserGenerator_Parser::LHS_ALIAS_3,
|
|
||||||
* - PHP_ParserGenerator_Parser::RHS_ALIAS_1,
|
|
||||||
* - PHP_ParserGenerator_Parser::RHS_ALIAS_2,
|
|
||||||
* - PHP_ParserGenerator_Parser::PRECEDENCE_MARK_1,
|
|
||||||
* - PHP_ParserGenerator_Parser::PRECEDENCE_MARK_2,
|
|
||||||
* - PHP_ParserGenerator_Parser::RESYNC_AFTER_RULE_ERROR,
|
|
||||||
* - PHP_ParserGenerator_Parser::RESYNC_AFTER_DECL_ERROR,
|
|
||||||
* - PHP_ParserGenerator_Parser::WAITING_FOR_DESTRUCTOR_SYMBOL,
|
|
||||||
* - PHP_ParserGenerator_Parser::WAITING_FOR_DATATYPE_SYMBOL,
|
|
||||||
* - PHP_ParserGenerator_Parser::WAITING_FOR_FALLBACK_ID
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $state;
|
|
||||||
/**
|
|
||||||
* The fallback token
|
|
||||||
* @var PHP_ParserGenerator_Symbol
|
|
||||||
*/
|
|
||||||
public $fallback;
|
|
||||||
/**
|
|
||||||
* Left-hand side of the current rule
|
|
||||||
* @var PHP_ParserGenerator_Symbol
|
|
||||||
*/
|
|
||||||
public $lhs;
|
|
||||||
/**
|
|
||||||
* Alias for the LHS
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $lhsalias;
|
|
||||||
/**
|
|
||||||
* Number of right-hand side symbols seen
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $nrhs;
|
|
||||||
/**
|
|
||||||
* Right-hand side symbols
|
|
||||||
* @var array array of {@link PHP_ParserGenerator_Symbol} objects
|
|
||||||
*/
|
|
||||||
public $rhs = array();
|
|
||||||
/**
|
|
||||||
* Aliases for each RHS symbol name (or NULL)
|
|
||||||
* @var array array of strings
|
|
||||||
*/
|
|
||||||
public $alias = array();
|
|
||||||
/**
|
|
||||||
* Previous rule parsed
|
|
||||||
* @var PHP_ParserGenerator_Rule
|
|
||||||
*/
|
|
||||||
public $prevrule;
|
|
||||||
/**
|
|
||||||
* Keyword of a declaration
|
|
||||||
*
|
|
||||||
* This is one of the %keyword keywords in the grammar file
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $declkeyword;
|
|
||||||
/**
|
|
||||||
* Where the declaration argument should be put
|
|
||||||
*
|
|
||||||
* This is assigned as a reference to an internal variable
|
|
||||||
* @var mixed
|
|
||||||
*/
|
|
||||||
public $declargslot = array();
|
|
||||||
/**
|
|
||||||
* Where the declaration linenumber is put
|
|
||||||
*
|
|
||||||
* This is assigned as a reference to an internal variable
|
|
||||||
* @var mixed
|
|
||||||
*/
|
|
||||||
public $decllnslot;
|
|
||||||
/*enum e_assoc*/
|
|
||||||
public $declassoc; /* Assign this association to decl arguments */
|
|
||||||
public $preccounter; /* Assign this precedence to decl arguments */
|
|
||||||
/**
|
|
||||||
* @var PHP_ParserGenerator_Rule
|
|
||||||
*/
|
|
||||||
public $firstrule; /* Pointer to first rule in the grammar */
|
|
||||||
/**
|
|
||||||
* @var PHP_ParserGenerator_Rule
|
|
||||||
*/
|
|
||||||
public $lastrule; /* Pointer to the most recently parsed rule */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var PHP_ParserGenerator
|
|
||||||
*/
|
|
||||||
private $lemon;
|
|
||||||
|
|
||||||
function __construct(PHP_ParserGenerator $lem)
|
|
||||||
{
|
|
||||||
$this->lemon = $lem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run the preprocessor over the input file text. The Lemon variable
|
|
||||||
* $azDefine contains the names of all defined
|
|
||||||
* macros. This routine looks for "%ifdef" and "%ifndef" and "%endif" and
|
|
||||||
* comments them out. Text in between is also commented out as appropriate.
|
|
||||||
* @param string
|
|
||||||
*/
|
|
||||||
private function preprocess_input(&$z)
|
|
||||||
{
|
|
||||||
$lineno = $exclude = 0;
|
|
||||||
for ($i=0; $i < strlen($z); $i++) {
|
|
||||||
if ($z[$i] == "\n") {
|
|
||||||
$lineno++;
|
|
||||||
}
|
|
||||||
if ($z[$i] != '%' || ($i > 0 && $z[$i-1] != "\n")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (substr($z, $i, 6) === "%endif" && trim($z[$i+6]) === '') {
|
|
||||||
if ($exclude) {
|
|
||||||
$exclude--;
|
|
||||||
if ($exclude === 0) {
|
|
||||||
for ($j = $start; $j < $i; $j++) {
|
|
||||||
if ($z[$j] != "\n") $z[$j] = ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for ($j = $i; $j < strlen($z) && $z[$j] != "\n"; $j++) {
|
|
||||||
$z[$j] = ' ';
|
|
||||||
}
|
|
||||||
} elseif (substr($z, $i, 6) === "%ifdef" && trim($z[$i+6]) === '' ||
|
|
||||||
substr($z, $i, 7) === "%ifndef" && trim($z[$i+7]) === '') {
|
|
||||||
if ($exclude) {
|
|
||||||
$exclude++;
|
|
||||||
} else {
|
|
||||||
$j = $i;
|
|
||||||
$n = strtok(substr($z, $j), " \t");
|
|
||||||
$exclude = 1;
|
|
||||||
if (isset($this->lemon->azDefine[$n])) {
|
|
||||||
$exclude = 0;
|
|
||||||
}
|
|
||||||
if ($z[$i + 3]=='n') {
|
|
||||||
// this is a rather obtuse way of checking whether this is %ifndef
|
|
||||||
$exclude = !$exclude;
|
|
||||||
}
|
|
||||||
if ($exclude) {
|
|
||||||
$start = $i;
|
|
||||||
$start_lineno = $lineno;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//for ($j = $i; $j < strlen($z) && $z[$j] != "\n"; $j++) $z[$j] = ' ';
|
|
||||||
$j = strpos(substr($z, $i), "\n");
|
|
||||||
if ($j === false) {
|
|
||||||
$z = substr($z, 0, $i); // remove instead of adding ' '
|
|
||||||
} else {
|
|
||||||
$z = substr($z, 0, $i) . substr($z, $i + $j); // remove instead of adding ' '
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($exclude) {
|
|
||||||
throw new Exception("unterminated %ifdef starting on line $start_lineno\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* In spite of its name, this function is really a scanner.
|
|
||||||
*
|
|
||||||
* It reads in the entire input file (all at once) then tokenizes it.
|
|
||||||
* Each token is passed to the function "parseonetoken" which builds all
|
|
||||||
* the appropriate data structures in the global state vector "gp".
|
|
||||||
* @param PHP_ParserGenerator_Data
|
|
||||||
*/
|
|
||||||
function Parse(PHP_ParserGenerator_Data $gp)
|
|
||||||
{
|
|
||||||
$startline = 0;
|
|
||||||
|
|
||||||
$this->gp = $gp;
|
|
||||||
$this->filename = $gp->filename;
|
|
||||||
$this->errorcnt = 0;
|
|
||||||
$this->state = self::INITIALIZE;
|
|
||||||
|
|
||||||
/* Begin by reading the input file */
|
|
||||||
$filebuf = file_get_contents($this->filename);
|
|
||||||
if (!$filebuf) {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, 0, "Can't open this file for reading.");
|
|
||||||
$gp->errorcnt++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (filesize($this->filename) != strlen($filebuf)) {
|
|
||||||
ErrorMsg($this->filename, 0, "Can't read in all %d bytes of this file.",
|
|
||||||
filesize($this->filename));
|
|
||||||
$gp->errorcnt++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make an initial pass through the file to handle %ifdef and %ifndef */
|
|
||||||
$this->preprocess_input($filebuf);
|
|
||||||
|
|
||||||
/* Now scan the text of the input file */
|
|
||||||
$lineno = 1;
|
|
||||||
for ($cp = 0, $c = $filebuf[0]; $cp < strlen($filebuf); $cp++) {
|
|
||||||
$c = $filebuf[$cp];
|
|
||||||
if ($c == "\n") $lineno++; /* Keep track of the line number */
|
|
||||||
if (trim($c) === '') {
|
|
||||||
continue;
|
|
||||||
} /* Skip all white space */
|
|
||||||
if ($filebuf[$cp] == '/' && ($cp + 1 < strlen($filebuf)) && $filebuf[$cp + 1] == '/') {
|
|
||||||
/* Skip C++ style comments */
|
|
||||||
$cp += 2;
|
|
||||||
$z = strpos(substr($filebuf, $cp), "\n");
|
|
||||||
if ($z === false) {
|
|
||||||
$cp = strlen($filebuf);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$lineno++;
|
|
||||||
$cp += $z;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($filebuf[$cp] == '/' && ($cp + 1 < strlen($filebuf)) && $filebuf[$cp + 1] == '*') {
|
|
||||||
/* Skip C style comments */
|
|
||||||
$cp += 2;
|
|
||||||
$z = strpos(substr($filebuf, $cp), '*/');
|
|
||||||
if ($z !== false) {
|
|
||||||
$lineno += count(explode("\n", substr($filebuf, $cp, $z))) - 1;
|
|
||||||
}
|
|
||||||
$cp += $z + 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$this->tokenstart = $cp; /* Mark the beginning of the token */
|
|
||||||
$this->tokenlineno = $lineno; /* Linenumber on which token begins */
|
|
||||||
if ($filebuf[$cp] == '"') { /* String literals */
|
|
||||||
$cp++;
|
|
||||||
$oldcp = $cp;
|
|
||||||
$test = strpos(substr($filebuf, $cp), '"');
|
|
||||||
if ($test === false) {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $startline,
|
|
||||||
"String starting on this line is not terminated before the end of the file.");
|
|
||||||
$this->errorcnt++;
|
|
||||||
$nextcp = $cp = strlen($filebuf);
|
|
||||||
} else {
|
|
||||||
$cp += $test;
|
|
||||||
$nextcp = $cp + 1;
|
|
||||||
}
|
|
||||||
$lineno += count(explode("\n", substr($filebuf, $oldcp, $cp - $oldcp))) - 1;
|
|
||||||
} elseif ($filebuf[$cp] == '{') { /* A block of C code */
|
|
||||||
$cp++;
|
|
||||||
if ($filebuf[$cp]=="}") {
|
|
||||||
$filebuf = substr($filebuf, 0, $cp)." ".substr($filebuf, $cp);
|
|
||||||
}
|
|
||||||
|
|
||||||
for ($level = 1; $cp < strlen($filebuf) && ($level > 1 || $filebuf[$cp] != '}'); $cp++) {
|
|
||||||
if ($filebuf[$cp] == "\n") {
|
|
||||||
$lineno++;
|
|
||||||
} elseif ($filebuf[$cp] == '{') {
|
|
||||||
$level++;
|
|
||||||
} elseif ($filebuf[$cp] == '}') {
|
|
||||||
$level--;
|
|
||||||
} elseif ($filebuf[$cp] == '/' && $filebuf[$cp + 1] == '*') {
|
|
||||||
/* Skip comments */
|
|
||||||
$cp += 2;
|
|
||||||
$z = strpos(substr($filebuf, $cp), '*/');
|
|
||||||
if ($z !== false) {
|
|
||||||
$lineno += count(explode("\n", substr($filebuf, $cp, $z))) - 1;
|
|
||||||
}
|
|
||||||
$cp += $z + 2;
|
|
||||||
} elseif ($filebuf[$cp] == '/' && $filebuf[$cp + 1] == '/') {
|
|
||||||
/* Skip C++ style comments too */
|
|
||||||
$cp += 2;
|
|
||||||
$z = strpos(substr($filebuf, $cp), "\n");
|
|
||||||
if ($z === false) {
|
|
||||||
$cp = strlen($filebuf);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
$lineno++;
|
|
||||||
}
|
|
||||||
$cp += $z;
|
|
||||||
} elseif ($filebuf[$cp] == "'" || $filebuf[$cp] == '"') {
|
|
||||||
/* String a character literals */
|
|
||||||
$startchar = $filebuf[$cp];
|
|
||||||
$prevc = 0;
|
|
||||||
for ($cp++; $cp < strlen($filebuf) && ($filebuf[$cp] != $startchar || $prevc === '\\'); $cp++) {
|
|
||||||
if ($filebuf[$cp] == "\n") {
|
|
||||||
$lineno++;
|
|
||||||
}
|
|
||||||
if ($prevc === '\\') {
|
|
||||||
$prevc = 0;
|
|
||||||
} else {
|
|
||||||
$prevc = $filebuf[$cp];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($cp >= strlen($filebuf)) {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"PHP code starting on this line is not terminated before the end of the file.");
|
|
||||||
$this->errorcnt++;
|
|
||||||
$nextcp = $cp;
|
|
||||||
} else {
|
|
||||||
$nextcp = $cp + 1;
|
|
||||||
}
|
|
||||||
} elseif (preg_match('/[a-zA-Z0-9]/', $filebuf[$cp])) {
|
|
||||||
/* Identifiers */
|
|
||||||
preg_match('/[a-zA-Z0-9_]+/', substr($filebuf, $cp), $preg_results);
|
|
||||||
$cp += strlen($preg_results[0]);
|
|
||||||
$nextcp = $cp;
|
|
||||||
} elseif ($filebuf[$cp] == ':' && $filebuf[$cp + 1] == ':' &&
|
|
||||||
$filebuf[$cp + 2] == '=') {
|
|
||||||
/* The operator "::=" */
|
|
||||||
$cp += 3;
|
|
||||||
$nextcp = $cp;
|
|
||||||
} elseif (($filebuf[$cp] == '/' || $filebuf[$cp] == '|') &&
|
|
||||||
preg_match('/[a-zA-Z]/', $filebuf[$cp + 1])) {
|
|
||||||
$cp += 2;
|
|
||||||
preg_match('/[a-zA-Z0-9_]+/', substr($filebuf, $cp), $preg_results);
|
|
||||||
$cp += strlen($preg_results[0]);
|
|
||||||
$nextcp = $cp;
|
|
||||||
} else {
|
|
||||||
/* All other (one character) operators */
|
|
||||||
$cp ++;
|
|
||||||
$nextcp = $cp;
|
|
||||||
}
|
|
||||||
$this->parseonetoken(substr($filebuf, $this->tokenstart,
|
|
||||||
$cp - $this->tokenstart)); /* Parse the token */
|
|
||||||
$cp = $nextcp - 1;
|
|
||||||
}
|
|
||||||
$gp->rule = $this->firstrule;
|
|
||||||
$gp->errorcnt = $this->errorcnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a single token
|
|
||||||
* @param string token
|
|
||||||
*/
|
|
||||||
function parseonetoken($token)
|
|
||||||
{
|
|
||||||
$x = $token;
|
|
||||||
$this->a = 0; // for referencing in WAITING_FOR_DECL_KEYWORD
|
|
||||||
if (PHP_ParserGenerator::DEBUG) {
|
|
||||||
printf("%s:%d: Token=[%s] state=%d\n",
|
|
||||||
$this->filename, $this->tokenlineno, $token, $this->state);
|
|
||||||
}
|
|
||||||
switch ($this->state) {
|
|
||||||
case self::INITIALIZE:
|
|
||||||
$this->prevrule = 0;
|
|
||||||
$this->preccounter = 0;
|
|
||||||
$this->firstrule = $this->lastrule = 0;
|
|
||||||
$this->gp->nrule = 0;
|
|
||||||
/* Fall thru to next case */
|
|
||||||
case self::WAITING_FOR_DECL_OR_RULE:
|
|
||||||
if ($x[0] == '%') {
|
|
||||||
$this->state = self::WAITING_FOR_DECL_KEYWORD;
|
|
||||||
} elseif (preg_match('/[a-z]/', $x[0])) {
|
|
||||||
$this->lhs = PHP_ParserGenerator_Symbol::Symbol_new($x);
|
|
||||||
$this->nrhs = 0;
|
|
||||||
$this->lhsalias = 0;
|
|
||||||
$this->state = self::WAITING_FOR_ARROW;
|
|
||||||
} elseif ($x[0] == '{') {
|
|
||||||
if ($this->prevrule === 0) {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"There is no prior rule opon which to attach the code
|
|
||||||
fragment which begins on this line.");
|
|
||||||
$this->errorcnt++;
|
|
||||||
} elseif ($this->prevrule->code != 0) {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"Code fragment beginning on this line is not the first \
|
|
||||||
to follow the previous rule.");
|
|
||||||
$this->errorcnt++;
|
|
||||||
} else {
|
|
||||||
$this->prevrule->line = $this->tokenlineno;
|
|
||||||
$this->prevrule->code = substr($x, 1);
|
|
||||||
}
|
|
||||||
} elseif ($x[0] == '[') {
|
|
||||||
$this->state = self::PRECEDENCE_MARK_1;
|
|
||||||
} else {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"Token \"%s\" should be either \"%%\" or a nonterminal name.",
|
|
||||||
$x);
|
|
||||||
$this->errorcnt++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::PRECEDENCE_MARK_1:
|
|
||||||
if (!preg_match('/[A-Z]/', $x[0])) {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"The precedence symbol must be a terminal.");
|
|
||||||
$this->errorcnt++;
|
|
||||||
} elseif ($this->prevrule === 0) {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"There is no prior rule to assign precedence \"[%s]\".", $x);
|
|
||||||
$this->errorcnt++;
|
|
||||||
} elseif ($this->prevrule->precsym != 0) {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"Precedence mark on this line is not the first to follow the previous rule.");
|
|
||||||
$this->errorcnt++;
|
|
||||||
} else {
|
|
||||||
$this->prevrule->precsym = PHP_ParserGenerator_Symbol::Symbol_new($x);
|
|
||||||
}
|
|
||||||
$this->state = self::PRECEDENCE_MARK_2;
|
|
||||||
break;
|
|
||||||
case self::PRECEDENCE_MARK_2:
|
|
||||||
if ($x[0] != ']') {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"Missing \"]\" on precedence mark.");
|
|
||||||
$this->errorcnt++;
|
|
||||||
}
|
|
||||||
$this->state = self::WAITING_FOR_DECL_OR_RULE;
|
|
||||||
break;
|
|
||||||
case self::WAITING_FOR_ARROW:
|
|
||||||
if ($x[0] == ':' && $x[1] == ':' && $x[2] == '=') {
|
|
||||||
$this->state = self::IN_RHS;
|
|
||||||
} elseif ($x[0] == '(') {
|
|
||||||
$this->state = self::LHS_ALIAS_1;
|
|
||||||
} else {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"Expected to see a \":\" following the LHS symbol \"%s\".",
|
|
||||||
$this->lhs->name);
|
|
||||||
$this->errorcnt++;
|
|
||||||
$this->state = self::RESYNC_AFTER_RULE_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::LHS_ALIAS_1:
|
|
||||||
if (preg_match('/[A-Za-z]/', $x[0])) {
|
|
||||||
$this->lhsalias = $x;
|
|
||||||
$this->state = self::LHS_ALIAS_2;
|
|
||||||
} else {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"\"%s\" is not a valid alias for the LHS \"%s\"\n",
|
|
||||||
$x, $this->lhs->name);
|
|
||||||
$this->errorcnt++;
|
|
||||||
$this->state = self::RESYNC_AFTER_RULE_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::LHS_ALIAS_2:
|
|
||||||
if ($x[0] == ')') {
|
|
||||||
$this->state = self::LHS_ALIAS_3;
|
|
||||||
} else {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"Missing \")\" following LHS alias name \"%s\".",$this->lhsalias);
|
|
||||||
$this->errorcnt++;
|
|
||||||
$this->state = self::RESYNC_AFTER_RULE_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::LHS_ALIAS_3:
|
|
||||||
if ($x == '::=') {
|
|
||||||
$this->state = self::IN_RHS;
|
|
||||||
} else {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"Missing \"->\" following: \"%s(%s)\".",
|
|
||||||
$this->lhs->name, $this->lhsalias);
|
|
||||||
$this->errorcnt++;
|
|
||||||
$this->state = self::RESYNC_AFTER_RULE_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::IN_RHS:
|
|
||||||
if ($x[0] == '.') {
|
|
||||||
$rp = new PHP_ParserGenerator_Rule;
|
|
||||||
$rp->ruleline = $this->tokenlineno;
|
|
||||||
for ($i = 0; $i < $this->nrhs; $i++) {
|
|
||||||
$rp->rhs[$i] = $this->rhs[$i];
|
|
||||||
$rp->rhsalias[$i] = $this->alias[$i];
|
|
||||||
}
|
|
||||||
if (count(array_unique($rp->rhsalias)) != count($rp->rhsalias)) {
|
|
||||||
$used = array();
|
|
||||||
foreach ($rp->rhsalias as $i => $symbol) {
|
|
||||||
if (!is_string($symbol)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (isset($used[$symbol])) {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename,
|
|
||||||
$this->tokenlineno,
|
|
||||||
"RHS symbol \"%s\" used multiple times.",
|
|
||||||
$symbol);
|
|
||||||
$this->errorcnt++;
|
|
||||||
} else {
|
|
||||||
$used[$symbol] = $i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$rp->lhs = $this->lhs;
|
|
||||||
$rp->lhsalias = $this->lhsalias;
|
|
||||||
$rp->nrhs = $this->nrhs;
|
|
||||||
$rp->code = 0;
|
|
||||||
$rp->precsym = 0;
|
|
||||||
$rp->index = $this->gp->nrule++;
|
|
||||||
$rp->nextlhs = $rp->lhs->rule;
|
|
||||||
$rp->lhs->rule = $rp;
|
|
||||||
$rp->next = 0;
|
|
||||||
if ($this->firstrule === 0) {
|
|
||||||
$this->firstrule = $this->lastrule = $rp;
|
|
||||||
} else {
|
|
||||||
$this->lastrule->next = $rp;
|
|
||||||
$this->lastrule = $rp;
|
|
||||||
}
|
|
||||||
$this->prevrule = $rp;
|
|
||||||
$this->state = self::WAITING_FOR_DECL_OR_RULE;
|
|
||||||
} elseif (preg_match('/[a-zA-Z]/', $x[0])) {
|
|
||||||
if ($this->nrhs >= PHP_ParserGenerator::MAXRHS) {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"Too many symbols on RHS or rule beginning at \"%s\".",
|
|
||||||
$x);
|
|
||||||
$this->errorcnt++;
|
|
||||||
$this->state = self::RESYNC_AFTER_RULE_ERROR;
|
|
||||||
} else {
|
|
||||||
if (isset($this->rhs[$this->nrhs - 1])) {
|
|
||||||
$msp = $this->rhs[$this->nrhs - 1];
|
|
||||||
if ($msp->type == PHP_ParserGenerator_Symbol::MULTITERMINAL) {
|
|
||||||
$inf = array_reduce($msp->subsym,
|
|
||||||
array($this, '_printmulti'), '');
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
'WARNING: symbol ' . $x . ' will not' .
|
|
||||||
' be part of previous multiterminal %s',
|
|
||||||
substr($inf, 0, strlen($inf) - 1)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->rhs[$this->nrhs] = PHP_ParserGenerator_Symbol::Symbol_new($x);
|
|
||||||
$this->alias[$this->nrhs] = 0;
|
|
||||||
$this->nrhs++;
|
|
||||||
}
|
|
||||||
} elseif (($x[0] == '|' || $x[0] == '/') && $this->nrhs > 0) {
|
|
||||||
$msp = $this->rhs[$this->nrhs - 1];
|
|
||||||
if ($msp->type != PHP_ParserGenerator_Symbol::MULTITERMINAL) {
|
|
||||||
$origsp = $msp;
|
|
||||||
$msp = new PHP_ParserGenerator_Symbol;
|
|
||||||
$msp->type = PHP_ParserGenerator_Symbol::MULTITERMINAL;
|
|
||||||
$msp->nsubsym = 1;
|
|
||||||
$msp->subsym = array($origsp);
|
|
||||||
$msp->name = $origsp->name;
|
|
||||||
$this->rhs[$this->nrhs - 1] = $msp;
|
|
||||||
}
|
|
||||||
$msp->nsubsym++;
|
|
||||||
$msp->subsym[$msp->nsubsym - 1] = PHP_ParserGenerator_Symbol::Symbol_new(substr($x, 1));
|
|
||||||
if (preg_match('/[a-z]/', $x[1]) ||
|
|
||||||
preg_match('/[a-z]/', $msp->subsym[0]->name[0])) {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"Cannot form a compound containing a non-terminal");
|
|
||||||
$this->errorcnt++;
|
|
||||||
}
|
|
||||||
} elseif ($x[0] == '(' && $this->nrhs > 0) {
|
|
||||||
$this->state = self::RHS_ALIAS_1;
|
|
||||||
} else {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"Illegal character on RHS of rule: \"%s\".", $x);
|
|
||||||
$this->errorcnt++;
|
|
||||||
$this->state = self::RESYNC_AFTER_RULE_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::RHS_ALIAS_1:
|
|
||||||
if (preg_match('/[A-Za-z]/', $x[0])) {
|
|
||||||
$this->alias[$this->nrhs - 1] = $x;
|
|
||||||
$this->state = self::RHS_ALIAS_2;
|
|
||||||
} else {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"\"%s\" is not a valid alias for the RHS symbol \"%s\"\n",
|
|
||||||
$x, $this->rhs[$this->nrhs - 1]->name);
|
|
||||||
$this->errorcnt++;
|
|
||||||
$this->state = self::RESYNC_AFTER_RULE_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::RHS_ALIAS_2:
|
|
||||||
if ($x[0] == ')') {
|
|
||||||
$this->state = self::IN_RHS;
|
|
||||||
} else {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"Missing \")\" following LHS alias name \"%s\".", $this->lhsalias);
|
|
||||||
$this->errorcnt++;
|
|
||||||
$this->state = self::RESYNC_AFTER_RULE_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::WAITING_FOR_DECL_KEYWORD:
|
|
||||||
if(preg_match('/[A-Za-z]/', $x[0])) {
|
|
||||||
$this->declkeyword = $x;
|
|
||||||
$this->declargslot = &$this->a;
|
|
||||||
$this->decllnslot = &$this->a;
|
|
||||||
$this->state = self::WAITING_FOR_DECL_ARG;
|
|
||||||
if ('name' == $x) {
|
|
||||||
$this->declargslot = &$this->gp->name;
|
|
||||||
} elseif ('include' == $x) {
|
|
||||||
$this->declargslot = &$this->gp->include_code;
|
|
||||||
$this->decllnslot = &$this->gp->includeln;
|
|
||||||
} elseif ('include_class' == $x) {
|
|
||||||
$this->declargslot = &$this->gp->include_classcode;
|
|
||||||
$this->decllnslot = &$this->gp->include_classln;
|
|
||||||
} elseif ('declare_class' == $x) {
|
|
||||||
$this->declargslot = &$this->gp->declare_classcode;
|
|
||||||
$this->decllnslot = &$this->gp->declare_classln;
|
|
||||||
} elseif ('code' == $x) {
|
|
||||||
$this->declargslot = &$this->gp->extracode;
|
|
||||||
$this->decllnslot = &$this->gp->extracodeln;
|
|
||||||
} elseif ('token_destructor' == $x) {
|
|
||||||
$this->declargslot = &$this->gp->tokendest;
|
|
||||||
$this->decllnslot = &$this->gp->tokendestln;
|
|
||||||
} elseif ('default_destructor' == $x) {
|
|
||||||
$this->declargslot = &$this->gp->vardest;
|
|
||||||
$this->decllnslot = &$this->gp->vardestln;
|
|
||||||
} elseif ('token_prefix' == $x) {
|
|
||||||
$this->declargslot = &$this->gp->tokenprefix;
|
|
||||||
} elseif ('syntax_error' == $x) {
|
|
||||||
$this->declargslot = &$this->gp->error;
|
|
||||||
$this->decllnslot = &$this->gp->errorln;
|
|
||||||
} elseif ('parse_accept' == $x) {
|
|
||||||
$this->declargslot = &$this->gp->accept;
|
|
||||||
$this->decllnslot = &$this->gp->acceptln;
|
|
||||||
} elseif ('parse_failure' == $x) {
|
|
||||||
$this->declargslot = &$this->gp->failure;
|
|
||||||
$this->decllnslot = &$this->gp->failureln;
|
|
||||||
} elseif ('stack_overflow' == $x) {
|
|
||||||
$this->declargslot = &$this->gp->overflow;
|
|
||||||
$this->decllnslot = &$this->gp->overflowln;
|
|
||||||
} elseif ('token_type' == $x) {
|
|
||||||
$this->declargslot = &$this->gp->tokentype;
|
|
||||||
} elseif ('default_type' == $x) {
|
|
||||||
$this->declargslot = &$this->gp->vartype;
|
|
||||||
} elseif ('stack_size' == $x) {
|
|
||||||
$this->declargslot = &$this->gp->stacksize;
|
|
||||||
} elseif ('start_symbol' == $x) {
|
|
||||||
$this->declargslot = &$this->gp->start;
|
|
||||||
} elseif ('left' == $x) {
|
|
||||||
$this->preccounter++;
|
|
||||||
$this->declassoc = PHP_ParserGenerator_Symbol::LEFT;
|
|
||||||
$this->state = self::WAITING_FOR_PRECEDENCE_SYMBOL;
|
|
||||||
} elseif ('right' == $x) {
|
|
||||||
$this->preccounter++;
|
|
||||||
$this->declassoc = PHP_ParserGenerator_Symbol::RIGHT;
|
|
||||||
$this->state = self::WAITING_FOR_PRECEDENCE_SYMBOL;
|
|
||||||
} elseif ('nonassoc' == $x) {
|
|
||||||
$this->preccounter++;
|
|
||||||
$this->declassoc = PHP_ParserGenerator_Symbol::NONE;
|
|
||||||
$this->state = self::WAITING_FOR_PRECEDENCE_SYMBOL;
|
|
||||||
} elseif ('destructor' == $x) {
|
|
||||||
$this->state = self::WAITING_FOR_DESTRUCTOR_SYMBOL;
|
|
||||||
} elseif ('type' == $x) {
|
|
||||||
$this->state = self::WAITING_FOR_DATATYPE_SYMBOL;
|
|
||||||
} elseif ('fallback' == $x) {
|
|
||||||
$this->fallback = 0;
|
|
||||||
$this->state = self::WAITING_FOR_FALLBACK_ID;
|
|
||||||
} else {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"Unknown declaration keyword: \"%%%s\".", $x);
|
|
||||||
$this->errorcnt++;
|
|
||||||
$this->state = self::RESYNC_AFTER_DECL_ERROR;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"Illegal declaration keyword: \"%s\".", $x);
|
|
||||||
$this->errorcnt++;
|
|
||||||
$this->state = self::RESYNC_AFTER_DECL_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::WAITING_FOR_DESTRUCTOR_SYMBOL:
|
|
||||||
if (!preg_match('/[A-Za-z]/', $x[0])) {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"Symbol name missing after %destructor keyword");
|
|
||||||
$this->errorcnt++;
|
|
||||||
$this->state = self::RESYNC_AFTER_DECL_ERROR;
|
|
||||||
} else {
|
|
||||||
$sp = PHP_ParserGenerator_Symbol::Symbol_new($x);
|
|
||||||
$this->declargslot = &$sp->destructor;
|
|
||||||
$this->decllnslot = &$sp->destructorln;
|
|
||||||
$this->state = self::WAITING_FOR_DECL_ARG;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::WAITING_FOR_DATATYPE_SYMBOL:
|
|
||||||
if (!preg_match('/[A-Za-z]/', $x[0])) {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"Symbol name missing after %destructor keyword");
|
|
||||||
$this->errorcnt++;
|
|
||||||
$this->state = self::RESYNC_AFTER_DECL_ERROR;
|
|
||||||
} else {
|
|
||||||
$sp = PHP_ParserGenerator_Symbol::Symbol_new($x);
|
|
||||||
$this->declargslot = &$sp->datatype;
|
|
||||||
$this->state = self::WAITING_FOR_DECL_ARG;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::WAITING_FOR_PRECEDENCE_SYMBOL:
|
|
||||||
if ($x[0] == '.') {
|
|
||||||
$this->state = self::WAITING_FOR_DECL_OR_RULE;
|
|
||||||
} elseif (preg_match('/[A-Z]/', $x[0])) {
|
|
||||||
$sp = PHP_ParserGenerator_Symbol::Symbol_new($x);
|
|
||||||
if ($sp->prec >= 0) {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"Symbol \"%s\" has already been given a precedence.", $x);
|
|
||||||
$this->errorcnt++;
|
|
||||||
} else {
|
|
||||||
$sp->prec = $this->preccounter;
|
|
||||||
$sp->assoc = $this->declassoc;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"Can't assign a precedence to \"%s\".", $x);
|
|
||||||
$this->errorcnt++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::WAITING_FOR_DECL_ARG:
|
|
||||||
if (preg_match('/[A-Za-z0-9{"]/', $x[0])) {
|
|
||||||
if ($this->declargslot != 0) {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"The argument \"%s\" to declaration \"%%%s\" is not the first.",
|
|
||||||
$x[0] == '"' ? substr($x, 1) : $x, $this->declkeyword);
|
|
||||||
$this->errorcnt++;
|
|
||||||
$this->state = self::RESYNC_AFTER_DECL_ERROR;
|
|
||||||
} else {
|
|
||||||
$this->declargslot = ($x[0] == '"' || $x[0] == '{') ? substr($x, 1) : $x;
|
|
||||||
$this->a = 1;
|
|
||||||
if (!$this->decllnslot) {
|
|
||||||
$this->decllnslot = $this->tokenlineno;
|
|
||||||
}
|
|
||||||
$this->state = self::WAITING_FOR_DECL_OR_RULE;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"Illegal argument to %%%s: %s",$this->declkeyword, $x);
|
|
||||||
$this->errorcnt++;
|
|
||||||
$this->state = self::RESYNC_AFTER_DECL_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::WAITING_FOR_FALLBACK_ID:
|
|
||||||
if ($x[0] == '.') {
|
|
||||||
$this->state = self::WAITING_FOR_DECL_OR_RULE;
|
|
||||||
} elseif (!preg_match('/[A-Z]/', $x[0])) {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"%%fallback argument \"%s\" should be a token", $x);
|
|
||||||
$this->errorcnt++;
|
|
||||||
} else {
|
|
||||||
$sp = PHP_ParserGenerator_Symbol::Symbol_new($x);
|
|
||||||
if ($this->fallback === 0) {
|
|
||||||
$this->fallback = $sp;
|
|
||||||
} elseif (is_object($sp->fallback)) {
|
|
||||||
PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno,
|
|
||||||
"More than one fallback assigned to token %s", $x);
|
|
||||||
$this->errorcnt++;
|
|
||||||
} else {
|
|
||||||
$sp->fallback = $this->fallback;
|
|
||||||
$this->gp->has_fallback = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::RESYNC_AFTER_RULE_ERROR:
|
|
||||||
/* if ($x[0] == '.') $this->state = self::WAITING_FOR_DECL_OR_RULE;
|
|
||||||
** break; */
|
|
||||||
case self::RESYNC_AFTER_DECL_ERROR:
|
|
||||||
if ($x[0] == '.') {
|
|
||||||
$this->state = self::WAITING_FOR_DECL_OR_RULE;
|
|
||||||
}
|
|
||||||
if ($x[0] == '%') {
|
|
||||||
$this->state = self::WAITING_FOR_DECL_KEYWORD;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* return a descriptive string for a multi-terminal token.
|
|
||||||
*
|
|
||||||
* @param string $a
|
|
||||||
* @param string $b
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function _printmulti($a, $b)
|
|
||||||
{
|
|
||||||
if (!$a) {
|
|
||||||
$a = '';
|
|
||||||
}
|
|
||||||
$a .= $b->name . '|';
|
|
||||||
return $a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* PHP_ParserGenerator, a php 5 parser generator.
|
|
||||||
*
|
|
||||||
* This is a direct port of the Lemon parser generator, found at
|
|
||||||
* {@link http://www.hwaci.com/sw/lemon/}
|
|
||||||
*
|
|
||||||
* PHP version 5
|
|
||||||
*
|
|
||||||
* LICENSE:
|
|
||||||
*
|
|
||||||
* Copyright (c) 2006, Gregory Beaver <cellog@php.net>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in
|
|
||||||
* the documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the PHP_ParserGenerator nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived
|
|
||||||
* from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
||||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
||||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version CVS: $Id: PropagationLink.php 302382 2010-08-17 06:08:09Z jespino $
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since File available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* A followset propagation link indicates that the contents of one
|
|
||||||
* configuration followset should be propagated to another whenever
|
|
||||||
* the first changes.
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version Release: @package_version@
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since Class available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
class PHP_ParserGenerator_PropagationLink
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The configuration that defines this propagation link
|
|
||||||
*
|
|
||||||
* @var PHP_ParserGenerator_Config
|
|
||||||
*/
|
|
||||||
public $cfp;
|
|
||||||
/**
|
|
||||||
* The next propagation link
|
|
||||||
*
|
|
||||||
* @var PHP_ParserGenerator_PropagationLink|0
|
|
||||||
*/
|
|
||||||
public $next = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a propagation link to the current list
|
|
||||||
*
|
|
||||||
* This prepends the configuration passed in to the first parameter
|
|
||||||
* which is either 0 or a PHP_ParserGenerator_PropagationLink defining
|
|
||||||
* an existing list.
|
|
||||||
*
|
|
||||||
* @param PHP_ParserGenerator_PropagationLink|null
|
|
||||||
* @param PHP_ParserGenerator_Config
|
|
||||||
*/
|
|
||||||
static function Plink_add(&$plpp, PHP_ParserGenerator_Config $cfp)
|
|
||||||
{
|
|
||||||
$new = new PHP_ParserGenerator_PropagationLink;
|
|
||||||
$new->next = $plpp;
|
|
||||||
$plpp = $new;
|
|
||||||
$new->cfp = $cfp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transfer every propagation link on the list "from" to the list "to"
|
|
||||||
*/
|
|
||||||
static function Plink_copy(PHP_ParserGenerator_PropagationLink &$to, PHP_ParserGenerator_PropagationLink $from)
|
|
||||||
{
|
|
||||||
while ($from) {
|
|
||||||
$nextpl = $from->next;
|
|
||||||
$from->next = $to;
|
|
||||||
$to = $from;
|
|
||||||
$from = $nextpl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete every propagation link on the list
|
|
||||||
*
|
|
||||||
* @param PHP_ParserGenerator_PropagationLink|0
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
static function Plink_delete($plp)
|
|
||||||
{
|
|
||||||
while ($plp) {
|
|
||||||
$nextpl = $plp->next;
|
|
||||||
$plp->next = 0;
|
|
||||||
$plp = $nextpl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* PHP_ParserGenerator, a php 5 parser generator.
|
|
||||||
*
|
|
||||||
* This is a direct port of the Lemon parser generator, found at
|
|
||||||
* {@link http://www.hwaci.com/sw/lemon/}
|
|
||||||
*
|
|
||||||
* PHP version 5
|
|
||||||
*
|
|
||||||
* LICENSE:
|
|
||||||
*
|
|
||||||
* Copyright (c) 2006, Gregory Beaver <cellog@php.net>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in
|
|
||||||
* the documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the PHP_ParserGenerator nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived
|
|
||||||
* from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
||||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
||||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version CVS: $Id: Rule.php 302382 2010-08-17 06:08:09Z jespino $
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since File available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Each production rule in the grammar is stored in this class
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version Release: @package_version@
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since Class available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
class PHP_ParserGenerator_Rule
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Left-hand side of the rule
|
|
||||||
* @var array an array of {@link PHP_ParserGenerator_Symbol} objects
|
|
||||||
*/
|
|
||||||
public $lhs;
|
|
||||||
/**
|
|
||||||
* Alias for the LHS (NULL if none)
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $lhsalias = array();
|
|
||||||
/**
|
|
||||||
* Line number for the rule
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $ruleline;
|
|
||||||
/**
|
|
||||||
* Number of right-hand side symbols
|
|
||||||
*/
|
|
||||||
public $nrhs;
|
|
||||||
/**
|
|
||||||
* The right-hand side symbols
|
|
||||||
* @var array an array of {@link PHP_ParserGenerator_Symbol} objects
|
|
||||||
*/
|
|
||||||
public $rhs;
|
|
||||||
/**
|
|
||||||
* Aliases for each right-hand side symbol, or null if no alis.
|
|
||||||
*
|
|
||||||
* In this rule:
|
|
||||||
* <pre>
|
|
||||||
* foo ::= BAR(A) baz(B).
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* The right-hand side aliases are A for BAR, and B for baz.
|
|
||||||
* @var array aliases are indexed by the right-hand side symbol index.
|
|
||||||
*/
|
|
||||||
public $rhsalias = array();
|
|
||||||
/**
|
|
||||||
* Line number at which code begins
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $line;
|
|
||||||
/**
|
|
||||||
* The code executed when this rule is reduced
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* foo(R) ::= BAR(A) baz(B). {R = A + B;}
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* In the rule above, the code is "R = A + B;"
|
|
||||||
* @var string|0
|
|
||||||
*/
|
|
||||||
public $code;
|
|
||||||
/**
|
|
||||||
* Precedence symbol for this rule
|
|
||||||
* @var PHP_ParserGenerator_Symbol
|
|
||||||
*/
|
|
||||||
public $precsym;
|
|
||||||
/**
|
|
||||||
* An index number for this rule
|
|
||||||
*
|
|
||||||
* Used in both naming of reduce functions and determining which rule code
|
|
||||||
* to use for reduce actions
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $index;
|
|
||||||
/**
|
|
||||||
* True if this rule is ever reduced
|
|
||||||
* @var boolean
|
|
||||||
*/
|
|
||||||
public $canReduce;
|
|
||||||
/**
|
|
||||||
* Next rule with the same left-hand side
|
|
||||||
* @var PHP_ParserGenerator_Rule|0
|
|
||||||
*/
|
|
||||||
public $nextlhs;
|
|
||||||
/**
|
|
||||||
* Next rule in the global list
|
|
||||||
* @var PHP_ParserGenerator_Rule|0
|
|
||||||
*/
|
|
||||||
public $next;
|
|
||||||
}
|
|
||||||
@@ -1,283 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* PHP_ParserGenerator, a php 5 parser generator.
|
|
||||||
*
|
|
||||||
* This is a direct port of the Lemon parser generator, found at
|
|
||||||
* {@link http://www.hwaci.com/sw/lemon/}
|
|
||||||
*
|
|
||||||
* PHP version 5
|
|
||||||
*
|
|
||||||
* LICENSE:
|
|
||||||
*
|
|
||||||
* Copyright (c) 2006, Gregory Beaver <cellog@php.net>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in
|
|
||||||
* the documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the PHP_ParserGenerator nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived
|
|
||||||
* from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
||||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
||||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version CVS: $Id: State.php 302382 2010-08-17 06:08:09Z jespino $
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since File available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The structure used to represent a state in the associative array
|
|
||||||
* for a PHP_ParserGenerator_Config.
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version Release: @package_version@
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since Class available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
class PHP_ParserGenerator_StateNode
|
|
||||||
{
|
|
||||||
public $key;
|
|
||||||
public $data;
|
|
||||||
public $from = 0;
|
|
||||||
public $next = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Each state of the generated parser's finite state machine
|
|
||||||
* is encoded as an instance of this class
|
|
||||||
*
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.php.net/license/3_01.txt PHP License 3.01
|
|
||||||
* @version Release: @package_version@
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since Class available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
class PHP_ParserGenerator_State
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The basis configurations for this state
|
|
||||||
* @var PHP_ParserGenerator_Config
|
|
||||||
*/
|
|
||||||
public $bp;
|
|
||||||
/**
|
|
||||||
* All configurations in this state
|
|
||||||
* @var PHP_ParserGenerator_Config
|
|
||||||
*/
|
|
||||||
public $cfp;
|
|
||||||
/**
|
|
||||||
* Sequential number for this state
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $statenum;
|
|
||||||
/**
|
|
||||||
* Linked list of actions for this state.
|
|
||||||
* @var PHP_ParserGenerator_Action
|
|
||||||
*/
|
|
||||||
public $ap;
|
|
||||||
/**
|
|
||||||
* Number of terminal (token) actions
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $nTknAct,
|
|
||||||
/**
|
|
||||||
* Number of non-terminal actions
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
$nNtAct;
|
|
||||||
/**
|
|
||||||
* The offset into the $yy_action table for terminal tokens.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $iTknOfst,
|
|
||||||
/**
|
|
||||||
* The offset into the $yy_action table for non-terminals.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
$iNtOfst;
|
|
||||||
/**
|
|
||||||
* Default action
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $iDflt;
|
|
||||||
/**
|
|
||||||
* Associative array of PHP_ParserGenerator_State objects
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public static $x3a = array();
|
|
||||||
/**
|
|
||||||
* Array of PHP_ParserGenerator_State objects
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public static $states = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare two states for sorting purposes. The smaller state is the
|
|
||||||
* one with the most non-terminal actions. If they have the same number
|
|
||||||
* of non-terminal actions, then the smaller is the one with the most
|
|
||||||
* token actions.
|
|
||||||
*/
|
|
||||||
static function stateResortCompare($a, $b)
|
|
||||||
{
|
|
||||||
$n = $b->nNtAct - $a->nNtAct;
|
|
||||||
if ($n === 0) {
|
|
||||||
$n = $b->nTknAct - $a->nTknAct;
|
|
||||||
}
|
|
||||||
return $n;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare two states based on their configurations
|
|
||||||
*
|
|
||||||
* @param PHP_ParserGenerator_Config|0 $a
|
|
||||||
* @param PHP_ParserGenerator_Config|0 $b
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
static function statecmp($a, $b)
|
|
||||||
{
|
|
||||||
for ($rc = 0; $rc == 0 && $a && $b; $a = $a->bp, $b = $b->bp) {
|
|
||||||
$rc = $a->rp->index - $b->rp->index;
|
|
||||||
if ($rc === 0) {
|
|
||||||
$rc = $a->dot - $b->dot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($rc == 0) {
|
|
||||||
if ($a) {
|
|
||||||
$rc = 1;
|
|
||||||
}
|
|
||||||
if ($b) {
|
|
||||||
$rc = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hash a state based on its configuration
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
private static function statehash(PHP_ParserGenerator_Config $a)
|
|
||||||
{
|
|
||||||
$h = 0;
|
|
||||||
while ($a) {
|
|
||||||
$h = $h * 571 + $a->rp->index * 37 + $a->dot;
|
|
||||||
$a = $a->bp;
|
|
||||||
}
|
|
||||||
return (int) $h;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a pointer to data assigned to the given key. Return NULL
|
|
||||||
* if no such key.
|
|
||||||
* @param PHP_ParserGenerator_Config
|
|
||||||
* @return null|PHP_ParserGenerator_State
|
|
||||||
*/
|
|
||||||
static function State_find(PHP_ParserGenerator_Config $key)
|
|
||||||
{
|
|
||||||
if (!count(self::$x3a)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
$h = self::statehash($key);
|
|
||||||
if (!isset(self::$x3a[$h])) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
$np = self::$x3a[$h];
|
|
||||||
while ($np) {
|
|
||||||
if (self::statecmp($np->key, $key) == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$np = $np->next;
|
|
||||||
}
|
|
||||||
return $np ? $np->data : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert a new record into the array. Return TRUE if successful.
|
|
||||||
* Prior data with the same key is NOT overwritten
|
|
||||||
*
|
|
||||||
* @param PHP_ParserGenerator_State $state
|
|
||||||
* @param PHP_ParserGenerator_Config $key
|
|
||||||
* @return unknown
|
|
||||||
*/
|
|
||||||
static function State_insert(PHP_ParserGenerator_State $state, PHP_ParserGenerator_Config $key)
|
|
||||||
{
|
|
||||||
$h = self::statehash($key);
|
|
||||||
if (isset(self::$x3a[$h])) {
|
|
||||||
$np = self::$x3a[$h];
|
|
||||||
} else {
|
|
||||||
$np = 0;
|
|
||||||
}
|
|
||||||
while ($np) {
|
|
||||||
if (self::statecmp($np->key, $key) == 0) {
|
|
||||||
/* An existing entry with the same key is found. */
|
|
||||||
/* Fail because overwrite is not allows. */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
$np = $np->next;
|
|
||||||
}
|
|
||||||
/* Insert the new data */
|
|
||||||
$np = new PHP_ParserGenerator_StateNode;
|
|
||||||
$np->key = $key;
|
|
||||||
$np->data = $state;
|
|
||||||
self::$states[] = $np;
|
|
||||||
// the original lemon code sets the from link always to itself
|
|
||||||
// setting up a faulty double-linked list
|
|
||||||
// however, the from links are never used, so I suspect a copy/paste
|
|
||||||
// error from a standard algorithm that was never caught
|
|
||||||
if (isset(self::$x3a[$h])) {
|
|
||||||
self::$x3a[$h]->from = $np; // lemon has $np->next here
|
|
||||||
} else {
|
|
||||||
self::$x3a[$h] = 0; // dummy to avoid notice
|
|
||||||
}
|
|
||||||
$np->next = self::$x3a[$h];
|
|
||||||
self::$x3a[$h] = $np;
|
|
||||||
$np->from = self::$x3a[$h];
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an array indexed by state number
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
static function State_arrayof()
|
|
||||||
{
|
|
||||||
return self::$states;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,288 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* PHP_ParserGenerator, a php 5 parser generator.
|
|
||||||
*
|
|
||||||
* This is a direct port of the Lemon parser generator, found at
|
|
||||||
* {@link http://www.hwaci.com/sw/lemon/}
|
|
||||||
*
|
|
||||||
* PHP version 5
|
|
||||||
*
|
|
||||||
* LICENSE:
|
|
||||||
*
|
|
||||||
* Copyright (c) 2006, Gregory Beaver <cellog@php.net>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in
|
|
||||||
* the documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the PHP_ParserGenerator nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived
|
|
||||||
* from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
||||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
||||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version CVS: $Id: Symbol.php 302382 2010-08-17 06:08:09Z jespino $
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since File available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Symbols (terminals and nonterminals) of the grammar are stored in this class
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_ParserGenerator
|
|
||||||
* @author Gregory Beaver <cellog@php.net>
|
|
||||||
* @copyright 2006 Gregory Beaver
|
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
|
||||||
* @version Release: @package_version@
|
|
||||||
* @link http://pear.php.net/package/PHP_ParserGenerator
|
|
||||||
* @since Class available since Release 0.1.0
|
|
||||||
*/
|
|
||||||
class PHP_ParserGenerator_Symbol
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Symbols that start with a capital letter like FOO.
|
|
||||||
*
|
|
||||||
* These are tokens directly from the lexer
|
|
||||||
*/
|
|
||||||
const TERMINAL = 1;
|
|
||||||
/**
|
|
||||||
* Symbols that start with a lower-case letter like foo.
|
|
||||||
*
|
|
||||||
* These are grammar rules like "foo ::= BLAH."
|
|
||||||
*/
|
|
||||||
const NONTERMINAL = 2;
|
|
||||||
/**
|
|
||||||
* Multiple terminal symbols.
|
|
||||||
*
|
|
||||||
* These are a grammar rule that consists of several terminals like
|
|
||||||
* FOO|BAR|BAZ. Note that non-terminals cannot be in a multi-terminal,
|
|
||||||
* and a multi-terminal acts like a single terminal.
|
|
||||||
*
|
|
||||||
* "FOO|BAR FOO|BAZ" is actually two multi-terminals, FOO|BAR and FOO|BAZ.
|
|
||||||
*/
|
|
||||||
const MULTITERMINAL = 3;
|
|
||||||
|
|
||||||
const LEFT = 1;
|
|
||||||
const RIGHT = 2;
|
|
||||||
const NONE = 3;
|
|
||||||
const UNK = 4;
|
|
||||||
/**
|
|
||||||
* Name of the symbol
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $name;
|
|
||||||
/**
|
|
||||||
* Index of this symbol.
|
|
||||||
*
|
|
||||||
* This will ultimately end up representing the symbol in the generated
|
|
||||||
* parser
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $index;
|
|
||||||
/**
|
|
||||||
* Symbol type
|
|
||||||
*
|
|
||||||
* One of PHP_ParserGenerator_Symbol::TERMINAL,
|
|
||||||
* PHP_ParserGenerator_Symbol::NONTERMINAL or
|
|
||||||
* PHP_ParserGenerator_Symbol::MULTITERMINAL
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $type;
|
|
||||||
/**
|
|
||||||
* Linked list of rules that use this symbol, if it is a non-terminal.
|
|
||||||
* @var PHP_ParserGenerator_Rule
|
|
||||||
*/
|
|
||||||
public $rule;
|
|
||||||
/**
|
|
||||||
* Fallback token in case this token doesn't parse
|
|
||||||
* @var PHP_ParserGenerator_Symbol
|
|
||||||
*/
|
|
||||||
public $fallback;
|
|
||||||
/**
|
|
||||||
* Precendence, if defined.
|
|
||||||
*
|
|
||||||
* -1 if no unusual precedence
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $prec = -1;
|
|
||||||
/**
|
|
||||||
* Associativity if precedence is defined.
|
|
||||||
*
|
|
||||||
* One of PHP_ParserGenerator_Symbol::LEFT,
|
|
||||||
* PHP_ParserGenerator_Symbol::RIGHT, PHP_ParserGenerator_Symbol::NONE
|
|
||||||
* or PHP_ParserGenerator_Symbol::UNK
|
|
||||||
* @var unknown_type
|
|
||||||
*/
|
|
||||||
public $assoc;
|
|
||||||
/**
|
|
||||||
* First-set for all rules of this symbol
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $firstset;
|
|
||||||
/**
|
|
||||||
* True if this symbol is a non-terminal and can generate an empty
|
|
||||||
* result.
|
|
||||||
*
|
|
||||||
* For instance "foo ::= ."
|
|
||||||
* @var boolean
|
|
||||||
*/
|
|
||||||
public $lambda;
|
|
||||||
/**
|
|
||||||
* Code that executes whenever this symbol is popped from the stack during
|
|
||||||
* error processing.
|
|
||||||
*
|
|
||||||
* @var string|0
|
|
||||||
*/
|
|
||||||
public $destructor = 0;
|
|
||||||
/**
|
|
||||||
* Line number of destructor code
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $destructorln;
|
|
||||||
/**
|
|
||||||
* Unused relic of the C version of Lemon.
|
|
||||||
*
|
|
||||||
* The data type of information held by this object. Only used
|
|
||||||
* if this is a non-terminal
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $datatype;
|
|
||||||
/**
|
|
||||||
* Unused relic of the C version of Lemon.
|
|
||||||
*
|
|
||||||
* The data type number. In the parser, the value
|
|
||||||
* stack is a union. The .yy%d element of this
|
|
||||||
* union is the correct data type for this object
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $dtnum;
|
|
||||||
/**#@+
|
|
||||||
* The following fields are used by MULTITERMINALs only
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Number of terminal symbols in the MULTITERMINAL
|
|
||||||
*
|
|
||||||
* This is of course the same as count($this->subsym)
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $nsubsym;
|
|
||||||
/**
|
|
||||||
* Array of terminal symbols in the MULTITERMINAL
|
|
||||||
* @var array an array of {@link PHP_ParserGenerator_Symbol} objects
|
|
||||||
*/
|
|
||||||
public $subsym = array();
|
|
||||||
/**#@-*/
|
|
||||||
/**
|
|
||||||
* Singleton storage of symbols
|
|
||||||
*
|
|
||||||
* @var array an array of PHP_ParserGenerator_Symbol objects
|
|
||||||
*/
|
|
||||||
private static $_symbol_table = array();
|
|
||||||
/**
|
|
||||||
* Return a pointer to the (terminal or nonterminal) symbol "x".
|
|
||||||
* Create a new symbol if this is the first time "x" has been seen.
|
|
||||||
* (this is a singleton)
|
|
||||||
* @param string
|
|
||||||
* @return PHP_ParserGenerator_Symbol
|
|
||||||
*/
|
|
||||||
public static function Symbol_new($x)
|
|
||||||
{
|
|
||||||
if (isset(self::$_symbol_table[$x])) {
|
|
||||||
return self::$_symbol_table[$x];
|
|
||||||
}
|
|
||||||
$sp = new PHP_ParserGenerator_Symbol;
|
|
||||||
$sp->name = $x;
|
|
||||||
$sp->type = preg_match('/[A-Z]/', $x[0]) ? self::TERMINAL : self::NONTERMINAL;
|
|
||||||
$sp->rule = 0;
|
|
||||||
$sp->fallback = 0;
|
|
||||||
$sp->prec = -1;
|
|
||||||
$sp->assoc = self::UNK;
|
|
||||||
$sp->firstset = array();
|
|
||||||
$sp->lambda = false;
|
|
||||||
$sp->destructor = 0;
|
|
||||||
$sp->datatype = 0;
|
|
||||||
self::$_symbol_table[$sp->name] = $sp;
|
|
||||||
return $sp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the number of unique symbols
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public static function Symbol_count()
|
|
||||||
{
|
|
||||||
return count(self::$_symbol_table);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function Symbol_arrayof()
|
|
||||||
{
|
|
||||||
return array_values(self::$_symbol_table);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function Symbol_find($x)
|
|
||||||
{
|
|
||||||
if (isset(self::$_symbol_table[$x])) {
|
|
||||||
return self::$_symbol_table[$x];
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sort function helper for symbols
|
|
||||||
*
|
|
||||||
* Symbols that begin with upper case letters (terminals or tokens)
|
|
||||||
* must sort before symbols that begin with lower case letters
|
|
||||||
* (non-terminals). Other than that, the order does not matter.
|
|
||||||
*
|
|
||||||
* We find experimentally that leaving the symbols in their original
|
|
||||||
* order (the order they appeared in the grammar file) gives the
|
|
||||||
* smallest parser tables in SQLite.
|
|
||||||
* @param PHP_ParserGenerator_Symbol
|
|
||||||
* @param PHP_ParserGenerator_Symbol
|
|
||||||
*/
|
|
||||||
public static function sortSymbols($a, $b)
|
|
||||||
{
|
|
||||||
$i1 = $a->index + 10000000*(ord($a->name[0]) > ord('Z'));
|
|
||||||
$i2 = $b->index + 10000000*(ord($b->name[0]) > ord('Z'));
|
|
||||||
return $i1 - $i2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if two symbols are the same.
|
|
||||||
*/
|
|
||||||
public static function same_symbol(PHP_ParserGenerator_Symbol $a, PHP_ParserGenerator_Symbol $b)
|
|
||||||
{
|
|
||||||
if ($a === $b) return 1;
|
|
||||||
if ($a->type != self::MULTITERMINAL) return 0;
|
|
||||||
if ($b->type != self::MULTITERMINAL) return 0;
|
|
||||||
if ($a->nsubsym != $b->nsubsym) return 0;
|
|
||||||
for ($i = 0; $i < $a->nsubsym; $i++) {
|
|
||||||
if ($a->subsym[$i] != $b->subsym[$i]) return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once 'PHP/ParserGenerator.php';
|
|
||||||
$me = new PHP_ParserGenerator;
|
|
||||||
$me->main();
|
|
||||||
?>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# Rebuild the iTop Lexer / Parser
|
|
||||||
# PEAR is required to build (really?)
|
|
||||||
# Launch this batch from the core/oql/build directory
|
|
||||||
# with ./build.bash
|
|
||||||
#
|
|
||||||
php PHP/LexerGenerator/cli.php ../oql-lexer.plex
|
|
||||||
php PHP/ParserGenerator/cli.php ../oql-parser.y
|
|
||||||
php -r "echo date('Y-m-d');" > ../version.txt
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
rem must be run with current directory = the directory of the batch
|
|
||||||
rem PEAR is required to build
|
|
||||||
php -d include_path=".;C:\iTop\PHP\PEAR" ".\PHP\LexerGenerator\cli.php" ..\oql-lexer.plex
|
|
||||||
php ".\PHP\ParserGenerator\cli.php" ..\oql-parser.y
|
|
||||||
php -r "echo date('Y-m-d');" > ..\version.txt
|
|
||||||
pause
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Minimal file (with all the needed includes) to check the validity of an OQL by verifying:
|
|
||||||
* - The syntax (of the OQL query string)
|
|
||||||
* - The consistency with a given data model (represented by an instance of ModelReflection)
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
*
|
|
||||||
* require_once(APPROOT.'core/oql/check_oql.php');
|
|
||||||
*
|
|
||||||
* $sOQL = "SELECT Zerver WHERE status = 'production'";
|
|
||||||
* $oModelReflection = new ModelReflectionRuntime();
|
|
||||||
* $aResults = CheckOQL($sOQL, $oModelReflection);
|
|
||||||
* if ($aResults['status'] == 'error')
|
|
||||||
* {
|
|
||||||
* echo "The query '$sOQL' is not a valid query. Reason: {$aResults['message']}";
|
|
||||||
* }
|
|
||||||
* else
|
|
||||||
* {
|
|
||||||
* echo "Ok, '$sOQL' is a valid query";
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
class CoreException extends Exception
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
require_once(__DIR__.'/expression.class.inc.php');
|
|
||||||
require_once(__DIR__.'/oqlquery.class.inc.php');
|
|
||||||
require_once(__DIR__.'/oqlexception.class.inc.php');
|
|
||||||
require_once(__DIR__.'/oql-parser.php');
|
|
||||||
require_once(__DIR__.'/oql-lexer.php');
|
|
||||||
require_once(__DIR__.'/oqlinterpreter.class.inc.php');
|
|
||||||
|
|
||||||
function CheckOQL($sOQL, ModelReflection $oModelReflection)
|
|
||||||
{
|
|
||||||
$aRes = array('status' => 'ok', 'message' => '');
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$oOql = new OqlInterpreter($sOQL);
|
|
||||||
$oOqlQuery = $oOql->ParseQuery(); // Exceptions thrown in case of issue
|
|
||||||
$oOqlQuery->Check($oModelReflection,$sOQL); // Exceptions thrown in case of issue
|
|
||||||
}
|
|
||||||
catch(Exception $e)
|
|
||||||
{
|
|
||||||
$aRes['status'] = 'error';
|
|
||||||
$aRes['message'] = $e->getMessage();
|
|
||||||
}
|
|
||||||
return $aRes;
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
# The following source files are not re-distributed with the "build" of the application
|
# The following source files are not re-distributed with the "build" of the application
|
||||||
# since they are used solely for constructing other files during the build process
|
# since they are used solely for constructing other files during the build process
|
||||||
#
|
#
|
||||||
build
|
build.cmd
|
||||||
|
build.bash
|
||||||
oql-lexer.plex
|
oql-lexer.plex
|
||||||
oql-parser.y
|
oql-parser.y
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2012 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
/**
|
/**
|
||||||
* OQL syntax analyzer, to be used prior to run the lexical analyzer
|
* OQL syntax analyzer, to be used prior to run the lexical analyzer
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -108,7 +108,6 @@ class OQLLexerRaw
|
|||||||
do {
|
do {
|
||||||
$rules = array(
|
$rules = array(
|
||||||
'/\G[ \t\n\r]+/ ',
|
'/\G[ \t\n\r]+/ ',
|
||||||
'/\GUNION/ ',
|
|
||||||
'/\GSELECT/ ',
|
'/\GSELECT/ ',
|
||||||
'/\GFROM/ ',
|
'/\GFROM/ ',
|
||||||
'/\GAS/ ',
|
'/\GAS/ ',
|
||||||
@@ -281,364 +280,359 @@ class OQLLexerRaw
|
|||||||
function yy_r1_1($yy_subpatterns)
|
function yy_r1_1($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::UNION;
|
$this->token = OQLParser::SELECT;
|
||||||
}
|
}
|
||||||
function yy_r1_2($yy_subpatterns)
|
function yy_r1_2($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::SELECT;
|
$this->token = OQLParser::FROM;
|
||||||
}
|
}
|
||||||
function yy_r1_3($yy_subpatterns)
|
function yy_r1_3($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::FROM;
|
$this->token = OQLParser::AS_ALIAS;
|
||||||
}
|
}
|
||||||
function yy_r1_4($yy_subpatterns)
|
function yy_r1_4($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::AS_ALIAS;
|
$this->token = OQLParser::WHERE;
|
||||||
}
|
}
|
||||||
function yy_r1_5($yy_subpatterns)
|
function yy_r1_5($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::WHERE;
|
$this->token = OQLParser::JOIN;
|
||||||
}
|
}
|
||||||
function yy_r1_6($yy_subpatterns)
|
function yy_r1_6($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::JOIN;
|
$this->token = OQLParser::ON;
|
||||||
}
|
}
|
||||||
function yy_r1_7($yy_subpatterns)
|
function yy_r1_7($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::ON;
|
$this->token = OQLParser::MATH_DIV;
|
||||||
}
|
}
|
||||||
function yy_r1_8($yy_subpatterns)
|
function yy_r1_8($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::MATH_DIV;
|
$this->token = OQLParser::MATH_MULT;
|
||||||
}
|
}
|
||||||
function yy_r1_9($yy_subpatterns)
|
function yy_r1_9($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::MATH_MULT;
|
$this->token = OQLParser::MATH_PLUS;
|
||||||
}
|
}
|
||||||
function yy_r1_10($yy_subpatterns)
|
function yy_r1_10($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::MATH_PLUS;
|
$this->token = OQLParser::MATH_MINUS;
|
||||||
}
|
}
|
||||||
function yy_r1_11($yy_subpatterns)
|
function yy_r1_11($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::MATH_MINUS;
|
$this->token = OQLParser::LOG_AND;
|
||||||
}
|
}
|
||||||
function yy_r1_12($yy_subpatterns)
|
function yy_r1_12($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::LOG_AND;
|
$this->token = OQLParser::LOG_OR;
|
||||||
}
|
}
|
||||||
function yy_r1_13($yy_subpatterns)
|
function yy_r1_13($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::LOG_OR;
|
$this->token = OQLParser::BITWISE_OR;
|
||||||
}
|
}
|
||||||
function yy_r1_14($yy_subpatterns)
|
function yy_r1_14($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::BITWISE_OR;
|
$this->token = OQLParser::BITWISE_AND;
|
||||||
}
|
}
|
||||||
function yy_r1_15($yy_subpatterns)
|
function yy_r1_15($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::BITWISE_AND;
|
$this->token = OQLParser::BITWISE_XOR;
|
||||||
}
|
}
|
||||||
function yy_r1_16($yy_subpatterns)
|
function yy_r1_16($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::BITWISE_XOR;
|
$this->token = OQLParser::BITWISE_LEFT_SHIFT;
|
||||||
}
|
}
|
||||||
function yy_r1_17($yy_subpatterns)
|
function yy_r1_17($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::BITWISE_LEFT_SHIFT;
|
$this->token = OQLParser::BITWISE_RIGHT_SHIFT;
|
||||||
}
|
}
|
||||||
function yy_r1_18($yy_subpatterns)
|
function yy_r1_18($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::BITWISE_RIGHT_SHIFT;
|
$this->token = OQLParser::COMA;
|
||||||
}
|
}
|
||||||
function yy_r1_19($yy_subpatterns)
|
function yy_r1_19($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::COMA;
|
$this->token = OQLParser::PAR_OPEN;
|
||||||
}
|
}
|
||||||
function yy_r1_20($yy_subpatterns)
|
function yy_r1_20($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::PAR_OPEN;
|
$this->token = OQLParser::PAR_CLOSE;
|
||||||
}
|
}
|
||||||
function yy_r1_21($yy_subpatterns)
|
function yy_r1_21($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::PAR_CLOSE;
|
$this->token = OQLParser::REGEXP;
|
||||||
}
|
}
|
||||||
function yy_r1_22($yy_subpatterns)
|
function yy_r1_22($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::REGEXP;
|
$this->token = OQLParser::EQ;
|
||||||
}
|
}
|
||||||
function yy_r1_23($yy_subpatterns)
|
function yy_r1_23($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::EQ;
|
$this->token = OQLParser::NOT_EQ;
|
||||||
}
|
}
|
||||||
function yy_r1_24($yy_subpatterns)
|
function yy_r1_24($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::NOT_EQ;
|
$this->token = OQLParser::GT;
|
||||||
}
|
}
|
||||||
function yy_r1_25($yy_subpatterns)
|
function yy_r1_25($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::GT;
|
$this->token = OQLParser::LT;
|
||||||
}
|
}
|
||||||
function yy_r1_26($yy_subpatterns)
|
function yy_r1_26($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::LT;
|
$this->token = OQLParser::GE;
|
||||||
}
|
}
|
||||||
function yy_r1_27($yy_subpatterns)
|
function yy_r1_27($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::GE;
|
$this->token = OQLParser::LE;
|
||||||
}
|
}
|
||||||
function yy_r1_28($yy_subpatterns)
|
function yy_r1_28($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::LE;
|
$this->token = OQLParser::LIKE;
|
||||||
}
|
}
|
||||||
function yy_r1_29($yy_subpatterns)
|
function yy_r1_29($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::LIKE;
|
$this->token = OQLParser::NOT_LIKE;
|
||||||
}
|
}
|
||||||
function yy_r1_30($yy_subpatterns)
|
function yy_r1_30($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::NOT_LIKE;
|
$this->token = OQLParser::IN;
|
||||||
}
|
}
|
||||||
function yy_r1_31($yy_subpatterns)
|
function yy_r1_31($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::IN;
|
$this->token = OQLParser::NOT_IN;
|
||||||
}
|
}
|
||||||
function yy_r1_32($yy_subpatterns)
|
function yy_r1_32($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::NOT_IN;
|
$this->token = OQLParser::INTERVAL;
|
||||||
}
|
}
|
||||||
function yy_r1_33($yy_subpatterns)
|
function yy_r1_33($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::INTERVAL;
|
$this->token = OQLParser::F_IF;
|
||||||
}
|
}
|
||||||
function yy_r1_34($yy_subpatterns)
|
function yy_r1_34($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_IF;
|
$this->token = OQLParser::F_ELT;
|
||||||
}
|
}
|
||||||
function yy_r1_35($yy_subpatterns)
|
function yy_r1_35($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_ELT;
|
$this->token = OQLParser::F_COALESCE;
|
||||||
}
|
}
|
||||||
function yy_r1_36($yy_subpatterns)
|
function yy_r1_36($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_COALESCE;
|
$this->token = OQLParser::F_ISNULL;
|
||||||
}
|
}
|
||||||
function yy_r1_37($yy_subpatterns)
|
function yy_r1_37($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_ISNULL;
|
$this->token = OQLParser::F_CONCAT;
|
||||||
}
|
}
|
||||||
function yy_r1_38($yy_subpatterns)
|
function yy_r1_38($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_CONCAT;
|
$this->token = OQLParser::F_SUBSTR;
|
||||||
}
|
}
|
||||||
function yy_r1_39($yy_subpatterns)
|
function yy_r1_39($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_SUBSTR;
|
$this->token = OQLParser::F_TRIM;
|
||||||
}
|
}
|
||||||
function yy_r1_40($yy_subpatterns)
|
function yy_r1_40($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_TRIM;
|
$this->token = OQLParser::F_DATE;
|
||||||
}
|
}
|
||||||
function yy_r1_41($yy_subpatterns)
|
function yy_r1_41($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_DATE;
|
$this->token = OQLParser::F_DATE_FORMAT;
|
||||||
}
|
}
|
||||||
function yy_r1_42($yy_subpatterns)
|
function yy_r1_42($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_DATE_FORMAT;
|
$this->token = OQLParser::F_CURRENT_DATE;
|
||||||
}
|
}
|
||||||
function yy_r1_43($yy_subpatterns)
|
function yy_r1_43($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_CURRENT_DATE;
|
$this->token = OQLParser::F_NOW;
|
||||||
}
|
}
|
||||||
function yy_r1_44($yy_subpatterns)
|
function yy_r1_44($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_NOW;
|
$this->token = OQLParser::F_TIME;
|
||||||
}
|
}
|
||||||
function yy_r1_45($yy_subpatterns)
|
function yy_r1_45($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_TIME;
|
$this->token = OQLParser::F_TO_DAYS;
|
||||||
}
|
}
|
||||||
function yy_r1_46($yy_subpatterns)
|
function yy_r1_46($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_TO_DAYS;
|
$this->token = OQLParser::F_FROM_DAYS;
|
||||||
}
|
}
|
||||||
function yy_r1_47($yy_subpatterns)
|
function yy_r1_47($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_FROM_DAYS;
|
$this->token = OQLParser::F_YEAR;
|
||||||
}
|
}
|
||||||
function yy_r1_48($yy_subpatterns)
|
function yy_r1_48($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_YEAR;
|
$this->token = OQLParser::F_MONTH;
|
||||||
}
|
}
|
||||||
function yy_r1_49($yy_subpatterns)
|
function yy_r1_49($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_MONTH;
|
$this->token = OQLParser::F_DAY;
|
||||||
}
|
}
|
||||||
function yy_r1_50($yy_subpatterns)
|
function yy_r1_50($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_DAY;
|
$this->token = OQLParser::F_HOUR;
|
||||||
}
|
}
|
||||||
function yy_r1_51($yy_subpatterns)
|
function yy_r1_51($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_HOUR;
|
$this->token = OQLParser::F_MINUTE;
|
||||||
}
|
}
|
||||||
function yy_r1_52($yy_subpatterns)
|
function yy_r1_52($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_MINUTE;
|
$this->token = OQLParser::F_SECOND;
|
||||||
}
|
}
|
||||||
function yy_r1_53($yy_subpatterns)
|
function yy_r1_53($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_SECOND;
|
$this->token = OQLParser::F_DATE_ADD;
|
||||||
}
|
}
|
||||||
function yy_r1_54($yy_subpatterns)
|
function yy_r1_54($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_DATE_ADD;
|
$this->token = OQLParser::F_DATE_SUB;
|
||||||
}
|
}
|
||||||
function yy_r1_55($yy_subpatterns)
|
function yy_r1_55($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_DATE_SUB;
|
$this->token = OQLParser::F_ROUND;
|
||||||
}
|
}
|
||||||
function yy_r1_56($yy_subpatterns)
|
function yy_r1_56($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_ROUND;
|
$this->token = OQLParser::F_FLOOR;
|
||||||
}
|
}
|
||||||
function yy_r1_57($yy_subpatterns)
|
function yy_r1_57($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_FLOOR;
|
$this->token = OQLParser::F_INET_ATON;
|
||||||
}
|
}
|
||||||
function yy_r1_58($yy_subpatterns)
|
function yy_r1_58($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_INET_ATON;
|
$this->token = OQLParser::F_INET_NTOA;
|
||||||
}
|
}
|
||||||
function yy_r1_59($yy_subpatterns)
|
function yy_r1_59($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::F_INET_NTOA;
|
$this->token = OQLParser::BELOW;
|
||||||
}
|
}
|
||||||
function yy_r1_60($yy_subpatterns)
|
function yy_r1_60($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::BELOW;
|
$this->token = OQLParser::BELOW_STRICT;
|
||||||
}
|
}
|
||||||
function yy_r1_61($yy_subpatterns)
|
function yy_r1_61($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::BELOW_STRICT;
|
$this->token = OQLParser::NOT_BELOW;
|
||||||
}
|
}
|
||||||
function yy_r1_62($yy_subpatterns)
|
function yy_r1_62($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::NOT_BELOW;
|
$this->token = OQLParser::NOT_BELOW_STRICT;
|
||||||
}
|
}
|
||||||
function yy_r1_63($yy_subpatterns)
|
function yy_r1_63($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::NOT_BELOW_STRICT;
|
$this->token = OQLParser::ABOVE;
|
||||||
}
|
}
|
||||||
function yy_r1_64($yy_subpatterns)
|
function yy_r1_64($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::ABOVE;
|
$this->token = OQLParser::ABOVE_STRICT;
|
||||||
}
|
}
|
||||||
function yy_r1_65($yy_subpatterns)
|
function yy_r1_65($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::ABOVE_STRICT;
|
$this->token = OQLParser::NOT_ABOVE;
|
||||||
}
|
}
|
||||||
function yy_r1_66($yy_subpatterns)
|
function yy_r1_66($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::NOT_ABOVE;
|
$this->token = OQLParser::NOT_ABOVE_STRICT;
|
||||||
}
|
}
|
||||||
function yy_r1_67($yy_subpatterns)
|
function yy_r1_67($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::NOT_ABOVE_STRICT;
|
$this->token = OQLParser::HEXVAL;
|
||||||
}
|
}
|
||||||
function yy_r1_68($yy_subpatterns)
|
function yy_r1_68($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::HEXVAL;
|
$this->token = OQLParser::NUMVAL;
|
||||||
}
|
}
|
||||||
function yy_r1_69($yy_subpatterns)
|
function yy_r1_69($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::NUMVAL;
|
$this->token = OQLParser::STRVAL;
|
||||||
}
|
}
|
||||||
function yy_r1_70($yy_subpatterns)
|
function yy_r1_70($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::STRVAL;
|
$this->token = OQLParser::NAME;
|
||||||
}
|
}
|
||||||
function yy_r1_71($yy_subpatterns)
|
function yy_r1_71($yy_subpatterns)
|
||||||
{
|
|
||||||
|
|
||||||
$this->token = OQLParser::NAME;
|
|
||||||
}
|
|
||||||
function yy_r1_72($yy_subpatterns)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::VARNAME;
|
$this->token = OQLParser::VARNAME;
|
||||||
}
|
}
|
||||||
function yy_r1_73($yy_subpatterns)
|
function yy_r1_72($yy_subpatterns)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->token = OQLParser::DOT;
|
$this->token = OQLParser::DOT;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2012 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
/**
|
/**
|
||||||
* OQL syntax analyzer, to be used prior to run the lexical analyzer
|
* OQL syntax analyzer, to be used prior to run the lexical analyzer
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -80,7 +80,6 @@ class OQLLexerRaw
|
|||||||
%line $this->line
|
%line $this->line
|
||||||
%matchlongest 1
|
%matchlongest 1
|
||||||
whitespace = /[ \t\n\r]+/
|
whitespace = /[ \t\n\r]+/
|
||||||
union = "UNION"
|
|
||||||
select = "SELECT"
|
select = "SELECT"
|
||||||
from = "FROM"
|
from = "FROM"
|
||||||
as_alias = "AS"
|
as_alias = "AS"
|
||||||
@@ -177,9 +176,6 @@ dot = "."
|
|||||||
whitespace {
|
whitespace {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
union {
|
|
||||||
$this->token = OQLParser::UNION;
|
|
||||||
}
|
|
||||||
select {
|
select {
|
||||||
$this->token = OQLParser::SELECT;
|
$this->token = OQLParser::SELECT;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -26,17 +26,9 @@ TODO : solve the 2 remaining shift-reduce conflicts (JOIN)
|
|||||||
throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
result ::= union(X). { $this->my_result = X; }
|
|
||||||
result ::= query(X). { $this->my_result = X; }
|
result ::= query(X). { $this->my_result = X; }
|
||||||
result ::= condition(X). { $this->my_result = X; }
|
result ::= condition(X). { $this->my_result = X; }
|
||||||
|
|
||||||
union(A) ::= query(X) UNION query(Y). {
|
|
||||||
A = new OqlUnionQuery(X, Y);
|
|
||||||
}
|
|
||||||
union(A) ::= query(X) UNION union(Y). {
|
|
||||||
A = new OqlUnionQuery(X, Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
query(A) ::= SELECT class_name(X) join_statement(J) where_statement(W). {
|
query(A) ::= SELECT class_name(X) join_statement(J) where_statement(W). {
|
||||||
A = new OqlObjectQuery(X, X, W, J, array(X));
|
A = new OqlObjectQuery(X, X, W, J, array(X));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2012 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
/**
|
/**
|
||||||
* Wrapper to execute the parser, lexical analyzer and normalization of an OQL query
|
* Wrapper to execute the parser, lexical analyzer and normalization of an OQL query
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -83,10 +83,10 @@ class OqlInterpreter
|
|||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ParseQuery()
|
public function ParseObjectQuery()
|
||||||
{
|
{
|
||||||
$oRes = $this->Parse();
|
$oRes = $this->Parse();
|
||||||
if (!$oRes instanceof OqlQuery)
|
if (!$oRes instanceof OqlObjectQuery)
|
||||||
{
|
{
|
||||||
throw new OQLException('Expecting an OQL query', $this->m_sQuery, 0, 0, get_class($oRes));
|
throw new OQLException('Expecting an OQL query', $this->m_sQuery, 0, 0, get_class($oRes));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2013 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -20,20 +20,10 @@
|
|||||||
/**
|
/**
|
||||||
* Classes defined for lexical analyze (see oql-parser.y)
|
* Classes defined for lexical analyze (see oql-parser.y)
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define('TREE_OPERATOR_EQUALS', 0);
|
|
||||||
define('TREE_OPERATOR_BELOW', 1);
|
|
||||||
define('TREE_OPERATOR_BELOW_STRICT', 2);
|
|
||||||
define('TREE_OPERATOR_NOT_BELOW', 3);
|
|
||||||
define('TREE_OPERATOR_NOT_BELOW_STRICT', 4);
|
|
||||||
define('TREE_OPERATOR_ABOVE', 5);
|
|
||||||
define('TREE_OPERATOR_ABOVE_STRICT', 6);
|
|
||||||
define('TREE_OPERATOR_NOT_ABOVE', 7);
|
|
||||||
define('TREE_OPERATOR_NOT_ABOVE_STRICT', 8);
|
|
||||||
|
|
||||||
// Position a string within an OQL query
|
// Position a string within an OQL query
|
||||||
// This is a must if we want to be able to pinpoint an error at any stage of the query interpretation
|
// This is a must if we want to be able to pinpoint an error at any stage of the query interpretation
|
||||||
// In particular, the normalization phase requires this
|
// In particular, the normalization phase requires this
|
||||||
@@ -276,35 +266,23 @@ class IntervalOqlExpression extends IntervalExpression implements CheckableExpre
|
|||||||
|
|
||||||
abstract class OqlQuery
|
abstract class OqlQuery
|
||||||
{
|
{
|
||||||
public function __construct()
|
protected $m_aJoins; // array of OqlJoinSpec
|
||||||
|
protected $m_oCondition; // condition tree (expressions)
|
||||||
|
|
||||||
|
public function __construct($oCondition = null, $aJoins = null)
|
||||||
{
|
{
|
||||||
|
$this->m_aJoins = $aJoins;
|
||||||
|
$this->m_oCondition = $oCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function GetJoins()
|
||||||
* Check the validity of the expression with regard to the data model
|
{
|
||||||
* and the query in which it is used
|
return $this->m_aJoins;
|
||||||
*
|
}
|
||||||
* @param ModelReflection $oModelReflection MetaModel to consider
|
public function GetCondition()
|
||||||
* @throws OqlNormalizeException
|
{
|
||||||
*/
|
return $this->m_oCondition;
|
||||||
abstract public function Check(ModelReflection $oModelReflection, $sSourceQuery);
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the class
|
|
||||||
*
|
|
||||||
* @param ModelReflection $oModelReflection MetaModel to consider
|
|
||||||
* @return string
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
abstract public function GetClass(ModelReflection $oModelReflection);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the class alias
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
abstract public function GetClassAlias();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class OqlObjectQuery extends OqlQuery
|
class OqlObjectQuery extends OqlQuery
|
||||||
@@ -312,43 +290,23 @@ class OqlObjectQuery extends OqlQuery
|
|||||||
protected $m_aSelect; // array of selected classes
|
protected $m_aSelect; // array of selected classes
|
||||||
protected $m_oClass;
|
protected $m_oClass;
|
||||||
protected $m_oClassAlias;
|
protected $m_oClassAlias;
|
||||||
protected $m_aJoins; // array of OqlJoinSpec
|
|
||||||
protected $m_oCondition; // condition tree (expressions)
|
|
||||||
|
|
||||||
public function __construct($oClass, $oClassAlias, $oCondition = null, $aJoins = null, $aSelect = null)
|
public function __construct($oClass, $oClassAlias, $oCondition = null, $aJoins = null, $aSelect = null)
|
||||||
{
|
{
|
||||||
$this->m_aSelect = $aSelect;
|
$this->m_aSelect = $aSelect;
|
||||||
$this->m_oClass = $oClass;
|
$this->m_oClass = $oClass;
|
||||||
$this->m_oClassAlias = $oClassAlias;
|
$this->m_oClassAlias = $oClassAlias;
|
||||||
$this->m_aJoins = $aJoins;
|
parent::__construct($oCondition, $aJoins);
|
||||||
$this->m_oCondition = $oCondition;
|
|
||||||
|
|
||||||
parent::__construct();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetSelectedClasses()
|
public function GetSelectedClasses()
|
||||||
{
|
{
|
||||||
return $this->m_aSelect;
|
return $this->m_aSelect;
|
||||||
}
|
}
|
||||||
|
public function GetClass()
|
||||||
/**
|
|
||||||
* Determine the class
|
|
||||||
*
|
|
||||||
* @param ModelReflection $oModelReflection MetaModel to consider
|
|
||||||
* @return string
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function GetClass(ModelReflection $oModelReflection)
|
|
||||||
{
|
{
|
||||||
return $this->m_oClass->GetValue();
|
return $this->m_oClass->GetValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the class alias
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function GetClassAlias()
|
public function GetClassAlias()
|
||||||
{
|
{
|
||||||
return $this->m_oClassAlias->GetValue();
|
return $this->m_oClassAlias->GetValue();
|
||||||
@@ -363,15 +321,6 @@ class OqlObjectQuery extends OqlQuery
|
|||||||
return $this->m_oClassAlias;
|
return $this->m_oClassAlias;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetJoins()
|
|
||||||
{
|
|
||||||
return $this->m_aJoins;
|
|
||||||
}
|
|
||||||
public function GetCondition()
|
|
||||||
{
|
|
||||||
return $this->m_oCondition;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively check the validity of the expression with regard to the data model
|
* Recursively check the validity of the expression with regard to the data model
|
||||||
* and the query in which it is used
|
* and the query in which it is used
|
||||||
@@ -381,7 +330,7 @@ class OqlObjectQuery extends OqlQuery
|
|||||||
*/
|
*/
|
||||||
public function Check(ModelReflection $oModelReflection, $sSourceQuery)
|
public function Check(ModelReflection $oModelReflection, $sSourceQuery)
|
||||||
{
|
{
|
||||||
$sClass = $this->GetClass($oModelReflection);
|
$sClass = $this->GetClass();
|
||||||
$sClassAlias = $this->GetClassAlias();
|
$sClassAlias = $this->GetClassAlias();
|
||||||
|
|
||||||
if (!$oModelReflection->IsValidClass($sClass))
|
if (!$oModelReflection->IsValidClass($sClass))
|
||||||
@@ -439,22 +388,17 @@ class OqlObjectQuery extends OqlQuery
|
|||||||
throw new OqlNormalizeException('Unknown class in join condition (right expression)', $sSourceQuery, $oRightField->GetParentDetails(), array_keys($aAliases));
|
throw new OqlNormalizeException('Unknown class in join condition (right expression)', $sSourceQuery, $oRightField->GetParentDetails(), array_keys($aAliases));
|
||||||
}
|
}
|
||||||
$aExtKeys = $oModelReflection->ListAttributes($aAliases[$sFromClass], 'AttributeExternalKey');
|
$aExtKeys = $oModelReflection->ListAttributes($aAliases[$sFromClass], 'AttributeExternalKey');
|
||||||
$aObjKeys = $oModelReflection->ListAttributes($aAliases[$sFromClass], 'AttributeObjectKey');
|
if (!array_key_exists($sExtKeyAttCode, $aExtKeys))
|
||||||
$aAllKeys = array_merge($aExtKeys, $aObjKeys);
|
|
||||||
if (!array_key_exists($sExtKeyAttCode, $aAllKeys))
|
|
||||||
{
|
{
|
||||||
throw new OqlNormalizeException('Unknown key in join condition (left expression)', $sSourceQuery, $oLeftField->GetNameDetails(), array_keys($aAllKeys));
|
throw new OqlNormalizeException('Unknown external key in join condition (left expression)', $sSourceQuery, $oLeftField->GetNameDetails(), array_keys($aExtKeys));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($sFromClass == $sJoinClassAlias)
|
if ($sFromClass == $sJoinClassAlias)
|
||||||
{
|
{
|
||||||
if (array_key_exists($sExtKeyAttCode, $aExtKeys)) // Skip that check for object keys
|
$sTargetClass = $oModelReflection->GetAttributeProperty($aAliases[$sFromClass], $sExtKeyAttCode, 'targetclass');
|
||||||
|
if(!$oModelReflection->IsSameFamilyBranch($aAliases[$sToClass], $sTargetClass))
|
||||||
{
|
{
|
||||||
$sTargetClass = $oModelReflection->GetAttributeProperty($aAliases[$sFromClass], $sExtKeyAttCode, 'targetclass');
|
throw new OqlNormalizeException("The joined class ($aAliases[$sFromClass]) is not compatible with the external key, which is pointing to $sTargetClass", $sSourceQuery, $oLeftField->GetNameDetails());
|
||||||
if(!$oModelReflection->IsSameFamilyBranch($aAliases[$sToClass], $sTargetClass))
|
|
||||||
{
|
|
||||||
throw new OqlNormalizeException("The joined class ($aAliases[$sFromClass]) is not compatible with the external key, which is pointing to $sTargetClass", $sSourceQuery, $oLeftField->GetNameDetails());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -490,13 +434,10 @@ class OqlObjectQuery extends OqlQuery
|
|||||||
$iOperatorCode = TREE_OPERATOR_NOT_ABOVE_STRICT;
|
$iOperatorCode = TREE_OPERATOR_NOT_ABOVE_STRICT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (array_key_exists($sExtKeyAttCode, $aExtKeys)) // Skip that check for object keys
|
$sTargetClass = $oModelReflection->GetAttributeProperty($aAliases[$sFromClass], $sExtKeyAttCode, 'targetclass');
|
||||||
|
if(!$oModelReflection->IsSameFamilyBranch($aAliases[$sToClass], $sTargetClass))
|
||||||
{
|
{
|
||||||
$sTargetClass = $oModelReflection->GetAttributeProperty($aAliases[$sFromClass], $sExtKeyAttCode, 'targetclass');
|
throw new OqlNormalizeException("The joined class ($aAliases[$sToClass]) is not compatible with the external key, which is pointing to $sTargetClass", $sSourceQuery, $oLeftField->GetNameDetails());
|
||||||
if(!$oModelReflection->IsSameFamilyBranch($aAliases[$sToClass], $sTargetClass))
|
|
||||||
{
|
|
||||||
throw new OqlNormalizeException("The joined class ($aAliases[$sToClass]) is not compatible with the external key, which is pointing to $sTargetClass", $sSourceQuery, $oLeftField->GetNameDetails());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$aAttList = $oModelReflection->ListAttributes($aAliases[$sFromClass]);
|
$aAttList = $oModelReflection->ListAttributes($aAliases[$sFromClass]);
|
||||||
$sAttType = $aAttList[$sExtKeyAttCode];
|
$sAttType = $aAttList[$sExtKeyAttCode];
|
||||||
@@ -510,6 +451,7 @@ class OqlObjectQuery extends OqlQuery
|
|||||||
|
|
||||||
// Check the select information
|
// Check the select information
|
||||||
//
|
//
|
||||||
|
$aSelected = array();
|
||||||
foreach ($this->GetSelectedClasses() as $oClassDetails)
|
foreach ($this->GetSelectedClasses() as $oClassDetails)
|
||||||
{
|
{
|
||||||
$sClassToSelect = $oClassDetails->GetValue();
|
$sClassToSelect = $oClassDetails->GetValue();
|
||||||
@@ -517,6 +459,7 @@ class OqlObjectQuery extends OqlQuery
|
|||||||
{
|
{
|
||||||
throw new OqlNormalizeException('Unknown class [alias]', $sSourceQuery, $oClassDetails, array_keys($aAliases));
|
throw new OqlNormalizeException('Unknown class [alias]', $sSourceQuery, $oClassDetails, array_keys($aAliases));
|
||||||
}
|
}
|
||||||
|
$aSelected[$sClassToSelect] = $aAliases[$sClassToSelect];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the condition tree
|
// Check the condition tree
|
||||||
@@ -526,222 +469,6 @@ class OqlObjectQuery extends OqlQuery
|
|||||||
$this->m_oCondition->Check($oModelReflection, $aAliases, $sSourceQuery);
|
$this->m_oCondition->Check($oModelReflection, $aAliases, $sSourceQuery);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Make the relevant DBSearch instance (FromOQL)
|
|
||||||
*/
|
|
||||||
public function ToDBSearch($sQuery)
|
|
||||||
{
|
|
||||||
$sClass = $this->GetClass(new ModelReflectionRuntime());
|
|
||||||
$sClassAlias = $this->GetClassAlias();
|
|
||||||
|
|
||||||
$oSearch = new DBObjectSearch($sClass, $sClassAlias);
|
|
||||||
$oSearch->InitFromOqlQuery($this, $sQuery);
|
|
||||||
return $oSearch;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class OqlUnionQuery extends OqlQuery
|
?>
|
||||||
{
|
|
||||||
protected $aQueries;
|
|
||||||
|
|
||||||
public function __construct(OqlObjectQuery $oLeftQuery, OqlQuery $oRightQueryOrUnion)
|
|
||||||
{
|
|
||||||
$this->aQueries[] = $oLeftQuery;
|
|
||||||
if ($oRightQueryOrUnion instanceof OqlUnionQuery)
|
|
||||||
{
|
|
||||||
foreach ($oRightQueryOrUnion->GetQueries() as $oSingleQuery)
|
|
||||||
{
|
|
||||||
$this->aQueries[] = $oSingleQuery;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->aQueries[] = $oRightQueryOrUnion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetQueries()
|
|
||||||
{
|
|
||||||
return $this->aQueries;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check the validity of the expression with regard to the data model
|
|
||||||
* and the query in which it is used
|
|
||||||
*
|
|
||||||
* @param ModelReflection $oModelReflection MetaModel to consider
|
|
||||||
* @throws OqlNormalizeException
|
|
||||||
*/
|
|
||||||
public function Check(ModelReflection $oModelReflection, $sSourceQuery)
|
|
||||||
{
|
|
||||||
$aColumnToClasses = array();
|
|
||||||
foreach ($this->aQueries as $iQuery => $oQuery)
|
|
||||||
{
|
|
||||||
$oQuery->Check($oModelReflection, $sSourceQuery);
|
|
||||||
|
|
||||||
$aAliasToClass = array($oQuery->GetClassAlias() => $oQuery->GetClass($oModelReflection));
|
|
||||||
$aJoinSpecs = $oQuery->GetJoins();
|
|
||||||
if (is_array($aJoinSpecs))
|
|
||||||
{
|
|
||||||
foreach ($aJoinSpecs as $oJoinSpec)
|
|
||||||
{
|
|
||||||
$aAliasToClass[$oJoinSpec->GetClassAlias()] = $oJoinSpec->GetClass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$aSelectedClasses = $oQuery->GetSelectedClasses();
|
|
||||||
if ($iQuery != 0)
|
|
||||||
{
|
|
||||||
if (count($aSelectedClasses) < count($aColumnToClasses))
|
|
||||||
{
|
|
||||||
$oLastClass = end($aSelectedClasses);
|
|
||||||
throw new OqlNormalizeException('Too few selected classes in the subquery', $sSourceQuery, $oLastClass);
|
|
||||||
}
|
|
||||||
if (count($aSelectedClasses) > count($aColumnToClasses))
|
|
||||||
{
|
|
||||||
$oLastClass = end($aSelectedClasses);
|
|
||||||
throw new OqlNormalizeException('Too many selected classes in the subquery', $sSourceQuery, $oLastClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ($aSelectedClasses as $iColumn => $oClassDetails)
|
|
||||||
{
|
|
||||||
$sAlias = $oClassDetails->GetValue();
|
|
||||||
$sClass = $aAliasToClass[$sAlias];
|
|
||||||
$aColumnToClasses[$iColumn][] = array(
|
|
||||||
'alias' => $sAlias,
|
|
||||||
'class' => $sClass,
|
|
||||||
'class_name' => $oClassDetails,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ($aColumnToClasses as $iColumn => $aClasses)
|
|
||||||
{
|
|
||||||
foreach ($aClasses as $iQuery => $aData)
|
|
||||||
{
|
|
||||||
if ($iQuery == 0)
|
|
||||||
{
|
|
||||||
// Establish the reference
|
|
||||||
$sRootClass = $oModelReflection->GetRootClass($aData['class']);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ($oModelReflection->GetRootClass($aData['class']) != $sRootClass)
|
|
||||||
{
|
|
||||||
$aSubclasses = $oModelReflection->EnumChildClasses($sRootClass, ENUM_CHILD_CLASSES_ALL);
|
|
||||||
throw new OqlNormalizeException('Incompatible classes: could not find a common ancestor', $sSourceQuery, $aData['class_name'], $aSubclasses);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the class
|
|
||||||
*
|
|
||||||
* @param ModelReflection $oModelReflection MetaModel to consider
|
|
||||||
* @return string
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function GetClass(ModelReflection $oModelReflection)
|
|
||||||
{
|
|
||||||
$aFirstColClasses = array();
|
|
||||||
foreach ($this->aQueries as $iQuery => $oQuery)
|
|
||||||
{
|
|
||||||
$aFirstColClasses[] = $oQuery->GetClass($oModelReflection);
|
|
||||||
}
|
|
||||||
$sClass = self::GetLowestCommonAncestor($oModelReflection, $aFirstColClasses);
|
|
||||||
if (is_null($sClass))
|
|
||||||
{
|
|
||||||
throw new Exception('Could not determine the class of the union query. This issue should have been detected earlier by calling OqlQuery::Check()');
|
|
||||||
}
|
|
||||||
return $sClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the class alias
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function GetClassAlias()
|
|
||||||
{
|
|
||||||
$sAlias = $this->aQueries[0]->GetClassAlias();
|
|
||||||
return $sAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check the validity of the expression with regard to the data model
|
|
||||||
* and the query in which it is used
|
|
||||||
*
|
|
||||||
* @param ModelReflection $oModelReflection MetaModel to consider
|
|
||||||
* @param array $aClasses Flat list of classes
|
|
||||||
* @return string the lowest common ancestor amongst classes, null if none has been found
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public static function GetLowestCommonAncestor(ModelReflection $oModelReflection, $aClasses)
|
|
||||||
{
|
|
||||||
$sAncestor = null;
|
|
||||||
foreach($aClasses as $sClass)
|
|
||||||
{
|
|
||||||
if (is_null($sAncestor))
|
|
||||||
{
|
|
||||||
// first loop
|
|
||||||
$sAncestor = $sClass;
|
|
||||||
}
|
|
||||||
elseif ($sClass == $sAncestor)
|
|
||||||
{
|
|
||||||
// remains the same
|
|
||||||
}
|
|
||||||
elseif ($oModelReflection->GetRootClass($sClass) != $oModelReflection->GetRootClass($sAncestor))
|
|
||||||
{
|
|
||||||
$sAncestor = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sAncestor = self::LowestCommonAncestor($oModelReflection, $sAncestor, $sClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $sAncestor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Note: assumes that class A and B have a common ancestor
|
|
||||||
*/
|
|
||||||
protected static function LowestCommonAncestor(ModelReflection $oModelReflection, $sClassA, $sClassB)
|
|
||||||
{
|
|
||||||
if ($sClassA == $sClassB)
|
|
||||||
{
|
|
||||||
$sRet = $sClassA;
|
|
||||||
}
|
|
||||||
elseif (in_array($sClassA, $oModelReflection->EnumChildClasses($sClassB)))
|
|
||||||
{
|
|
||||||
$sRet = $sClassB;
|
|
||||||
}
|
|
||||||
elseif (in_array($sClassB, $oModelReflection->EnumChildClasses($sClassA)))
|
|
||||||
{
|
|
||||||
$sRet = $sClassA;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Recurse
|
|
||||||
$sRet = self::LowestCommonAncestor($oModelReflection, $sClassA, $oModelReflection->GetParentClass($sClassB));
|
|
||||||
}
|
|
||||||
return $sRet;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Make the relevant DBSearch instance (FromOQL)
|
|
||||||
*/
|
|
||||||
public function ToDBSearch($sQuery)
|
|
||||||
{
|
|
||||||
$aSearches = array();
|
|
||||||
foreach ($this->aQueries as $oQuery)
|
|
||||||
{
|
|
||||||
$aSearches[] = $oQuery->ToDBSearch($sQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
$oSearch = new DBUnionSearch($aSearches);
|
|
||||||
return $oSearch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
2015-08-31
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2012 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -23,7 +23,7 @@ define('CASELOG_SEPARATOR', "\n".'========== %1$s : %2$s (%3$d) ============'."\
|
|||||||
/**
|
/**
|
||||||
* Class to store a "case log" in a structured way, keeping track of its successive entries
|
* Class to store a "case log" in a structured way, keeping track of its successive entries
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
class ormCaseLog {
|
class ormCaseLog {
|
||||||
@@ -212,88 +212,11 @@ class ormCaseLog {
|
|||||||
return $sHtml;
|
return $sHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Produces an HTML representation, aimed at being used to produce a PDF with TCPDF (no table)
|
|
||||||
*/
|
|
||||||
public function GetAsSimpleHtml()
|
|
||||||
{
|
|
||||||
$sStyleCaseLogHeader = '';
|
|
||||||
$sStyleCaseLogEntry = '';
|
|
||||||
|
|
||||||
$sHtml = '<ul class="case_log_simple_html">';
|
|
||||||
$iPos = 0;
|
|
||||||
$aIndex = $this->m_aIndex;
|
|
||||||
for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
|
|
||||||
{
|
|
||||||
$iPos += $aIndex[$index]['separator_length'];
|
|
||||||
$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
|
|
||||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
|
||||||
$iPos += $aIndex[$index]['text_length'];
|
|
||||||
|
|
||||||
$sEntry = '<li>';
|
|
||||||
// Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
|
|
||||||
// therefore we have changed the format. To preserve the compatibility with existing
|
|
||||||
// installations of iTop, both format are allowed:
|
|
||||||
// the 'date' item is either a DateTime object, or a unix timestamp
|
|
||||||
if (is_int($aIndex[$index]['date']))
|
|
||||||
{
|
|
||||||
// Unix timestamp
|
|
||||||
$sDate = date(Dict::S('UI:CaseLog:DateFormat'),$aIndex[$index]['date']);
|
|
||||||
}
|
|
||||||
elseif (is_object($aIndex[$index]['date']))
|
|
||||||
{
|
|
||||||
if (version_compare(phpversion(), '5.3.0', '>='))
|
|
||||||
{
|
|
||||||
// DateTime
|
|
||||||
$sDate = $aIndex[$index]['date']->format(Dict::S('UI:CaseLog:DateFormat'));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// No Warning... but the date is unknown
|
|
||||||
$sDate = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$sEntry .= sprintf(Dict::S('UI:CaseLog:Header_Date_UserName'), '<span class="caselog_header_date">'.$sDate.'</span>', '<span class="caselog_header_user">'.$aIndex[$index]['user_name'].'</span>');
|
|
||||||
$sEntry .= '<div class="case_log_simple_html_entry" style="'.$sStyleCaseLogEntry.'">';
|
|
||||||
$sEntry .= $sTextEntry;
|
|
||||||
$sEntry .= '</div>';
|
|
||||||
$sEntry .= '</li>';
|
|
||||||
$sHtml = $sHtml.$sEntry;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process the case of an eventual remainder (quick migration of AttributeText fields)
|
|
||||||
if ($iPos < (strlen($this->m_sLog) - 1))
|
|
||||||
{
|
|
||||||
$sTextEntry = substr($this->m_sLog, $iPos);
|
|
||||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
|
||||||
|
|
||||||
if (count($this->m_aIndex) == 0)
|
|
||||||
{
|
|
||||||
$sHtml .= '<li>';
|
|
||||||
$sHtml .= $sTextEntry;
|
|
||||||
$sHtml .= '</li>';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sHtml .= '<li>';
|
|
||||||
$sHtml .= Dict::S('UI:CaseLog:InitialValue');
|
|
||||||
$sHtml .= '<div class="case_log_simple_html_entry" style="'.$sStyleCaseLogEntry.'">';
|
|
||||||
$sHtml .= $sTextEntry;
|
|
||||||
$sHtml .= '</div>';
|
|
||||||
$sHtml .= '</li>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$sHtml .= '</ul>';
|
|
||||||
return $sHtml;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces an HTML representation, aimed at being used within the iTop framework
|
* Produces an HTML representation, aimed at being used within the iTop framework
|
||||||
*/
|
*/
|
||||||
public function GetAsHTML(WebPage $oP = null, $bEditMode = false, $aTransfoHandler = null)
|
public function GetAsHTML(WebPage $oP = null, $bEditMode = false, $aTransfoHandler = null)
|
||||||
{
|
{
|
||||||
$bPrintableVersion = (utils::ReadParam('printable', '0') == '1');
|
|
||||||
|
|
||||||
$sHtml = '<table style="width:100%;table-layout:fixed"><tr><td>'; // Use table-layout:fixed to force the with to be independent from the actual content
|
$sHtml = '<table style="width:100%;table-layout:fixed"><tr><td>'; // Use table-layout:fixed to force the with to be independent from the actual content
|
||||||
$iPos = 0;
|
$iPos = 0;
|
||||||
$aIndex = $this->m_aIndex;
|
$aIndex = $this->m_aIndex;
|
||||||
@@ -305,7 +228,7 @@ class ormCaseLog {
|
|||||||
}
|
}
|
||||||
for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
|
for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
|
||||||
{
|
{
|
||||||
if (!$bPrintableVersion && ($index < count($aIndex) - CASELOG_VISIBLE_ITEMS))
|
if ($index < count($aIndex) - CASELOG_VISIBLE_ITEMS)
|
||||||
{
|
{
|
||||||
$sOpen = '';
|
$sOpen = '';
|
||||||
$sDisplay = 'style="display:none;"';
|
$sDisplay = 'style="display:none;"';
|
||||||
@@ -373,7 +296,7 @@ class ormCaseLog {
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!$bPrintableVersion && (count($this->m_aIndex) - CASELOG_VISIBLE_ITEMS > 0))
|
if (count($this->m_aIndex) - CASELOG_VISIBLE_ITEMS > 0)
|
||||||
{
|
{
|
||||||
$sOpen = '';
|
$sOpen = '';
|
||||||
$sDisplay = 'style="display:none;"';
|
$sDisplay = 'style="display:none;"';
|
||||||
@@ -461,7 +384,7 @@ class ormCaseLog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function AddLogEntryFromJSON($oJson, $bCheckUserId = true)
|
public function AddLogEntryFromJSON($oJson)
|
||||||
{
|
{
|
||||||
$sText = isset($oJson->message) ? $oJson->message : '';
|
$sText = isset($oJson->message) ? $oJson->message : '';
|
||||||
|
|
||||||
@@ -471,24 +394,16 @@ class ormCaseLog {
|
|||||||
{
|
{
|
||||||
throw new Exception("Only administrators can set the user id", RestResult::UNAUTHORIZED);
|
throw new Exception("Only administrators can set the user id", RestResult::UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
if ($bCheckUserId && ($oJson->user_id != 0))
|
try
|
||||||
{
|
{
|
||||||
try
|
$oUser = RestUtils::FindObjectFromKey('User', $oJson->user_id);
|
||||||
{
|
|
||||||
$oUser = RestUtils::FindObjectFromKey('User', $oJson->user_id);
|
|
||||||
}
|
|
||||||
catch(Exception $e)
|
|
||||||
{
|
|
||||||
throw new Exception('user_id: '.$e->getMessage(), $e->getCode());
|
|
||||||
}
|
|
||||||
$iUserId = $oUser->GetKey();
|
|
||||||
$sOnBehalfOf = $oUser->GetFriendlyName();
|
|
||||||
}
|
}
|
||||||
else
|
catch(Exception $e)
|
||||||
{
|
{
|
||||||
$iUserId = $oJson->user_id;
|
throw new Exception('user_id: '.$e->getMessage(), $e->getCode());
|
||||||
$sOnBehalfOf = $oJson->user_login;
|
|
||||||
}
|
}
|
||||||
|
$iUserId = $oUser->GetKey();
|
||||||
|
$sOnBehalfOf = $oUser->GetFriendlyName();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -554,26 +469,5 @@ class ormCaseLog {
|
|||||||
$iLast = end($aKeys); // Strict standards: the parameter passed to 'end' must be a variable since it is passed by reference
|
$iLast = end($aKeys); // Strict standards: the parameter passed to 'end' must be a variable since it is passed by reference
|
||||||
return $iLast;
|
return $iLast;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the text string corresponding to the given entry in the log (zero based index, older entries first)
|
|
||||||
* @param integer $iIndex
|
|
||||||
* @return string The text of the entry
|
|
||||||
*/
|
|
||||||
public function GetEntryAt($iIndex)
|
|
||||||
{
|
|
||||||
$iPos = 0;
|
|
||||||
$index = count($this->m_aIndex) - 1;
|
|
||||||
$aIndex = $this->m_aIndex;
|
|
||||||
while($index > $iIndex)
|
|
||||||
{
|
|
||||||
$iPos += $this->m_aIndex[$index]['separator_length'];
|
|
||||||
$iPos += $this->m_aIndex[$index]['text_length'];
|
|
||||||
$index--;
|
|
||||||
}
|
|
||||||
$iPos += $this->m_aIndex[$index]['separator_length'];
|
|
||||||
$sText = substr($this->m_sLog, $iPos, $this->m_aIndex[$index]['text_length']);
|
|
||||||
return $sText;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
@@ -86,9 +86,8 @@ class ormStopWatch
|
|||||||
* Get the working elapsed time since the start of the stop watch
|
* Get the working elapsed time since the start of the stop watch
|
||||||
* even if it is currently running
|
* even if it is currently running
|
||||||
* @param oAttDef AttributeDefinition Attribute hosting the stop watch
|
* @param oAttDef AttributeDefinition Attribute hosting the stop watch
|
||||||
* @param oObject Hosting object (used for query parameters)
|
|
||||||
*/
|
*/
|
||||||
public function GetElapsedTime($oAttDef, $oObject)
|
public function GetElapsedTime($oAttDef)
|
||||||
{
|
{
|
||||||
if (is_null($this->iLastStart))
|
if (is_null($this->iLastStart))
|
||||||
{
|
{
|
||||||
@@ -96,7 +95,7 @@ class ormStopWatch
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$iElapsed = $this->ComputeDuration($oObject, $oAttDef, $this->iLastStart, time());
|
$iElapsed = $this->ComputeDuration($this, $oAttDef, $this->iLastStart, time());
|
||||||
return $this->iTimeSpent + $iElapsed;
|
return $this->iTimeSpent + $iElapsed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -331,7 +330,7 @@ class ormStopWatch
|
|||||||
* It is the responsibility of the caller to compute the deadlines
|
* It is the responsibility of the caller to compute the deadlines
|
||||||
* (to avoid computing twice for the same result)
|
* (to avoid computing twice for the same result)
|
||||||
*/
|
*/
|
||||||
public function Start($oObject, $oAttDef, $iNow = null)
|
public function Start($oObject, $oAttDef)
|
||||||
{
|
{
|
||||||
if (!is_null($this->iLastStart))
|
if (!is_null($this->iLastStart))
|
||||||
{
|
{
|
||||||
@@ -339,16 +338,11 @@ class ormStopWatch
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_null($iNow))
|
|
||||||
{
|
|
||||||
$iNow = time();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_null($this->iStarted))
|
if (is_null($this->iStarted))
|
||||||
{
|
{
|
||||||
$this->iStarted = $iNow;
|
$this->iStarted = time();
|
||||||
}
|
}
|
||||||
$this->iLastStart = $iNow;
|
$this->iLastStart = time();
|
||||||
$this->iStopped = null;
|
$this->iStopped = null;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -412,7 +406,7 @@ class ormStopWatch
|
|||||||
/**
|
/**
|
||||||
* Stop counting if not already done
|
* Stop counting if not already done
|
||||||
*/
|
*/
|
||||||
public function Stop($oObject, $oAttDef, $iNow = null)
|
public function Stop($oObject, $oAttDef)
|
||||||
{
|
{
|
||||||
if (is_null($this->iLastStart))
|
if (is_null($this->iLastStart))
|
||||||
{
|
{
|
||||||
@@ -420,18 +414,13 @@ class ormStopWatch
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_null($iNow))
|
|
||||||
{
|
|
||||||
$iNow = time();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (class_exists('WorkingTimeRecorder'))
|
if (class_exists('WorkingTimeRecorder'))
|
||||||
{
|
{
|
||||||
$sClass = get_class($oObject);
|
$sClass = get_class($oObject);
|
||||||
$sAttCode = $oAttDef->GetCode();
|
$sAttCode = $oAttDef->GetCode();
|
||||||
WorkingTimeRecorder::Start($oObject, $iNow, "ormStopWatch-TimeSpent-$sAttCode", 'Core:ExplainWTC:StopWatch-TimeSpent', array("Class:$sClass/Attribute:$sAttCode"), true /*cumulative*/);
|
WorkingTimeRecorder::Start($oObject, time(), "ormStopWatch-TimeSpent-$sAttCode", 'Core:ExplainWTC:StopWatch-TimeSpent', array("Class:$sClass/Attribute:$sAttCode"), true /*cumulative*/);
|
||||||
}
|
}
|
||||||
$iElapsed = $this->ComputeDuration($oObject, $oAttDef, $this->iLastStart, $iNow);
|
$iElapsed = $this->ComputeDuration($oObject, $oAttDef, $this->iLastStart, time());
|
||||||
$this->iTimeSpent = $this->iTimeSpent + $iElapsed;
|
$this->iTimeSpent = $this->iTimeSpent + $iElapsed;
|
||||||
if (class_exists('WorkingTimeRecorder'))
|
if (class_exists('WorkingTimeRecorder'))
|
||||||
{
|
{
|
||||||
@@ -440,7 +429,7 @@ class ormStopWatch
|
|||||||
|
|
||||||
foreach ($this->aThresholds as $iPercent => &$aThresholdData)
|
foreach ($this->aThresholds as $iPercent => &$aThresholdData)
|
||||||
{
|
{
|
||||||
if (!is_null($aThresholdData['deadline']) && ($iNow > $aThresholdData['deadline']))
|
if (!is_null($aThresholdData['deadline']) && (time() > $aThresholdData['deadline']))
|
||||||
{
|
{
|
||||||
if ($aThresholdData['overrun'] > 0)
|
if ($aThresholdData['overrun'] > 0)
|
||||||
{
|
{
|
||||||
@@ -450,7 +439,7 @@ class ormStopWatch
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// First stop after the deadline has been passed
|
// First stop after the deadline has been passed
|
||||||
$iOverrun = $this->ComputeDuration($oObject, $oAttDef, $aThresholdData['deadline'], $iNow);
|
$iOverrun = $this->ComputeDuration($oObject, $oAttDef, $aThresholdData['deadline'], time());
|
||||||
$aThresholdData['overrun'] = $iOverrun;
|
$aThresholdData['overrun'] = $iOverrun;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -458,7 +447,7 @@ class ormStopWatch
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->iLastStart = null;
|
$this->iLastStart = null;
|
||||||
$this->iStopped = $iNow;
|
$this->iStopped = time();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,352 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Copyright (C) 2015 Combodo SARL
|
|
||||||
//
|
|
||||||
// This file is part of iTop.
|
|
||||||
//
|
|
||||||
// iTop is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// iTop is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mechanism to obtain an exclusive lock while editing an object
|
|
||||||
*
|
|
||||||
* @package iTopORM
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Persistent storage (in the database) for remembering that an object is locked
|
|
||||||
*/
|
|
||||||
class iTopOwnershipToken extends DBObject
|
|
||||||
{
|
|
||||||
public static function Init()
|
|
||||||
{
|
|
||||||
$aParams = array
|
|
||||||
(
|
|
||||||
'category' => 'application',
|
|
||||||
'key_type' => 'autoincrement',
|
|
||||||
'name_attcode' => array('obj_class', 'obj_key'),
|
|
||||||
'state_attcode' => '',
|
|
||||||
'reconc_keys' => array(''),
|
|
||||||
'db_table' => 'priv_ownership_token',
|
|
||||||
'db_key_field' => 'id',
|
|
||||||
'db_finalclass_field' => '',
|
|
||||||
);
|
|
||||||
MetaModel::Init_Params($aParams);
|
|
||||||
MetaModel::Init_InheritAttributes();
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeDateTime("acquired", array("allowed_values"=>null, "sql"=>'acquired', "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeDateTime("last_seen", array("allowed_values"=>null, "sql"=>'last_seen', "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeString("obj_class", array("allowed_values"=>null, "sql"=>'obj_class', "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeInteger("obj_key", array("allowed_values"=>null, "sql"=>'obj_key', "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeString("token", array("allowed_values"=>null, "sql"=>'token', "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "jointype"=> '', "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
|
|
||||||
|
|
||||||
MetaModel::Init_SetZListItems('details', array ('obj_class', 'obj_key', 'last_seen', 'token'));
|
|
||||||
MetaModel::Init_SetZListItems('standard_search', array ('obj_class', 'obj_key', 'last_seen', 'token'));
|
|
||||||
MetaModel::Init_SetZListItems('list', array ('obj_class', 'obj_key', 'last_seen', 'token'));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility class to acquire/extend/release/kill an exclusive lock on a given persistent object,
|
|
||||||
* for example to prevent concurrent edition of the same object.
|
|
||||||
* Each lock has an expiration delay of 120 seconds (tunable via the configuration parameter 'concurrent_lock_expiration_delay')
|
|
||||||
* A watchdog (called twice during this delay) is in charge of keeping the lock "alive" while an object is being edited.
|
|
||||||
*/
|
|
||||||
class iTopOwnershipLock
|
|
||||||
{
|
|
||||||
protected $sObjClass;
|
|
||||||
protected $iObjKey;
|
|
||||||
protected $oToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Acquires an exclusive lock on the specified DBObject. Once acquired, the lock is identified
|
|
||||||
* by a unique "token" string.
|
|
||||||
* @param string $sObjClass The class of the object for which to acquire the lock
|
|
||||||
* @param integer $iObjKey The identifier of the object for which to acquire the lock
|
|
||||||
* @return multitype:boolean iTopOwnershipLock Ambigous <boolean, string, DBObjectSet>
|
|
||||||
*/
|
|
||||||
public static function AcquireLock($sObjClass, $iObjKey)
|
|
||||||
{
|
|
||||||
$oMutex = new iTopMutex('lock_'.$sObjClass.'::'.$iObjKey);
|
|
||||||
|
|
||||||
$oMutex->Lock();
|
|
||||||
$oOwnershipLock = new iTopOwnershipLock($sObjClass, $iObjKey);
|
|
||||||
$token = $oOwnershipLock->Acquire();
|
|
||||||
$oMutex->Unlock();
|
|
||||||
|
|
||||||
return array('success' => $token !== false, 'token' => $token, 'lock' => $oOwnershipLock, 'acquired' => $oOwnershipLock->oToken->Get('acquired'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extends the ownership lock or acquires it if none exists
|
|
||||||
* Returns a hash array with 3 elements:
|
|
||||||
* 'status': either true or false, tells if the lock is still owned
|
|
||||||
* 'owner': is status is false, the User object currently owning the lock
|
|
||||||
* 'operation': whether the lock was 'renewed' (i.e. the lock was valid, its duration has been extended) or 'acquired' (there was no valid lock for this object and a new one was created)
|
|
||||||
* @param string $sToken
|
|
||||||
* @return multitype:boolean string User
|
|
||||||
*/
|
|
||||||
public static function ExtendLock($sObjClass, $iObjKey, $sToken)
|
|
||||||
{
|
|
||||||
$oMutex = new iTopMutex('lock_'.$sObjClass.'::'.$iObjKey);
|
|
||||||
|
|
||||||
$oMutex->Lock();
|
|
||||||
$oOwnershipLock = new iTopOwnershipLock($sObjClass, $iObjKey);
|
|
||||||
$aResult = $oOwnershipLock->Extend($sToken);
|
|
||||||
$oMutex->Unlock();
|
|
||||||
|
|
||||||
return $aResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Releases the given lock for the specified object
|
|
||||||
*
|
|
||||||
* @param string $sObjClass The class of the object
|
|
||||||
* @param int $iObjKey The identifier of the object
|
|
||||||
* @param string $sToken The string identifying the lock
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public static function ReleaseLock($sObjClass, $iObjKey, $sToken)
|
|
||||||
{
|
|
||||||
$oMutex = new iTopMutex('lock_'.$sObjClass.'::'.$iObjKey);
|
|
||||||
|
|
||||||
$oMutex->Lock();
|
|
||||||
$oOwnershipLock = new iTopOwnershipLock($sObjClass, $iObjKey);
|
|
||||||
$bResult = $oOwnershipLock->Release($sToken);
|
|
||||||
self::DeleteExpiredLocks(); // Cleanup orphan locks
|
|
||||||
$oMutex->Unlock();
|
|
||||||
|
|
||||||
return $bResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kills the lock for the specified object
|
|
||||||
*
|
|
||||||
* @param string $sObjClass The class of the object
|
|
||||||
* @param int $iObjKey The identifier of the object
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public static function KillLock($sObjClass, $iObjKey)
|
|
||||||
{
|
|
||||||
$oMutex = new iTopMutex('lock_'.$sObjClass.'::'.$iObjKey);
|
|
||||||
|
|
||||||
$oMutex->Lock();
|
|
||||||
$sOQL = "SELECT iTopOwnershipToken WHERE obj_class = :obj_class AND obj_key = :obj_key";
|
|
||||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL, array('obj_class' => $sObjClass, 'obj_key' => $iObjKey)));
|
|
||||||
while($oLock = $oSet->Fetch())
|
|
||||||
{
|
|
||||||
$oLock->DBDelete();
|
|
||||||
}
|
|
||||||
$oMutex->Unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if an exclusive lock exists on the specified DBObject.
|
|
||||||
* @param string $sObjClass The class of the object for which to acquire the lock
|
|
||||||
* @param integer $iObjKey The identifier of the object for which to acquire the lock
|
|
||||||
* @return multitype:boolean iTopOwnershipLock Ambigous <boolean, string, DBObjectSet>
|
|
||||||
*/
|
|
||||||
public static function IsLocked($sObjClass, $iObjKey)
|
|
||||||
{
|
|
||||||
$bLocked = false;
|
|
||||||
$oMutex = new iTopMutex('lock_'.$sObjClass.'::'.$iObjKey);
|
|
||||||
|
|
||||||
$oMutex->Lock();
|
|
||||||
$oOwnershipLock = new iTopOwnershipLock($sObjClass, $iObjKey);
|
|
||||||
if ($oOwnershipLock->IsOwned())
|
|
||||||
{
|
|
||||||
$bLocked = true;
|
|
||||||
}
|
|
||||||
$oMutex->Unlock();
|
|
||||||
|
|
||||||
return array('locked' =>$bLocked, 'owner' => $oOwnershipLock->GetOwner());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current owner of the lock
|
|
||||||
* @return User
|
|
||||||
*/
|
|
||||||
public function GetOwner()
|
|
||||||
{
|
|
||||||
if ($this->IsTokenValid())
|
|
||||||
{
|
|
||||||
return MetaModel::GetObject('User', $this->oToken->Get('user_id'), false);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The constructor is protected. Use the static methods AcquireLock / ExtendLock / ReleaseLock / KillLock
|
|
||||||
* which are protected against concurrent access by a Mutex.
|
|
||||||
* @param string $sObjClass The class of the object for which to create a lock
|
|
||||||
* @param integer $iObjKey The identifier of the object for which to create a lock
|
|
||||||
*/
|
|
||||||
protected function __construct($sObjClass, $iObjKey)
|
|
||||||
{
|
|
||||||
$sOQL = "SELECT iTopOwnershipToken WHERE obj_class = :obj_class AND obj_key = :obj_key";
|
|
||||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL, array('obj_class' => $sObjClass, 'obj_key' => $iObjKey)));
|
|
||||||
$this->oToken = $oSet->Fetch();
|
|
||||||
$this->sObjClass = $sObjClass;
|
|
||||||
$this->iObjKey = $iObjKey;
|
|
||||||
// IssueLog::Info("iTopOwnershipLock::__construct($sObjClass, $iObjKey) oToken::".($this->oToken ? $this->oToken->GetKey() : 'null'));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function IsOwned()
|
|
||||||
{
|
|
||||||
return $this->IsTokenValid();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function Acquire($sToken = null)
|
|
||||||
{
|
|
||||||
if ($this->IsTokenValid())
|
|
||||||
{
|
|
||||||
// IssueLog::Info("Acquire($sToken) returns false");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sToken = $this->TakeOwnership($sToken);
|
|
||||||
// IssueLog::Info("Acquire($sToken) returns $sToken");
|
|
||||||
return $sToken;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extends the ownership lock or acquires it if none exists
|
|
||||||
* Returns a hash array with 3 elements:
|
|
||||||
* 'status': either true or false, tells if the lock is still owned
|
|
||||||
* 'owner': is status is false, the User object currently owning the lock
|
|
||||||
* 'operation': whether the lock was 'renewed' (i.e. the lock was valid, its duration was extended) or 'expired' (there was no valid lock for this object) or 'lost' (someone else grabbed it)
|
|
||||||
* 'acquired': date at which the lock was initially acquired
|
|
||||||
* @param string $sToken
|
|
||||||
* @return multitype:boolean string User
|
|
||||||
*/
|
|
||||||
protected function Extend($sToken)
|
|
||||||
{
|
|
||||||
$aResult = array('status' => true, 'owner' => '', 'operation' => 'renewed');
|
|
||||||
|
|
||||||
if ($this->IsTokenValid())
|
|
||||||
{
|
|
||||||
if ($sToken === $this->oToken->Get('token'))
|
|
||||||
{
|
|
||||||
$this->oToken->Set('last_seen', date('Y-m-d H:i:s'));
|
|
||||||
$this->oToken->DBUpdate();
|
|
||||||
$aResult['acquired'] = $this->oToken->Get('acquired');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// IssueLog::Info("Extend($sToken) returns false");
|
|
||||||
$aResult['status'] = false;
|
|
||||||
$aResult['operation'] = 'lost';
|
|
||||||
$aResult['owner'] = $this->GetOwner();
|
|
||||||
$aResult['acquired'] = $this->oToken->Get('acquired');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$aResult['status'] = false;
|
|
||||||
$aResult['operation'] = 'expired';
|
|
||||||
}
|
|
||||||
// IssueLog::Info("Extend($sToken) returns true");
|
|
||||||
return $aResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function HasOwnership($sToken)
|
|
||||||
{
|
|
||||||
$bRet = false;
|
|
||||||
if ($this->IsTokenValid())
|
|
||||||
{
|
|
||||||
if ($sToken === $this->oToken->Get('token'))
|
|
||||||
{
|
|
||||||
$bRet = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// IssueLog::Info("HasOwnership($sToken) return $bRet");
|
|
||||||
return $bRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function Release($sToken)
|
|
||||||
{
|
|
||||||
$bRet = false;
|
|
||||||
// IssueLog::Info("Release... begin [$sToken]");
|
|
||||||
if (($this->oToken) && ($sToken === $this->oToken->Get('token')))
|
|
||||||
{
|
|
||||||
// IssueLog::Info("oToken::".$this->oToken->GetKey().' ('.$sToken.') to be deleted');
|
|
||||||
$this->oToken->DBDelete();
|
|
||||||
// IssueLog::Info("oToken deleted");
|
|
||||||
$this->oToken = null;
|
|
||||||
$bRet = true;
|
|
||||||
}
|
|
||||||
else if ($this->oToken == null)
|
|
||||||
{
|
|
||||||
// IssueLog::Info("Release FAILED oToken == null !!!");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// IssueLog::Info("Release FAILED inconsistent tokens: sToken=\"".$sToken.'", oToken->Get(\'token\')="'.$this->oToken->Get('token').'"');
|
|
||||||
}
|
|
||||||
// IssueLog::Info("Release... end");
|
|
||||||
return $bRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function IsTokenValid()
|
|
||||||
{
|
|
||||||
$bRet = false;
|
|
||||||
if ($this->oToken != null)
|
|
||||||
{
|
|
||||||
$sToken = $this->oToken->Get('token');
|
|
||||||
$sDate = $this->oToken->Get('last_seen');
|
|
||||||
if (($sDate != '') && ($sToken != ''))
|
|
||||||
{
|
|
||||||
$oLastSeenTime = new DateTime($sDate);
|
|
||||||
$iNow = date('U');
|
|
||||||
if (($iNow - $oLastSeenTime->format('U')) < MetaModel::GetConfig()->Get('concurrent_lock_expiration_delay'))
|
|
||||||
{
|
|
||||||
$bRet = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $bRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function TakeOwnership($sToken = null)
|
|
||||||
{
|
|
||||||
if ($this->oToken == null)
|
|
||||||
{
|
|
||||||
$this->oToken = new iTopOwnershipToken();
|
|
||||||
$this->oToken->Set('obj_class', $this->sObjClass);
|
|
||||||
$this->oToken->Set('obj_key', $this->iObjKey);
|
|
||||||
}
|
|
||||||
$this->oToken->Set('acquired', date('Y-m-d H:i:s'));
|
|
||||||
$this->oToken->Set('user_id', UserRights::GetUserId());
|
|
||||||
$this->oToken->Set('last_seen', date('Y-m-d H:i:s'));
|
|
||||||
if ($sToken === null)
|
|
||||||
{
|
|
||||||
$sToken = sprintf('%X', microtime(true));
|
|
||||||
}
|
|
||||||
$this->oToken->Set('token', $sToken);
|
|
||||||
$this->oToken->DBWrite();
|
|
||||||
return $this->oToken->Get('token');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function DeleteExpiredLocks()
|
|
||||||
{
|
|
||||||
$sOQL = "SELECT iTopOwnershipToken WHERE last_seen < :last_seen_limit";
|
|
||||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL, array('last_seen_limit' => date('Y-m-d H:i:s', time() - MetaModel::GetConfig()->Get('concurrent_lock_expiration_delay')))));
|
|
||||||
while($oToken = $oSet->Fetch())
|
|
||||||
{
|
|
||||||
$oToken->DBDelete();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Copyright (C) 2015 Combodo SARL
|
|
||||||
//
|
|
||||||
// This file is part of iTop.
|
|
||||||
//
|
|
||||||
// iTop is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// iTop is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bulk export: PDF export, based on the HTML export converted to PDF
|
|
||||||
*
|
|
||||||
* @copyright Copyright (C) 2015 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
require_once(APPROOT.'application/pdfpage.class.inc.php');
|
|
||||||
|
|
||||||
class PDFBulkExport extends HTMLBulkExport
|
|
||||||
{
|
|
||||||
public function DisplayUsage(Page $oP)
|
|
||||||
{
|
|
||||||
$oP->p(" * pdf format options:");
|
|
||||||
$oP->p(" *\tfields: (mandatory) the comma separated list of field codes to export (e.g: name,org_id,service_name...).");
|
|
||||||
$oP->p(" *\tpage_size: (optional) size of the page. One of A4, A3, Letter (default is 'A4').");
|
|
||||||
$oP->p(" *\tpage_orientation: (optional) the orientation of the page. Either Portrait or Landscape (default is 'Portrait').");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function EnumFormParts()
|
|
||||||
{
|
|
||||||
return array_merge(array('pdf_options' => array('pdf_options')), parent::EnumFormParts());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function DisplayFormPart(WebPage $oP, $sPartId)
|
|
||||||
{
|
|
||||||
switch($sPartId)
|
|
||||||
{
|
|
||||||
case 'pdf_options':
|
|
||||||
$oP->add('<fieldset><legend>'.Dict::S('Core:BulkExport:PDFOptions').'</legend>');
|
|
||||||
$oP->add('<table>');
|
|
||||||
$oP->add('<tr>');
|
|
||||||
$oP->add('<td>'.Dict::S('Core:BulkExport:PDFPageSize').'</td>');
|
|
||||||
$oP->add('<td>'.$this->GetSelectCtrl('page_size', array('A3', 'A4', 'Letter'), 'Core:BulkExport:PageSize-', 'A4').'</td>');
|
|
||||||
$oP->add('</tr>');
|
|
||||||
$oP->add('<td>'.Dict::S('Core:BulkExport:PDFPageOrientation').'</td>');
|
|
||||||
$oP->add('<td>'.$this->GetSelectCtrl('page_orientation', array('P', 'L'), 'Core:BulkExport:PageOrientation-', 'L').'</td>');
|
|
||||||
$oP->add('</tr>');
|
|
||||||
$oP->add('</table>');
|
|
||||||
|
|
||||||
$oP->add('</fieldset>');
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return parent:: DisplayFormPart($oP, $sPartId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function GetSelectCtrl($sName, $aValues, $sDictPrefix, $sDefaultValue)
|
|
||||||
{
|
|
||||||
$sCurrentValue = utils::ReadParam($sName, $sDefaultValue, false, 'raw_data');
|
|
||||||
$aLabels = array();
|
|
||||||
foreach($aValues as $sVal)
|
|
||||||
{
|
|
||||||
$aLabels[$sVal] = Dict::S($sDictPrefix.$sVal);
|
|
||||||
}
|
|
||||||
asort($aLabels);
|
|
||||||
|
|
||||||
$sHtml = '<select name="'.$sName.'">';
|
|
||||||
foreach($aLabels as $sVal => $sLabel)
|
|
||||||
{
|
|
||||||
$sSelected = ($sVal == $sCurrentValue) ? 'selected' : '';
|
|
||||||
$sHtml .= '<option value="'.$sVal.'" '.$sSelected.'>'.htmlentities($sLabel, ENT_QUOTES, 'UTF-8').'</option>';
|
|
||||||
}
|
|
||||||
$sHtml .= '</select>';
|
|
||||||
return $sHtml;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function ReadParameters()
|
|
||||||
{
|
|
||||||
parent::ReadParameters();
|
|
||||||
$this->aStatusInfo['page_size'] = utils::ReadParam('page_size', 'A4', true, 'raw_data');
|
|
||||||
$this->aStatusInfo['page_orientation'] = utils::ReadParam('page_orientation', 'L', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetHeader()
|
|
||||||
{
|
|
||||||
$this->aStatusInfo['tmp_file'] = $this->MakeTmpFile('data');
|
|
||||||
$sData = parent::GetHeader();
|
|
||||||
$hFile = @fopen($this->aStatusInfo['tmp_file'], 'ab');
|
|
||||||
if ($hFile === false)
|
|
||||||
{
|
|
||||||
throw new Exception('PDFBulkExport: Failed to open temporary data file: "'.$this->aStatusInfo['tmp_file'].'" for writing.');
|
|
||||||
}
|
|
||||||
fwrite($hFile, $sData."\n");
|
|
||||||
fclose($hFile);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetNextChunk(&$aStatus)
|
|
||||||
{
|
|
||||||
$sData = parent::GetNextChunk($aStatus);
|
|
||||||
$hFile = @fopen($this->aStatusInfo['tmp_file'], 'ab');
|
|
||||||
if ($hFile === false)
|
|
||||||
{
|
|
||||||
throw new Exception('PDFBulkExport: Failed to open temporary data file: "'.$this->aStatusInfo['tmp_file'].'" for writing.');
|
|
||||||
}
|
|
||||||
fwrite($hFile, $sData."\n");
|
|
||||||
fclose($hFile);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetFooter()
|
|
||||||
{
|
|
||||||
$sData = parent::GetFooter();
|
|
||||||
|
|
||||||
$oPage = new PDFPage(Dict::Format('Core:BulkExportOf_Class', MetaModel::GetName($this->oSearch->GetClass())), $this->aStatusInfo['page_size'], $this->aStatusInfo['page_orientation']);
|
|
||||||
$oPDF = $oPage->get_tcpdf();
|
|
||||||
$oPDF->SetFont('dejavusans', '', 8, '', true);
|
|
||||||
|
|
||||||
$oPage->add(file_get_contents($this->aStatusInfo['tmp_file']));
|
|
||||||
$oPage->add($sData);
|
|
||||||
|
|
||||||
$sPDF = $oPage->get_pdf();
|
|
||||||
|
|
||||||
return $sPDF;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetSupportedFormats()
|
|
||||||
{
|
|
||||||
return array('pdf' => Dict::S('Core:BulkExport:PDFFormat'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetMimeType()
|
|
||||||
{
|
|
||||||
return 'application/x-pdf';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetFileExtension()
|
|
||||||
{
|
|
||||||
return 'pdf';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2015 Combodo SARL
|
// Copyright (C) 2010-2012 Combodo SARL
|
||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
/**
|
/**
|
||||||
* Associated with the metamodel -> MakeQuery/MakeQuerySingleTable
|
* Associated with the metamodel -> MakeQuery/MakeQuerySingleTable
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -29,11 +29,10 @@ class QueryBuilderContext
|
|||||||
protected $m_aClassAliases;
|
protected $m_aClassAliases;
|
||||||
protected $m_aTableAliases;
|
protected $m_aTableAliases;
|
||||||
protected $m_aModifierProperties;
|
protected $m_aModifierProperties;
|
||||||
protected $m_aSelectedClasses;
|
|
||||||
|
|
||||||
public $m_oQBExpressions;
|
public $m_oQBExpressions;
|
||||||
|
|
||||||
public function __construct($oFilter, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null)
|
public function __construct($oFilter, $aModifierProperties, $aGroupByExpr = null)
|
||||||
{
|
{
|
||||||
$this->m_oRootFilter = $oFilter;
|
$this->m_oRootFilter = $oFilter;
|
||||||
$this->m_oQBExpressions = new QueryBuilderExpressions($oFilter, $aGroupByExpr);
|
$this->m_oQBExpressions = new QueryBuilderExpressions($oFilter, $aGroupByExpr);
|
||||||
@@ -42,15 +41,6 @@ class QueryBuilderContext
|
|||||||
$this->m_aTableAliases = array();
|
$this->m_aTableAliases = array();
|
||||||
|
|
||||||
$this->m_aModifierProperties = $aModifierProperties;
|
$this->m_aModifierProperties = $aModifierProperties;
|
||||||
if (is_null($aSelectedClasses))
|
|
||||||
{
|
|
||||||
$this->m_aSelectedClasses = $oFilter->GetSelectedClasses();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// For the unions, the selected classes can be upper in the hierarchy (lowest common ancestor)
|
|
||||||
$this->m_aSelectedClasses = $aSelectedClasses;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetRootFilter()
|
public function GetRootFilter()
|
||||||
@@ -79,9 +69,6 @@ class QueryBuilderContext
|
|||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetSelectedClass($sAlias)
|
|
||||||
{
|
|
||||||
return $this->m_aSelectedClasses[$sAlias];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user