Compare commits

..

32 Commits

Author SHA1 Message Date
Denis Flaven
5a7c17b80a (retrofit from trunk) Properly sanitize the "switch_env" parameter and take it into account only if it contains a valid value.
SVN:2.0.3[4242]
2016-06-22 12:18:14 +00:00
Denis Flaven
0682f9f3b1 Use one-way encryption for storing the token used for the "Forgotten password" feature.
SVN:2.0.3[3924]
2016-02-19 18:23:00 +00:00
Denis Flaven
b2b0ab9eff Fixed a potential XSS vulnerability.
SVN:2.0.3[3665]
2015-07-30 09:14:51 +00:00
Denis Flaven
000dd3726d Enhancement: allow the API to create entries with a specified user_login.
SVN:2.0.3[3517]
2015-03-24 17:12:35 +00:00
Denis Flaven
0a8419c1d8 #594: properly display attachments inside "properties" by closing the span and the fieldset in non-edit mode.
SVN:2.0.3[3513]
2015-03-23 17:57:35 +00:00
Romain Quetiez
61fb800f23 #1060 Internal: improved the symptoms when calling MetaModel::GetAttributeDef with an invalid attribute code (feedback on the class name and no more FATAL errors) -retrofit from trunk
SVN:2.0.3[3494]
2015-02-09 13:16:30 +00:00
Romain Quetiez
ea7a8888e9 Partial retrofit of [3438]: fixes the helper RenameEnumValueInDB which did work only IF the values differ in case (ex. IpPhone to IPPhone)
SVN:2.0.3[3488]
2015-01-09 10:01:24 +00:00
Romain Quetiez
1e66220077 #1020 Restrict dashboard/shortcut refresh interval -retrofit from trunk
SVN:2.0.3[3449]
2014-12-03 14:18:16 +00:00
Romain Quetiez
ad3178d02e #1027 Regression introduced in [3148] thus in 2.0.3 (cache the reconciliation for external keys on the CSV import) a cache hit on an ambiguous external key was not correctly handled -retroffited from trunk
SVN:2.0.3[3441]
2014-12-01 19:21:19 +00:00
Romain Quetiez
028d4b52e4 #1007 Unexpected change of the case log when doing massive update of a User Request (+ hide the checkbox for the status because it makes no sense) -retrofit from trunk
SVN:2.0.3[3395]
2014-10-31 15:11:15 +00:00
Denis Flaven
6d4a78db20 #1011 Proper resizing of the dialog box for managing 1:n links - removed a trace - retrofit from trunk.
SVN:2.0.3[3391]
2014-10-30 08:56:15 +00:00
Denis Flaven
457ee5de64 Fixed the support of a non-default port for MySQL, thanks to theBigOne!
SVN:2.0.3[3369]
2014-10-16 14:34:25 +00:00
Denis Flaven
9bec433e94 #968 (continued) do not loose the "advanced mode" flag in the interactive display (retrofit from trunk).
SVN:2.0.3[3358]
2014-10-02 09:40:29 +00:00
Romain Quetiez
895a5fc5b0 #968 Interactive CSV Export truncated or missing characters (since 2.0.3) -reintegrated from trunk
SVN:2.0.3[3355]
2014-09-25 15:23:43 +00:00
Romain Quetiez
4c735a6dff #991 CSV export truncated (system dependent, since 2.0) due to a bug in iconv, the workaround is to do little by little -retrofit from trunk
SVN:2.0.3[3353]
2014-09-25 15:07:01 +00:00
Denis Flaven
5fd193885e #932: fixed a regression introduced by [r3319]... in case a criteria is present several times, retrofit from trunk.
SVN:2.0.3[3345]
2014-09-17 15:42:55 +00:00
Denis Flaven
9277184ba0 Bug fix: FetchAssoc was broken when dealing with in-memory sets, retrofit from trunk.
SVN:2.0.3[3341]
2014-09-16 09:47:03 +00:00
Romain Quetiez
aa0bd6c1ea #778 Issue on list sort order when editing an element - fixed a regression -reintegrated from trunk
SVN:2.0.3[3336]
2014-09-15 16:08:55 +00:00
Romain Quetiez
d8382a541c #778 Issue on list sort order when editing an element -reintegrated from trunk
SVN:2.0.3[3333]
2014-09-15 14:06:34 +00:00
Romain Quetiez
5e3eab90a6 #986 Search form: handle indirect external keys -reintegrated from trunk
SVN:2.0.3[3331]
2014-09-15 14:02:03 +00:00
Romain Quetiez
2c4c6a6690 #987 Usage login prevents from user deletion -reintegrated from trunk
SVN:2.0.3[3329]
2014-09-15 13:57:51 +00:00
Romain Quetiez
5e600c88a7 #932 Search form should be prefilled when running a search "shortcut" - very little progress: fixed the case when several criteria are given -reintegrated from trunk
SVN:2.0.3[3327]
2014-09-15 13:51:36 +00:00
Romain Quetiez
3c4057d546 #985 Shortcut auto refresh degrading table cosmetics -reintegrated from trunk
SVN:2.0.3[3325]
2014-09-15 13:48:28 +00:00
Romain Quetiez
9071f340a8 #984 Dashboard auto refresh degrading table functionalities like sorting -retrofit from trunk
SVN:2.0.3[3323]
2014-09-15 13:44:59 +00:00
Romain Quetiez
5cb9363cce #976 Fixed a typo introduced in the implementation of the fix -reintegrated from trunk
SVN:2.0.3[3316]
2014-09-11 08:26:08 +00:00
Romain Quetiez
9dd4b05866 Improved the processing of background task to enable more advanced functionalities like queuing (protection against reentrance) -reintegrated from trunk
SVN:2.0.3[3314]
2014-09-03 09:56:44 +00:00
Denis Flaven
4841738f01 Protect dashboards against invalid queries in "grouped by" dashlets.
SVN:2.0.3[3309]
2014-08-28 16:00:31 +00:00
Denis Flaven
8632d5597a #976: make sure that we do not bypass the method that computes the reference for newly created tickets.
SVN:2.0.3[3308]
2014-08-28 15:57:21 +00:00
Romain Quetiez
01e4f6af79 Improved the processing of background task to enable more advanced functionalities like queuing (factorized the error handling) -reintegrated from trunk
SVN:2.0.3[3303]
2014-08-21 08:55:57 +00:00
Romain Quetiez
0a2cdff528 Improved the processing of background task to enable more advanced functionalities like queuing -reintegrated from trunk
SVN:2.0.3[3301]
2014-08-19 10:10:18 +00:00
Romain Quetiez
b5166938df Legacy user rights management: allow the deletion of a profile in one step (it was nearly impossible because of the numerous related records, mainly of type URP_ActionGrant, for which iTop was requesting a manual deletion) -retrofit from trunk
SVN:2.0.3[3279]
2014-07-16 10:54:19 +00:00
Romain Quetiez
441b059581 Releasing 2.0.3
SVN:2.0.3[3276]
2014-07-15 13:36:53 +00:00
852 changed files with 19492 additions and 170869 deletions

View File

@@ -271,19 +271,6 @@ class URP_UserProfile extends UserRightsBaseClassGUI
{
return Dict::Format('UI:UserManagement:LinkBetween_User_And_Profile', $this->Get('userlogin'), $this->Get('profile'));
}
public function CheckToDelete(&$oDeletionPlan)
{
if (MetaModel::GetConfig()->Get('demo_mode'))
{
// Users deletion is NOT allowed in demo mode
$oDeletionPlan->AddToDelete($this, null);
$oDeletionPlan->SetDeletionIssues($this, array('deletion not allowed in demo mode.'), true);
$oDeletionPlan->ComputeResults();
return false;
}
return parent::CheckToDelete($oDeletionPlan);
}
}
class URP_UserOrg extends UserRightsBaseClassGUI

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
@@ -20,7 +20,7 @@
* Simple web page with no includes, header or fancy formatting, useful to
* 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
*/
@@ -42,10 +42,7 @@ class ajax_page extends WebPage implements iTabbedPage
*/
function __construct($s_title)
{
$sPrintable = utils::ReadParam('printable', '0');
$bPrintable = ($sPrintable == '1');
parent::__construct($s_title, $bPrintable);
parent::__construct($s_title);
$this->m_sReadyScript = "";
//$this->add_header("Content-type: text/html; charset=utf-8");
$this->add_header("Cache-control: no-cache");
@@ -200,7 +197,7 @@ EOF
);
}
// 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 ??
if (($this->sContentType == 'text/html') && (preg_match('/class="date-pick"/', $this->s_content) || preg_match('/class="datetime-pick"/', $this->s_content)) )
@@ -228,8 +225,9 @@ EOF
EOF
);
}
$s_captured_output = $this->ob_get_clean_safe();
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'))
$s_captured_output = ob_get_contents();
ob_end_clean();
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'))
{
// inline content != attachment && html => filter all scripts for malicious XSS scripts
echo self::FilterXSS($this->s_content);
@@ -281,9 +279,9 @@ EOF
echo self::FilterXSS($s_captured_output);
}
if (class_exists('DBSearch'))
if (class_exists('MetaModel'))
{
DBSearch::RecordQueryTrace();
MetaModel::RecordQueryTrace();
}
}
@@ -395,3 +393,4 @@ EOF
}
}
?>

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
@@ -781,12 +781,8 @@ class RestUtils
$oSearch = new DBObjectSearch($sClass);
foreach ($oCriteria as $sAttCode => $value)
{
$realValue = static::MakeValue($sClass, $sAttCode, $value);
$realValue = self::MakeValue($sClass, $sAttCode, $value);
$oSearch->AddCondition($sAttCode, $realValue, '=');
if (is_object($value) || is_array($value))
{
$value = json_encode($value);
}
$aCriteriaReport[] = "$sAttCode: $value ($realValue)";
}
$oSet = new DBObjectSet($oSearch);
@@ -818,7 +814,7 @@ class RestUtils
{
if (is_object($key))
{
$res = static::FindObjectFromCriteria($sClass, $key);
$res = self::FindObjectFromCriteria($sClass, $key);
}
elseif (is_numeric($key))
{
@@ -882,7 +878,7 @@ class RestUtils
$oSearch = new DBObjectSearch($sClass);
foreach ($key as $sAttCode => $value)
{
$realValue = static::MakeValue($sClass, $sAttCode, $value);
$realValue = self::MakeValue($sClass, $sAttCode, $value);
$oSearch->AddCondition($sAttCode, $realValue, '=');
}
}
@@ -926,7 +922,7 @@ class RestUtils
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
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;
}
elseif ($oAttDef instanceof AttributeLinkedSet)
@@ -939,7 +935,7 @@ class RestUtils
$aLinks = array();
foreach($value as $oValues)
{
$oLnk = static::MakeObjectFromFields($sLnkClass, $oValues);
$oLnk = self::MakeObjectFromFields($sLnkClass, $oValues);
$aLinks[] = $oLnk;
}
$value = DBObjectSet::FromArray($sLnkClass, $aLinks);
@@ -970,7 +966,7 @@ class RestUtils
$oObject = MetaModel::NewObject($sClass);
foreach ($aFields as $sAttCode => $value)
{
$realValue = static::MakeValue($sClass, $sAttCode, $value);
$realValue = self::MakeValue($sClass, $sAttCode, $value);
try
{
$oObject->Set($sAttCode, $realValue);
@@ -997,7 +993,7 @@ class RestUtils
$sClass = get_class($oObject);
foreach ($aFields as $sAttCode => $value)
{
$realValue = static::MakeValue($sClass, $sAttCode, $value);
$realValue = self::MakeValue($sClass, $sAttCode, $value);
try
{
$oObject->Set($sAttCode, $realValue);

View File

@@ -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("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 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
MetaModel::Init_SetZListItems('details', array('name', 'description', 'definition_set', 'rules_list')); // Attributes to be displayed for the complete details

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
@@ -21,7 +21,7 @@
* CLI page
* 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
*/
@@ -35,9 +35,9 @@ class CLIPage implements Page
public function output()
{
if (class_exists('DBSearch'))
if (class_exists('MetaModel'))
{
DBSearch::RecordQueryTrace();
MetaModel::RecordQueryTrace();
}
if (class_exists('ExecutionKPI'))
{

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2013 Combodo SARL
//
// This file is part of iTop.
//
@@ -21,7 +21,7 @@
* Abstract class that implements some common and useful methods for displaying
* the objects
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @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_NONE', '');
define('MIN_WATCHDOG_INTERVAL', 15); // Minimum interval for the watchdog: 15s
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
require_once(APPROOT.'/application/applicationextension.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
@@ -62,39 +60,6 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
{
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
@@ -125,156 +90,134 @@ EOF
'message' => $sMessage
);
}
}
}
function DisplayBareHeader(WebPage $oPage, $bEditMode = false)
{
// 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();
$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());
if ($aLockInfo['locked'])
{
$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]);
$sMsgClass = 'message_'.$aMessageData['severity'];
$aMessages[] = "<div class=\"header_message $sMsgClass\">".$aMessageData['message']."</div>";
$aRanks[] = $aMessageData['rank'];
}
array_multisort($aRanks, $aMessages);
foreach ($aMessages as $sMessage)
{
$oPage->add($sMessage);
}
unset($_SESSION['obj_messages'][$sMessageKey]);
}
if (!$oPage->IsPrintableVersion())
{
// action menu
$oSingletonFilter = new DBObjectSearch(get_class($this));
$oSingletonFilter->AddCondition('id', $this->GetKey(), '=');
$oBlock = new MenuBlock($oSingletonFilter, 'details', false);
$oBlock->Display($oPage, -1);
}
$oPage->add("<div class=\"page_header\"><h1>".$this->GetIcon()."&nbsp;\n");
// action menu
$oSingletonFilter = new DBObjectSearch(get_class($this));
$oSingletonFilter->AddCondition('id', $this->GetKey(), '=');
$oBlock = new MenuBlock($oSingletonFilter, 'details', false);
$oBlock->Display($oPage, -1);
// Master data sources
$sSynchroIcon = '';
$oReplicaSet = $this->GetMasterReplica();
$bSynchronized = false;
$aIcons = array();
if (!$oPage->IsPrintableVersion())
$oCreatorTask = null;
$bCanBeDeletedByTask = false;
$bCanBeDeletedByUser = true;
$aMasterSources = array();
if ($oReplicaSet->Count() > 0)
{
$oCreatorTask = null;
$bCanBeDeletedByTask = false;
$bCanBeDeletedByUser = true;
$aMasterSources = array();
$aSyncData = $this->GetSynchroData();
if (count($aSyncData) > 0)
$bSynchronized = true;
while($aData = $oReplicaSet->FetchAssoc())
{
$bSynchronized = true;
foreach ($aSyncData as $iSourceId => $aSourceData)
// Assumption: $aData['datasource'] will not be null because the data source id is always set...
$sApplicationURL = $aData['datasource']->GetApplicationUrl($this, $aData['replica']);
$sLink = $aData['datasource']->GetName();
if (!empty($sApplicationURL))
{
$oDataSource = $aSourceData['source'];
$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');
$sLink = "<a href=\"$sApplicationURL\" target=\"_blank\">".$aData['datasource']->GetName()."</a>";
}
if (is_object($oCreatorTask))
if ($aData['replica']->Get('status_dest_creator') == 1)
{
$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>";
}
$oCreatorTask = $aData['datasource'];
$bCreatedByTask = true;
}
else
{
$sTip = "<p>".Dict::S('Core:Synchro:ThisObjectIsSynchronized')."</p>";
$bCreatedByTask = false;
}
$sTip .= "<p><b>".Dict::S('Core:Synchro:ListOfDataSources')."</b></p>";
foreach($aMasterSources as $aStruct)
if ($bCreatedByTask)
{
$oDataSource = $aStruct['datasource'];
$sLink = $aStruct['url'];
$sTip .= "<p style=\"white-space:nowrap\">".$oDataSource->GetIcon(true, 'style="vertical-align:middle"')."&nbsp;$sLink<br/>";
$sTip .= Dict::S('Core:Synchro:LastSynchro').'<br/>'.$aStruct['last_synchro']."</p>";
$sDeletePolicy = $aData['datasource']->Get('delete_policy');
if (($sDeletePolicy == 'delete') || ($sDeletePolicy == 'update_then_delete'))
{
$bCanBeDeletedByTask = true;
}
$sUserDeletePolicy = $aData['datasource']->Get('user_delete_policy');
if ($sUserDeletePolicy == 'nobody')
{
$bCanBeDeletedByUser = false;
}
elseif (($sUserDeletePolicy == 'administrators') && !UserRights::IsAdministrator())
{
$bCanBeDeletedByUser = false;
}
else // everybody...
{
}
}
$aIcons[] = '&nbsp;<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' }} } );");
$aMasterSources[$aData['datasource']->GetKey()]['datasource'] = $aData['datasource'];
$aMasterSources[$aData['datasource']->GetKey()]['url'] = $sLink;
$aMasterSources[$aData['datasource']->GetKey()]['last_synchro'] = $aData['replica']->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"')."&nbsp;$sLink<br/>";
$sTip .= Dict::S('Core:Synchro:LastSynchro').'<br/>'.$aStruct['last_synchro']."</p>";
}
$sSynchroIcon = '&nbsp;<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(MetaModel::GetName(get_class($this)).": <span class=\"hilite\">".$this->GetName()."</span>$sIcons</h1>\n");
$oPage->add("<div class=\"page_header\"><h1>".$this->GetIcon()."&nbsp;\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");
}
@@ -302,7 +245,7 @@ EOF
$oExtensionInstance->OnDisplayProperties($this, $oPage, $bEditMode);
}
}
// 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
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
@@ -330,8 +273,6 @@ EOF
function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
{
$aRedundancySettings = $this->FindVisibleRedundancySettings();
// Related objects: display all the linkset attributes, each as a separate tab
// In the order described by the 'display' ZList
$aList = $this->FlattenZList(MetaModel::GetZListItems(get_class($this), 'details'));
@@ -397,7 +338,6 @@ EOF
// Non-readable/hidden linkedset... don't display anything
if ($iFlags & OPT_ATT_HIDDEN) continue;
$aArgs = array('this' => $this);
$bReadOnly = ($iFlags & (OPT_ATT_READONLY|OPT_ATT_SLAVE));
if ($bEditMode && (!$bReadOnly))
{
@@ -417,6 +357,7 @@ EOF
$oValue = $this->Get($sAttCode);
$sDisplayValue = ''; // not used
$aArgs = array('this' => $this);
$sHTMLValue = "<span id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $oValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).'</span>';
$this->AddToFieldsMap($sAttCode, $sInputId);
$oPage->add($sHTMLValue);
@@ -468,29 +409,6 @@ EOF
$oBlock = new DisplayBlock($this->Get($sAttCode)->GetFilter(), 'list', false);
$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('');
@@ -607,7 +525,18 @@ EOF
$sComments = isset($aFieldsComments[$sAttCode]) ? $aFieldsComments[$sAttCode] : '&nbsp;';
$sInfos = '&nbsp;';
$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))
{
// the caller may override some flags if needed
@@ -762,37 +691,6 @@ EOF
{
$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
@@ -803,11 +701,6 @@ EOF
*/
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']))
{
$iListId = $oPage->GetUniqueId(); // Works only if not in an Ajax page !!
@@ -1507,7 +1400,12 @@ EOF
$sHtml .= "<h2>".Dict::Format('UI:SearchFor_Class_Objects', $sClassesCombo)."</h2>\n";
$index = 0;
$sHtml .= "<p>\n";
$aFilterCriteria = $oSet->GetFilter()->GetCriteria();
$aMapCriteria = array();
foreach($aFilterCriteria as $aCriteria)
{
$aMapCriteria[$aCriteria['filtercode']][] = array('value' => $aCriteria['value'], 'opcode' => $aCriteria['opcode']);
}
$aList = MetaModel::GetZListItems($sClassName, 'standard_search');
$aConsts = $oSet->ListConstantFields(); // Some fields are constants based on the query/context
$sClassAlias = $oSet->GetFilter()->GetClassAlias();
@@ -1531,7 +1429,6 @@ EOF
$sFilterValue = $aMapCriteria[$sFilterCode][0]['value'];
$sFilterOpCode = $aMapCriteria[$sFilterCode][0]['opcode'];
}
// Todo: Investigate...
if ($sFilterCode != 'company')
{
$oUnlimitedFilter->AddCondition($sFilterCode, $sFilterValue, $sFilterOpCode);
@@ -1620,7 +1517,7 @@ EOF
{
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";
@@ -1830,7 +1727,7 @@ EOF
break;
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);
break;
@@ -1894,20 +1791,6 @@ EOF
$sHTMLValue .= "<!-- iFlags: $iFlags bMandatory: $bMandatory -->\n";
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':
default:
$aEventsList[] ='validate';
@@ -1966,71 +1849,19 @@ EOF
{
$sNullValue = "'$sNullValue'"; // Add quotes to turn this into a JS string if it's not a number
}
$sOriginalValue = ($iFlags & OPT_ATT_MUSTCHANGE) ? json_encode($value) : 'undefined';
$oPage->add_ready_script("$('#$iId').bind('".implode(' ', $aEventsList)."', function(evt, sFormId) { return ValidateField('$iId', '$sPattern', $bMandatory, sFormId, $sNullValue, $sOriginalValue) } );\n"); // Bind to a custom event: validate
$oPage->add_ready_script("$('#$iId').bind('".implode(' ', $aEventsList)."', function(evt, sFormId) { return ValidateField('$iId', '$sPattern', $bMandatory, sFormId, $sNullValue) } );\n"); // Bind to a custom event: validate
}
$aDependencies = MetaModel::GetDependentAttributes($sClass, $sAttCode); // List of attributes that depend on the current one
if (count($aDependencies) > 0)
{
// Unbind first to avoid duplicate event handlers in case of reload of the whole (or part of the) form
$oPage->add_ready_script("$('#$iId').unbind('change.dependencies').bind('change.dependencies', function(evt, sFormId) { return oWizardHelper{$sFormPrefix}.UpdateDependentFields(['".implode("','", $aDependencies)."']) } );\n"); // Bind to a custom event: validate
$oPage->add_ready_script("$('#$iId').bind('change', function(evt, sFormId) { return oWizardHelper{$sFormPrefix}.UpdateDependentFields(['".implode("','", $aDependencies)."']) } );\n"); // Bind to a custom event: validate
}
}
$oPage->add_dict_entry('UI:ValueMustBeSet');
$oPage->add_dict_entry('UI:ValueMustBeChanged');
$oPage->add_dict_entry('UI:ValueInvalidFormat');
return "<div>{$sHTMLValue}</div>";
}
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()."&nbsp;".Dict::Format('UI:ModificationTitle_Class_Object', $sClassLabel, $this->GetName())."</h1>\n");
$oPage->add("</div>\n");
$oPage->add("<div class=\"wizContainer\">\n");
}
self::$iGlobalFormId++;
$this->aFieldsMap = array();
$sPrefix = '';
@@ -2041,13 +1872,15 @@ EOF
$aFieldsComments = (isset($aExtraParams['fieldsComments'])) ? $aExtraParams['fieldsComments'] : array();
$this->m_iFormId = $sPrefix.self::$iGlobalFormId;
$sClass = get_class($this);
$oAppContext = new ApplicationContext();
$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
$iKey = $this->GetKey();
$aDetails = array();
$aFieldsMap = array();
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
{
@@ -2143,10 +1976,9 @@ EOF
}
$sConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
$sJSToken = json_encode($sOwnershipToken);
$oPage->add_ready_script(
<<<EOF
$(window).unload(function() { return OnUnload('$iTransactionId', '$sClass', $iKey, $sJSToken) } );
$(window).unload(function() { return OnUnload('$iTransactionId') } );
window.onbeforeunload = function() {
if (!window.bInSubmit && !window.bInCancel)
{
@@ -2190,10 +2022,6 @@ EOF
$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());
if ($sButtonsPosition != 'top')
{
@@ -2204,29 +2032,21 @@ EOF
// 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();
$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");
if (isset($aExtraParams['wizard_container']) && $aExtraParams['wizard_container'])
{
$oPage->add("</div>\n");
}
$iFieldsCount = count($aFieldsMap);
$sJsonFieldsMap = json_encode($aFieldsMap);
$sState = $this->GetState();
$sSessionStorageKey = $sClass.'_'.$iKey;
$oPage->add_script(
<<<EOF
sessionStorage.removeItem('$sSessionStorageKey');
// Create the object once at the beginning of the page...
var oWizardHelper$sPrefix = new WizardHelper('$sClass', '$sPrefix', '$sState');
oWizardHelper$sPrefix.SetFieldsMap($sJsonFieldsMap);
oWizardHelper$sPrefix.SetFieldsCount($iFieldsCount);
EOF
);
);
$oPage->add_ready_script(
<<<EOF
oWizardHelper$sPrefix.UpdateWizard();
@@ -2234,27 +2054,7 @@ EOF
CheckFields('form_{$this->m_iFormId}', false);
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())
@@ -2333,7 +2133,6 @@ EOF
public function DisplayStimulusForm(WebPage $oPage, $sStimulus)
{
$sClass = get_class($this);
$iKey = $this->GetKey();
$aTransitions = $this->EnumTransitions();
$aStimuli = MetaModel::EnumStimuli($sClass);
if (!isset($aTransitions[$sStimulus]))
@@ -2341,28 +2140,6 @@ EOF
// Invalid stimulus
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();
$sActionDetails = $aStimuli[$sStimulus]->GetDescription();
$aTransition = $aTransitions[$sStimulus];
@@ -2389,27 +2166,15 @@ EOF
$iFieldIndex = 0;
$aFieldsMap = array();
// The list of candidate fields is made of the ordered list of "details" attributes + other attributes
$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;
}
}
$aDetailsList =$this->FlattenZList(MetaModel::GetZListItems($sClass, 'details'));
// 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
$aDeps = array();
foreach($aAttributes as $sAttCode => $trash)
foreach($aDetailsList as $sAttCode)
{
$aDeps[$sAttCode] = MetaModel::GetPrequisiteAttributes($sClass, $sAttCode);
}
$aList = $this->OrderDependentFields($aDeps);
$aList =$this->OrderDependentFields($aDeps);
foreach($aList as $sAttCode)
{
@@ -2463,15 +2228,10 @@ EOF
$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=\"stimulus\" value=\"$sStimulus\">\n");
$iTransactionId = utils::GetNewTransactionId();
$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");
}
$oPage->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::GetNewTransactionId()."\">\n");
$oAppContext = new ApplicationContext();
$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>&nbsp;&nbsp;&nbsp;&nbsp;\n");
$oPage->add("<button type=\"button\" class=\"action\" onClick=\"BackToDetails('$sClass', ".$this->GetKey().")\"><span>".Dict::S('UI:Button:Cancel')."</span></button>&nbsp;&nbsp;&nbsp;&nbsp;\n");
$oPage->add("<button type=\"submit\" class=\"action\"><span>$sActionLabel</span></button>\n");
$oPage->add("</form>\n");
$oPage->add("</div>\n");
@@ -2494,19 +2254,12 @@ EOF
oWizardHelper.SetFieldsCount($iFieldsCount);
EOF
);
$sJSToken = json_encode($sOwnershipToken);
$oPage->add_ready_script(
<<<EOF
// Starts the validation when the page is ready
CheckFields('apply_stimulus', false);
$(window).unload(function() { return OnUnload('$iTransactionId', '$sClass', $iKey, $sJSToken) } );
EOF
);
if ($sOwnershipToken !== null)
{
$this->GetOwnershipJSHandler($oPage, $sOwnershipToken);
}
);
}
public static function ProcessZlist($aList, $aDetails, $sCurrentTab, $sCurrentCol, $sCurrentSet)
@@ -2712,7 +2465,7 @@ EOF
{
// Possible return values are:
// HILIGHT_CLASS_CRITICAL, HILIGHT_CLASS_WARNING, HILIGHT_CLASS_OK, HILIGHT_CLASS_NONE
$current = parent::GetHilightClass(); // Default computation
$current = HILIGHT_CLASS_NONE; // Not hilighted by default
// Invoke extensions before the deletion (the deletion will do some cleanup and we might loose some information
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
@@ -2869,26 +2622,6 @@ EOF
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
* @param string $aValues array of attcode => scalar or array (N-N links)
@@ -3083,10 +2816,6 @@ EOF
{
$value = array('fcontents' => utils::ReadPostedDocument("attr_{$sFormPrefix}{$sAttCode}", 'fcontents'));
}
elseif ($oAttDef->GetEditClass() == 'RedundancySetting')
{
$value = $oAttDef->ReadValueFromPostedForm($sFormPrefix);
}
else if (($oAttDef->GetEditClass() == 'LinkedSet') && !$oAttDef->IsIndirect() &&
(($oAttDef->GetEditMode() == LINKSET_EDITMODE_INPLACE) || ($oAttDef->GetEditMode() == LINKSET_EDITMODE_ADDREMOVE)) )
{
@@ -3205,7 +2934,7 @@ EOF
return $res;
}
protected static function BulkUpdateTracked_Internal(DBSearch $oFilter, array $aValues)
protected static function BulkUpdateTracked_Internal(DBObjectSearch $oFilter, array $aValues)
{
// Todo - invoke the extension
return parent::BulkUpdateTracked_Internal($oFilter, $aValues);
@@ -3688,7 +3417,7 @@ EOF
$oP->Table($aHeaders, $aRows);
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:
$oP->add("<form method=\"post\" action=\"$sFormAction\" enctype=\"multipart/form-data\">\n");
$aDefaults = utils::ReadParam('default', array());
@@ -4009,77 +3738,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
);
}
}
?>

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
@@ -21,7 +21,7 @@
* 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
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -39,11 +39,7 @@ class CSVPage extends WebPage
public function output()
{
$this->add_header("Content-Length: ".strlen(trim($this->s_content)));
// Get the unexpected output but do nothing with it
$sTrash = $this->ob_get_clean_safe();
$this->add_header("Content-Length: ".strlen(trim($this->s_content)));
foreach($this->a_headers as $s_header)
{
header($s_header);
@@ -51,9 +47,9 @@ class CSVPage extends WebPage
echo trim($this->s_content);
echo "\n";
if (class_exists('DBSearch'))
if (class_exists('MetaModel'))
{
DBSearch::RecordQueryTrace();
MetaModel::RecordQueryTrace();
}
if (class_exists('ExecutionKPI'))
{
@@ -109,3 +105,4 @@ class CSVPage extends WebPage
}
}
?>

View File

@@ -546,9 +546,7 @@ class RuntimeDashboard extends Dashboard
public function RenderEditionTools($oPage)
{
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.iframe-transport.js');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.fileupload.js');
$sEditMenu = "<td><span id=\"DashboardMenu\"><ul><li><img src=\"../images/pencil-menu.png\"><ul>";
$sEditMenu = "<td><span id=\"DashboardMenu\"><ul><li><img src=\"../images/edit.png\"><ul>";
$aActions = array();
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:Edit'), "return EditDashboard('{$this->sId}')");

View File

@@ -1570,7 +1570,7 @@ class DashletBadge extends Dashlet
$oPage->add('<p>');
$oPage->add(' <a>'.Dict::Format('UI:ClickToCreateNew', $sClassLabel).'</a>');
$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('</div>');

View File

@@ -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>

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2013 Combodo SARL
//
// This file is part of iTop.
//
@@ -18,7 +18,7 @@
/**
* 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
*/
@@ -290,24 +290,17 @@ EOF;
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?itopversion='.ITOP_VERSION.'"><ul>';
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
$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();
$sHtml .= $oPage->RenderPopupMenuItems($aActions);
}
else
{
$sHtml = '';
}
$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>';
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
$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();
$sHtml .= $oPage->RenderPopupMenuItems($aActions);
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
{
public $aClassAliases;
@@ -748,12 +713,6 @@ class DataTableSettings implements Serializable
{
$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);
}
foreach($aList as $sAttCode)

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2013 Combodo SARL
//
// This file is part of iTop.
//
@@ -19,7 +19,7 @@
/**
* 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
*/
@@ -50,7 +50,7 @@ class DisplayBlock
protected $m_aParams;
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_aConditions = array();
@@ -73,7 +73,6 @@ class DisplayBlock
{
$oDummyFilter = new DBObjectSearch($oSet->GetClass());
$aKeys = array();
$oSet->OptimizeColumnLoad(array('id')); // No need to load all the columns just to get the id
while($oObject = $oSet->Fetch())
{
$aKeys[] = $oObject->GetKey();
@@ -180,11 +179,11 @@ class DisplayBlock
switch($sEncoding)
{
case 'text/serialize':
$oFilter = DBSearch::unserialize($sITopData);
$oFilter = CMDBSearchFilter::unserialize($sITopData);
break;
case 'text/oql':
$oFilter = DBSearch::FromOQL($sITopData);
$oFilter = CMDBSearchFilter::FromOQL($sITopData);
break;
}
return new $sBlockClass($oFilter, $sBlockType, $bAsynchronous, $aParams);
@@ -407,7 +406,7 @@ class DisplayBlock
$aGroupBy = array();
$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);
$aGroupBy = array();
@@ -446,8 +445,6 @@ class DisplayBlock
$sFormat = isset($aExtraParams['format']) ? $aExtraParams['format'] : 'UI:Pagination:HeaderNoSelection';
$sHtml .= $oPage->GetP(Dict::Format($sFormat, $iTotalCount));
$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
{
@@ -705,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=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>';
break;
@@ -768,7 +765,6 @@ class DisplayBlock
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.urlencode($this->m_oFilter->serialize());
$sHtml .= '<h1>'.Dict::S(str_replace('_', ':', $sTitle)).'</h1>';
$sHtml .= '<a class="summary" href="'.$sHyperlink.'">'.Dict::Format(str_replace('_', ':', $sLabel), $iCount).'</a>';
$sHtml .= '<div style="clear:both;"></div>';
break;
case 'csv':
@@ -852,24 +848,21 @@ class DisplayBlock
break;
case 'search':
if (!$oPage->IsPrintableVersion())
{
$sStyle = (isset($aExtraParams['open']) && ($aExtraParams['open'] == 'true')) ? 'SearchDrawer' : 'SearchDrawer DrawerClosed';
$sHtml .= "<div id=\"ds_$sId\" class=\"$sStyle\">\n";
$oPage->add_ready_script(
$sStyle = (isset($aExtraParams['open']) && ($aExtraParams['open'] == 'true')) ? 'SearchDrawer' : 'SearchDrawer DrawerClosed';
$sHtml .= "<div id=\"ds_$sId\" class=\"$sStyle\">\n";
$oPage->add_ready_script(
<<<EOF
$("#dh_$sId").click( function() {
$("#ds_$sId").slideToggle('normal', function() { $("#ds_$sId").parent().resize(); FixSearchFormsDisposition(); $("#dh_$sId").trigger('toggle_complete'); } );
$("#dh_$sId").toggleClass('open');
});
$("#dh_$sId").click( function() {
$("#ds_$sId").slideToggle('normal', function() { $("#ds_$sId").parent().resize(); } );
$("#dh_$sId").toggleClass('open');
});
EOF
);
$aExtraParams['currentId'] = $sId;
$sHtml .= cmdbAbstractObject::GetSearchForm($oPage, $this->m_oSet, $aExtraParams);
$sHtml .= "</div>\n";
$sHtml .= "<div class=\"HRDrawer\"></div>\n";
$sHtml .= "<div id=\"dh_$sId\" class=\"DrawerHandle\">".Dict::S('UI:SearchToggle')."</div>\n";
}
);
$aExtraParams['currentId'] = $sId;
$sHtml .= cmdbAbstractObject::GetSearchForm($oPage, $this->m_oSet, $aExtraParams);
$sHtml .= "</div>\n";
$sHtml .= "<div class=\"HRDrawer\"></div>\n";
$sHtml .= "<div id=\"dh_$sId\" class=\"DrawerHandle\">".Dict::S('UI:SearchToggle')."</div>\n";
break;
case 'open_flash_chart':
@@ -916,7 +909,7 @@ EOF
$aGroupBy = array();
$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);
$aGroupBy = array();
@@ -991,7 +984,7 @@ EOF
$aGroupBy = array();
$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);
$aGroupBy = array();
@@ -1073,7 +1066,7 @@ EOF
$aGroupBy = array();
$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);
$aGroupBy = array();
@@ -1133,7 +1126,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
*/
protected function AddCondition($sFilterCode, $condition, $sOpCode = null)
@@ -1221,7 +1214,7 @@ class HistoryBlock extends DisplayBlock
protected $iLimitCount;
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);
$this->iLimitStart = 0;
@@ -1239,15 +1232,12 @@ class HistoryBlock extends DisplayBlock
$sHtml = '';
$bTruncated = 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);
if (($this->iLimitCount - $this->iLimitStart) < $oSet->Count())
{
$bTruncated = true;
}
$bTruncated = true;
}
}
$sHtml .= "<!-- filter: ".($this->m_oFilter->ToOQL())."-->\n";
@@ -1290,7 +1280,7 @@ class HistoryBlock extends DisplayBlock
{
$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;
}
@@ -1381,8 +1371,7 @@ class MenuBlock extends DisplayBlock
$sDefault.= "&default[$sKey]=$sValue";
}
}
$bIsCreationAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_CREATE) == UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
$sRefreshAction = '';
$bIsCreationAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_CREATE) == UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
switch($oSet->Count())
{
case 0:
@@ -1392,144 +1381,68 @@ class MenuBlock extends DisplayBlock
case 1:
$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);
// 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')
$aStimuli = Metamodel::EnumStimuli(get_class($oObj));
foreach($aTransitions as $sStimulusCode => $aTransitionDef)
{
// Actions specific to the list
$sOQL = addslashes($sFilterDesc);
$aActions['UI:Menu:AddToDashboard'] = array ('label' => Dict::S('UI:Menu:AddToDashboard'), 'url' => "#", 'onclick' => "return DashletCreationDlg('$sOQL')");
$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
}
}
*/
}
$this->AddMenuSeparator($aActions);
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
// Relations...
$aRelations = MetaModel::EnumRelations($sClass);
if (count($aRelations))
{
$oSet->Rewind();
foreach($oExtensionInstance->EnumAllowedActions($oSet) as $sLabel => $sUrl)
$this->AddMenuSeparator($aActions);
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;
@@ -1574,7 +1487,7 @@ class MenuBlock extends DisplayBlock
{
$aQueryParams = $aExtraParams['query_params'];
}
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy);
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy);
$aRes = CMDBSource::QueryToArray($sSql);
if (count($aRes) == 1)
{
@@ -1674,23 +1587,16 @@ class MenuBlock extends DisplayBlock
$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";
}
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>";
}
$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);
static $bPopupScript = false;
if (!$bPopupScript)

View File

@@ -1,536 +0,0 @@
<?php
require_once('xlsxwriter.class.php');
class ExcelExporter
{
protected $sToken;
protected $aStatistics;
protected $sState;
protected $fStartTime;
protected $oSearch;
protected $aObjectsIDs;
protected $aTableHeaders;
protected $aAuthorizedClasses;
protected $iChunkSize = 1000;
protected $iPosition;
protected $sOutputFilePath;
protected $bAdvancedMode;
public function __construct($sToken = null)
{
$this->aStatistics = array(
'objects_count' => 0,
'total_duration' => 0,
'data_retrieval_duration' => 0,
'excel_build_duration' => 0,
'excel_write_duration' => 0,
'peak_memory_usage' => 0,
);
$this->fStartTime = microtime(true);
$this->oSearch = null;
$this->sState = 'new';
$this->aObjectsIDs = array();
$this->iPosition = 0;
$this->aAuthorizedClasses = null;
$this->aTableHeaders = null;
$this->sOutputFilePath = null;
$this->bAdvancedMode = false;
$this->CheckDataDir();
if ($sToken == null)
{
$this->sToken = $this->GetNewToken();
}
else
{
$this->sToken = $sToken;
$this->ReloadState();
}
}
public function __destruct()
{
if (($this->sState != 'done') && ($this->sState != 'error') && ($this->sToken != null))
{
// Operation in progress, save the state
$this->SaveState();
}
else
{
// Operation completed, cleanup the temp files
@unlink($this->GetStateFile());
@unlink($this->GetDataFile());
}
self::CleanupOldFiles();
}
public function SetChunkSize($iChunkSize)
{
$this->iChunkSize = $iChunkSize;
}
public function SetOutputFilePath($sDestFilePath)
{
$this->sOutputFilePath = $sDestFilePath;
}
public function SetAdvancedMode($bAdvanced)
{
$this->bAdvancedMode = $bAdvanced;
}
public function SaveState()
{
$aState = array(
'state' => $this->sState,
'statistics' => $this->aStatistics,
'filter' => $this->oSearch->serialize(),
'position' => $this->iPosition,
'chunk_size' => $this->iChunkSize,
'object_ids' => $this->aObjectsIDs,
'output_file_path' => $this->sOutputFilePath,
'advanced_mode' => $this->bAdvancedMode,
);
file_put_contents($this->GetStateFile(), json_encode($aState));
return $this->sToken;
}
public function ReloadState()
{
if ($this->sToken == null)
{
throw new Exception('ExcelExporter not initialized with a token, cannot reload state');
}
if (!file_exists($this->GetStateFile()))
{
throw new Exception("ExcelExporter: missing status file '".$this->GetStateFile()."', cannot reload state.");
}
$sJson = file_get_contents($this->GetStateFile());
$aState = json_decode($sJson, true);
if ($aState === null)
{
throw new Exception("ExcelExporter:corrupted status file '".$this->GetStateFile()."', not a JSON, cannot reload state.");
}
$this->sState = $aState['state'];
$this->aStatistics = $aState['statistics'];
$this->oSearch = DBObjectSearch::unserialize($aState['filter']);
$this->iPosition = $aState['position'];
$this->iChunkSize = $aState['chunk_size'];
$this->aObjectsIDs = $aState['object_ids'];
$this->sOutputFilePath = $aState['output_file_path'];
$this->bAdvancedMode = $aState['advanced_mode'];
}
public function SetObjectList($oSearch)
{
$this->oSearch = $oSearch;
}
public function Run()
{
$sCode = 'error';
$iPercentage = 100;
$sMessage = Dict::Format('ExcelExporter:ErrorUnexpected_State', $this->sState);
$fTime = microtime(true);
try
{
switch($this->sState)
{
case 'new':
$oIDSet = new DBObjectSet($this->oSearch);
$oIDSet->OptimizeColumnLoad(array('id'));
$this->aObjectsIDs = array();
while($oObj = $oIDSet->Fetch())
{
$this->aObjectsIDs[] = $oObj->GetKey();
}
$sCode = 'retrieving-data';
$iPercentage = 5;
$sMessage = Dict::S('ExcelExporter:RetrievingData');
$this->iPosition = 0;
$this->aStatistics['objects_count'] = count($this->aObjectsIDs);
$this->aStatistics['data_retrieval_duration'] += microtime(true) - $fTime;
// The first line of the file is the "headers" specifying the label and the type of each column
$this->GetFieldsList($oIDSet, $this->bAdvancedMode);
$sRow = json_encode($this->aTableHeaders);
$hFile = @fopen($this->GetDataFile(), 'ab');
if ($hFile === false)
{
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for writing.');
}
fwrite($hFile, $sRow."\n");
fclose($hFile);
// Next state
$this->sState = 'retrieving-data';
break;
case 'retrieving-data':
$oCurrentSearch = clone $this->oSearch;
$aIDs = array_slice($this->aObjectsIDs, $this->iPosition, $this->iChunkSize);
$oCurrentSearch->AddCondition('id', $aIDs, 'IN');
$hFile = @fopen($this->GetDataFile(), 'ab');
if ($hFile === false)
{
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for writing.');
}
$oSet = new DBObjectSet($oCurrentSearch);
$this->GetFieldsList($oSet, $this->bAdvancedMode);
while($aObjects = $oSet->FetchAssoc())
{
$aRow = array();
foreach($this->aAuthorizedClasses as $sAlias => $sClassName)
{
$oObj = $aObjects[$sAlias];
if ($this->bAdvancedMode)
{
$aRow[] = $oObj->GetKey();
}
foreach($this->aFieldsList[$sAlias] as $sAttCodeEx => $oAttDef)
{
$value = $oObj->Get($sAttCodeEx);
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!
$sExcelVal = trim(preg_replace('/========== ([^=]+) ============/', '********** $1 ************', $value->GetText()));
}
else
{
$sExcelVal = $oAttDef->GetEditValue($value, $oObj);
}
$aRow[] = $sExcelVal;
}
}
$sRow = json_encode($aRow);
fwrite($hFile, $sRow."\n");
}
fclose($hFile);
if (($this->iPosition + $this->iChunkSize) > count($this->aObjectsIDs))
{
// Next state
$this->sState = 'building-excel';
$sCode = 'building-excel';
$iPercentage = 80;
$sMessage = Dict::S('ExcelExporter:BuildingExcelFile');
}
else
{
$sCode = 'retrieving-data';
$this->iPosition += $this->iChunkSize;
$iPercentage = 5 + round(75 * ($this->iPosition / count($this->aObjectsIDs)));
$sMessage = Dict::S('ExcelExporter:RetrievingData');
}
break;
case 'building-excel':
$hFile = @fopen($this->GetDataFile(), 'rb');
if ($hFile === false)
{
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for reading.');
}
$sHeaders = fgets($hFile);
$aHeaders = json_decode($sHeaders, true);
$aData = array();
while($sLine = fgets($hFile))
{
$aRow = json_decode($sLine);
$aData[] = $aRow;
}
fclose($hFile);
@unlink($this->GetDataFile());
$fStartExcel = microtime(true);
$writer = new XLSXWriter();
$writer->setAuthor(UserRights::GetUserFriendlyName());
$writer->writeSheet($aData,'Sheet1', $aHeaders);
$fExcelTime = microtime(true) - $fStartExcel;
$this->aStatistics['excel_build_duration'] = $fExcelTime;
$fTime = microtime(true);
$writer->writeToFile($this->GetExcelFilePath());
$fExcelSaveTime = microtime(true) - $fTime;
$this->aStatistics['excel_write_duration'] = $fExcelSaveTime;
// Next state
$this->sState = 'done';
$sCode = 'done';
$iPercentage = 100;
$sMessage = Dict::S('ExcelExporter:Done');
break;
case 'done':
$this->sState = 'done';
$sCode = 'done';
$iPercentage = 100;
$sMessage = Dict::S('ExcelExporter:Done');
break;
}
}
catch(Exception $e)
{
$sCode = 'error';
$sMessage = $e->getMessage();
}
$this->aStatistics['total_duration'] += microtime(true) - $fTime;
$peak_memory = memory_get_peak_usage(true);
if ($peak_memory > $this->aStatistics['peak_memory_usage'])
{
$this->aStatistics['peak_memory_usage'] = $peak_memory;
}
return array(
'code' => $sCode,
'message' => $sMessage,
'percentage' => $iPercentage,
);
}
public function GetExcelFilePath()
{
if ($this->sOutputFilePath == null)
{
return APPROOT.'data/bulk_export/'.$this->sToken.'.xlsx';
}
else
{
return $this->sOutputFilePath;
}
}
public static function GetExcelFileFromToken($sToken)
{
return @file_get_contents(APPROOT.'data/bulk_export/'.$sToken.'.xlsx');
}
public static function CleanupFromToken($sToken)
{
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.status');
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.data');
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.xlsx');
}
public function Cleanup()
{
self::CleanupFromToken($this->sToken);
}
/**
* Delete all files in the data/bulk_export directory which are older than 1 day
* unless a different delay is configured.
*/
public static function CleanupOldFiles()
{
$aFiles = glob(APPROOT.'data/bulk_export/*.*');
$iDelay = MetaModel::GetConfig()->Get('xlsx_exporter_cleanup_old_files_delay');
if($iDelay > 0)
{
foreach($aFiles as $sFile)
{
$iModificationTime = filemtime($sFile);
if($iModificationTime < (time() - $iDelay))
{
// Temporary files older than one day are deleted
//echo "Supposed to delete: '".$sFile." (Unix Modification Time: $iModificationTime)'\n";
@unlink($sFile);
}
}
}
}
public function DisplayStatistics(Page $oPage)
{
$aStats = array(
'Number of objects exported' => $this->aStatistics['objects_count'],
'Total export duration' => sprintf('%.3f s', $this->aStatistics['total_duration']),
'Data retrieval duration' => sprintf('%.3f s', $this->aStatistics['data_retrieval_duration']),
'Excel build duration' => sprintf('%.3f s', $this->aStatistics['excel_build_duration']),
'Excel write duration' => sprintf('%.3f s', $this->aStatistics['excel_write_duration']),
'Peak memory usage' => self::HumanDisplay($this->aStatistics['peak_memory_usage']),
);
if ($oPage instanceof CLIPage)
{
$oPage->add($this->GetStatistics('text'));
}
else
{
$oPage->add($this->GetStatistics('html'));
}
}
public function GetStatistics($sFormat = 'html')
{
$sStats = '';
$aStats = array(
'Number of objects exported' => $this->aStatistics['objects_count'],
'Total export duration' => sprintf('%.3f s', $this->aStatistics['total_duration']),
'Data retrieval duration' => sprintf('%.3f s', $this->aStatistics['data_retrieval_duration']),
'Excel build duration' => sprintf('%.3f s', $this->aStatistics['excel_build_duration']),
'Excel write duration' => sprintf('%.3f s', $this->aStatistics['excel_write_duration']),
'Peak memory usage' => self::HumanDisplay($this->aStatistics['peak_memory_usage']),
);
if ($sFormat == 'text')
{
foreach($aStats as $sLabel => $sValue)
{
$sStats .= "+------------------------------+----------+\n";
$sStats .= sprintf("|%-30s|%10s|\n", $sLabel, $sValue);
}
$sStats .= "+------------------------------+----------+";
}
else
{
$sStats .= '<table><tbody>';
foreach($aStats as $sLabel => $sValue)
{
$sStats .= "<tr><td>$sLabel</td><td>$sValue</td></tr>";
}
$sStats .= '</tbody></table>';
}
return $sStats;
}
public static function HumanDisplay($iSize)
{
$aUnits = array('B','KB','MB','GB','TB','PB');
return @round($iSize/pow(1024,($i=floor(log($iSize,1024)))),2).' '.$aUnits[$i];
}
protected function CheckDataDir()
{
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.');
}
}
protected function GetStateFile($sToken = null)
{
if ($sToken == null)
{
$sToken = $this->sToken;
}
return APPROOT."data/bulk_export/$sToken.status";
}
protected function GetDataFile()
{
return APPROOT.'data/bulk_export/'.$this->sToken.'.data';
}
protected function GetNewToken()
{
$iNum = rand();
do
{
$iNum++;
$sToken = sprintf("%08x", $iNum);
$sFileName = $this->GetStateFile($sToken);
$hFile = @fopen($sFileName, 'x');
}
while($hFile === false);
fclose($hFile);
return $sToken;
}
protected function GetFieldsList($oSet, $bFieldsAdvanced = false, $bLocalize = true, $aFields = null)
{
$this->aFieldsList = array();
$oAppContext = new ApplicationContext();
$aClasses = $oSet->GetFilter()->GetSelectedClasses();
$this->aAuthorizedClasses = array();
foreach($aClasses as $sAlias => $sClassName)
{
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
{
$this->aAuthorizedClasses[$sAlias] = $sClassName;
}
}
$aAttribs = array();
$this->aTableHeaders = array();
foreach($this->aAuthorizedClasses as $sAlias => $sClassName)
{
$aList[$sAlias] = array();
foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode => $oAttDef)
{
if (is_null($aFields) || (count($aFields) == 0))
{
// Standard list of attributes (no link sets)
if ($oAttDef->IsScalar() && ($oAttDef->IsWritable() || $oAttDef->IsExternalField()))
{
$sAttCodeEx = $oAttDef->IsExternalField() ? $oAttDef->GetKeyAttCode().'->'.$oAttDef->GetExtAttCode() : $sAttCode;
if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
{
if ($bFieldsAdvanced)
{
$aList[$sAlias][$sAttCodeEx] = $oAttDef;
if ($oAttDef->IsExternalKey(EXTKEY_RELATIVE))
{
$sRemoteClass = $oAttDef->GetTargetClass();
foreach(MetaModel::GetReconcKeys($sRemoteClass) as $sRemoteAttCode)
{
$this->aFieldsList[$sAlias][$sAttCode.'->'.$sRemoteAttCode] = MetaModel::GetAttributeDef($sRemoteClass, $sRemoteAttCode);
}
}
}
}
else
{
// Any other attribute
$this->aFieldsList[$sAlias][$sAttCodeEx] = $oAttDef;
}
}
}
else
{
// User defined list of attributes
if (in_array($sAttCode, $aFields) || in_array($sAlias.'.'.$sAttCode, $aFields))
{
$this->aFieldsList[$sAlias][$sAttCode] = $oAttDef;
}
}
}
if ($bFieldsAdvanced)
{
$this->aTableHeaders['id'] = '0';
}
foreach($this->aFieldsList[$sAlias] as $sAttCodeEx => $oAttDef)
{
$sLabel = $bLocalize ? MetaModel::GetLabel($sClassName, $sAttCodeEx, isset($aParams['showMandatoryFields'])) : $sAttCodeEx;
if($oAttDef instanceof AttributeDateTime)
{
$this->aTableHeaders[$sLabel] = 'datetime';
}
else
{
$this->aTableHeaders[$sLabel] = 'string';
}
}
}
}
}

View File

@@ -38,10 +38,7 @@ class DesignerForm
protected $bReadOnly;
protected $sHierarchyPath; // Needed to manage the visibility of nested subform
protected $sHierarchyParent; // Needed to manage the visibility of nested subform
protected $sHierarchySelector; // Needed to manage the visibility of nested subform
protected $bDisplayed;
protected $aDefaultValues;
protected $sFieldsSuffix;
public function __construct()
{
@@ -50,17 +47,14 @@ class DesignerForm
$this->sScript = '';
$this->sReadyScript = '';
$this->sFormPrefix = '';
$this->sFieldsSuffix = '';
$this->sParamsContainer = '';
$this->sFormId = 'form_'.rand();
$this->oParentForm = null;
$this->bReadOnly = false;
$this->sHierarchyPath = '';
$this->sHierarchyParent = '';
$this->sHierarchySelector = '';
$this->StartFieldSet($this->sCurrentFieldSet);
$this->bDisplayed = true;
$this->aDefaultvalues = array();
}
public function AddField(DesignerFormField $oField)
@@ -84,14 +78,15 @@ class DesignerForm
public function Render($oP, $bReturnHTML = false)
{
$sFormId = $this->GetFormId();
if ($this->oParentForm == null)
{
$sFormId = $this->sFormId;
$sReturn = '<form id="'.$sFormId.'">';
}
else
{
$sReturn = '';
$sFormId = $this->oParentForm->sFormId;
}
$sHiddenFields = '';
foreach($this->aFieldSets as $sLabel => $aFields)
@@ -107,7 +102,7 @@ class DesignerForm
$aRow = $oField->Render($oP, $sFormId);
if ($oField->IsVisible())
{
$sValidation = '&nbsp;<span class="prop_apply">'.$this->GetValidationArea($oField->GetFieldId()).'</span>';
$sValidation = '&nbsp;<span class="prop_apply">'.$this->GetValidationArea($oField->GetCode()).'</span>';
$sField = $aRow['value'].$sValidation;
$aDetails[] = array('label' => $aRow['label'], 'value' => $sField);
}
@@ -145,11 +140,6 @@ class DesignerForm
$oP->add($sReturn);
}
}
public function GetFieldSets()
{
return $this->aFieldSets;
}
public function SetSubmitParams($sSubmitToUrl, $aSubmitParams)
{
@@ -163,11 +153,6 @@ class DesignerForm
$this->aSubmitParams = $oParentForm->aSubmitParams;
}
public function GetSubmitParams()
{
return array( 'url' => $this->sSubmitTo, 'params' => $this->aSubmitParams);
}
/**
* Helper to handle subforms hide/show
*/
@@ -183,7 +168,7 @@ class DesignerForm
{
return $this->sHierarchyPath;
}
/**
* Helper to handle subforms hide/show
*/
@@ -206,21 +191,24 @@ class DesignerForm
$sReturn = '';
$sActionUrl = addslashes($this->sSubmitTo);
$sJSSubmitParams = json_encode($this->aSubmitParams);
$sFormId = $this->GetFormId();
if ($this->oParentForm == null)
{
$sFormId = $this->sFormId;
$sReturn = '<form id="'.$sFormId.'" onsubmit="return false;">';
$sReturn .= '<table class="prop_table">';
$sReturn .= '<thead><tr><th class="prop_header">'.Dict::S('UI:Form:Property').'</th><th class="prop_header">'.Dict::S('UI:Form:Value').'</th><th colspan="2" class="prop_header">&nbsp;</th></tr></thead><tbody>';
}
else
{
$sFormId = $this->oParentForm->sFormId;
}
$sHiddenFields = '';
foreach($this->aFieldSets as $sLabel => $aFields)
{
$aDetails = array();
if ($sLabel != '')
{
$sReturn .= $this->StartRow().'<th colspan="4">'.$sLabel.'</th>'.$this->EndRow();
$sReturn .= '<tr><th colspan="4">'.$sLabel.'</th></tr>';
}
@@ -230,42 +218,18 @@ class DesignerForm
if ($oField->IsVisible())
{
$sFieldId = $this->GetFieldId($oField->GetCode());
$sValidation = $this->GetValidationArea($sFieldId, '<span title="Apply" class="ui-icon ui-icon-circle-check"/>');
$sValidationFields = '</td><td class="prop_icon prop_apply">'.$sValidation.'</td><td class="prop_icon prop_cancel"><span title="Revert" class="ui-icon ui-icon-circle-close"/></td>'.$this->EndRow();
$sPath = $this->GetHierarchyPath().'/'.$oField->GetCode();
if (is_null($aRow['label']))
{
$sReturn .= $this->StartRow($sFieldId).'<td class="prop_value" colspan="2">'.$aRow['value'];
}
else
{
$sReturn .= $this->StartRow($sFieldId).'<td class="prop_label">'.$aRow['label'].'</td><td class="prop_value">'.$aRow['value'];
}
if (!($oField instanceof DesignerFormSelectorField) && !($oField instanceof DesignerMultipleSubFormField))
$sValidation = $this->GetValidationArea($oField->GetCode(), '<span title="Apply" class="ui-icon ui-icon-circle-check"/>');
$sValidationFields = '</td><td class="prop_icon prop_apply">'.$sValidation.'</td><td class="prop_icon prop_cancel"><span title="Revert" class="ui-icon ui-icon-circle-close"/></td></tr>';
$sReturn .= '<tr id="row_'.$sFieldId.'"><td class="prop_label">'.$aRow['label'].'</td><td class="prop_value">'.$aRow['value'];
if (!($oField instanceof DesignerFormSelectorField))
{
$sReturn .= $sValidationFields;
}
$sNotifyParentSelectorJS = is_null($sNotifyParentSelector) ? 'null' : "'".addslashes($sNotifyParentSelector)."'";
$sAutoApply = $oField->IsAutoApply() ? 'true' : 'false';
$sHandlerEquals = $oField->GetHandlerEquals();
$sHandlerGetValue = $oField->GetHandlerGetValue();
$sWidgetClass = $oField->GetWidgetClass();
$sJSExtraParams = '';
if (count($oField->GetWidgetExtraParams()) > 0)
{
$aExtraParams = array();
foreach($oField->GetWidgetExtraParams() as $key=> $value)
{
$aExtraParams[] = "'$key': ".json_encode($value);
}
$sJSExtraParams = ', '.implode(', ', $aExtraParams);
}
$this->AddReadyScript(
<<<EOF
$('#row_$sFieldId').$sWidgetClass({parent_selector: $sNotifyParentSelectorJS, field_id: '$sFieldId', equals: $sHandlerEquals, get_field_value: $sHandlerGetValue, auto_apply: $sAutoApply, value: '', submit_to: '$sActionUrl', submit_parameters: $sJSSubmitParams $sJSExtraParams });
$('#row_$sFieldId').property_field({parent_selector: $sNotifyParentSelectorJS, field_id: '$sFieldId', auto_apply: $sAutoApply, value: '', submit_to: '$sActionUrl', submit_parameters: $sJSSubmitParams });
EOF
);
}
@@ -283,7 +247,7 @@ EOF
$sReturn .= '</table>';
$sReturn .= $sHiddenFields;
$sReturn .= '</form>';
$sReturn .= '<div id="prop_submit_result"></div>'; // for the return of the submit operation
$sReturn .= '<div id="prop_submit_result"/>'; // for the return of the submit operation
}
else
{
@@ -323,26 +287,9 @@ EOF
{
$oP->add($sReturn);
}
}
public function StartRow($sFieldId = null)
}
public function RenderAsDialog($oPage, $sDialogId, $sDialogTitle, $iDialogWidth, $sOkButtonLabel, $sIntroduction = null)
{
if ($sFieldId != null)
{
return '<tr id="row_'.$sFieldId.'" data-path="'.$this->GetHierarchyPath().'" data-selector="'.$this->GetHierarchyParent().'">';
}
return '<tr data-path="'.$this->GetHierarchyPath().'" data-selector="'.$this->GetHierarchyParent().'">';
}
public function EndRow()
{
return '</tr>';
}
public function RenderAsDialog($oPage, $sDialogId, $sDialogTitle, $iDialogWidth, $sOkButtonLabel, $sIntroduction = null, $bAutoOpen = true)
{
$this->SetPrefix('dlg_'); // To make sure that the controls have different IDs that the property sheet which may be displayed at the same time
$sDialogTitle = addslashes($sDialogTitle);
$sOkButtonLabel = addslashes($sOkButtonLabel);
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
@@ -355,27 +302,17 @@ EOF
$this->Render($oPage);
$oPage->add('</div>');
$sAutoOpen = $bAutoOpen ? 'true' : 'false';
$oPage->add_ready_script(
<<<EOF
$('#$sDialogId').dialog({
height: 'auto',
width: $iDialogWidth,
width: 500,
modal: true,
autoOpen: $sAutoOpen,
title: '$sDialogTitle',
buttons: [
{ text: "$sOkButtonLabel", click: function() {
var oForm = $(this).closest('.ui-dialog').find('form');
oForm.submit();
if (AnimateDlgButtons)
{
sFormId = oForm.attr('id');
if (oFormValidation[sFormId].length == 0)
{
AnimateDlgButtons(this);
}
}
} },
{ text: "$sCancelButtonLabel", click: function() { KillAllMenus(); $(this).dialog( "close" ); $(this).remove(); } },
],
@@ -407,29 +344,9 @@ EOF
public function GetPrefix()
{
$sPrefix = '';
if ($this->oParentForm != null)
{
$sPrefix = $this->oParentForm->GetPrefix();
}
return $sPrefix.$this->sFormPrefix;
}
public function SetSuffix($sSuffix)
{
$this->sFieldsSuffix = $sSuffix;
return $this->sFormPrefix;
}
public function GetSuffix()
{
$sSuffix = '';
if ($this->oParentForm != null)
{
$sSuffix = $this->oParentForm->GetSuffix();
}
return $sSuffix.$this->sFieldsSuffix;
}
public function SetReadOnly($bReadOnly = true)
{
$this->bReadOnly = $bReadOnly;
@@ -469,39 +386,11 @@ EOF
$this->oParentForm = $oParentForm;
}
public function SetDefaultValues($aDefaultValues)
{
if (!is_array($aDefaultValues)) return;
foreach($this->aFieldSets as $sLabel => $aFields)
{
foreach($aFields as $oField)
{
$oField->SetDefaultValueFrom($aDefaultValues);
}
}
}
public function GetDefaultValues()
{
return $this->aDefaultValues;
}
public function GetParentForm()
{
return $this->oParentForm;
}
public function GetFormId()
{
if ($this->oParentForm)
{
$this->oParentForm->GetFormId();
}
return $this->sFormId;
}
public function SetDisplayed($bDisplayed)
{
$this->bDisplayed = $bDisplayed;
@@ -531,42 +420,27 @@ EOF
public function GetFieldId($sCode)
{
return $this->GetPrefix().'attr_'.$sCode;
return $this->sFormPrefix.'attr_'.$sCode;
}
public function GetFieldName($sCode)
{
return 'attr_'.$sCode.$this->GetSuffix();
return 'attr_'.$sCode;
}
public function GetParamName($sCode)
{
return 'attr_'.$sCode.$this->GetSuffix();
return 'attr_'.$sCode;
}
public function GetValidationArea($sId, $sContent = '')
public function GetValidationArea($sCode, $sContent = '')
{
return "<span style=\"display:inline-block;width:20px;\" id=\"v_{$sId}\"><span class=\"ui-icon ui-icon-alert\"></span>$sContent</span>";
return "<span style=\"display:inline-block;width:20px;\" id=\"v_{$this->sFormPrefix}attr_$sCode\"><span class=\"ui-icon ui-icon-alert\"></span>$sContent</span>";
}
public function GetAsyncActionClass()
{
return $this->sAsyncActionClass;
}
public function FindField($sFieldCode)
{
$oFoundField = false;
foreach($this->aFieldSets as $sLabel => $aFields)
{
foreach($aFields as $oField)
{
$oFoundField = $oField->FindField($sFieldCode);
if ($oFoundField !== false) break;
}
if ($oFoundField !== false) break;
}
return $oFoundField;
}
}
class DesignerTabularForm extends DesignerForm
@@ -582,11 +456,6 @@ class DesignerTabularForm extends DesignerForm
{
$this->aTable[] = $aRow;
}
public function RenderAsPropertySheet($oP, $bReturnHTML = false, $sNotifyParentSelector = null)
{
return $this->Render($oP, $bReturnHTML);
}
public function Render($oP, $bReturnHTML = false)
{
@@ -687,7 +556,6 @@ class DesignerFormField
protected $bAutoApply;
protected $aCSSClasses;
protected $bDisplayed;
protected $aWidgetExtraParams;
public function __construct($sCode, $sLabel, $defaultValue)
{
@@ -699,7 +567,6 @@ class DesignerFormField
$this->bAutoApply = false;
$this->aCSSClasses = array();
$this->bDisplayed = true;
$this->aWidgetExtraParams = array();
}
public function GetCode()
@@ -748,21 +615,6 @@ class DesignerFormField
return $this->bDisplayed;
}
public function GetFieldId()
{
return $this->oForm->GetFieldId($this->sCode);
}
public function GetWidgetClass()
{
return 'property_field';
}
public function GetWidgetExtraParams()
{
return $this->aWidgetExtraParams;
}
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
{
$sId = $this->oForm->GetFieldId($this->sCode);
@@ -806,36 +658,6 @@ class DesignerFormField
{
$this->aCSSClasses[] = $sCSSClass;
}
/**
* A way to set/change the default value after constructing the field
*/
public function SetDefaultValueFrom($aAllDefaultValue)
{
if (array_key_exists($this->GetCode(), $aAllDefaultValue))
{
$this->defaultValue = $aAllDefaultValue[$this->GetCode()];
}
}
public function FindField($sFieldCode)
{
if ($this->sCode == $sFieldCode)
{
return $this;
}
return false;
}
public function GetHandlerEquals()
{
return 'null';
}
public function GetHandlerGetValue()
{
return 'null';
}
}
class DesignerLabelField extends DesignerFormField
@@ -852,7 +674,7 @@ class DesignerLabelField extends DesignerFormField
{
$sId = $this->oForm->GetFieldId($this->sCode);
$sName = $this->oForm->GetFieldName($this->sCode);
return array('label' => $this->sLabel, 'value' => $this->sDescription);
return array('label' => $this->sLabel, 'value' => $sDescription);
}
public function ReadParam(&$aValues)
@@ -869,11 +691,13 @@ class DesignerTextField extends DesignerFormField
{
protected $sValidationPattern;
protected $aForbiddenValues;
protected $sExplainForbiddenValues;
public function __construct($sCode, $sLabel = '', $defaultValue = '')
{
parent::__construct($sCode, $sLabel, $defaultValue);
$this->sValidationPattern = '';
$this->aForbiddenValues = array();
$this->aForbiddenValues = null;
$this->sExplainForbiddenValues = null;
}
public function SetValidationPattern($sValidationPattern)
@@ -883,23 +707,22 @@ class DesignerTextField extends DesignerFormField
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)
{
// 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')
{
$sId = $this->oForm->GetFieldId($this->sCode);
$sName = $this->oForm->GetFieldName($this->sCode);
if ($this->IsReadOnly())
{
@@ -911,15 +734,17 @@ class DesignerTextField extends DesignerFormField
if (is_array($this->aForbiddenValues))
{
$sForbiddenValues = json_encode($this->aForbiddenValues);
$sExplainForbiddenValues = addslashes($this->sExplainForbiddenValues);
}
else
{
$sForbiddenValues = '[]'; //Empty JS array
$sForbiddenValues = 'null';
$sExplainForbiddenValues = 'null';
}
$sMandatory = $this->bMandatory ? 'true' : 'false';
$oP->add_ready_script(
<<<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', '$sFormId', $sForbiddenValues, '$sExplainForbiddenValues'); } );
{
var myTimer = null;
$('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
@@ -939,8 +764,8 @@ EOF
public function 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;
}
@@ -962,35 +787,30 @@ class DesignerLongTextField extends DesignerTextField
if (is_array($this->aForbiddenValues))
{
$sForbiddenValues = json_encode($this->aForbiddenValues);
$sExplainForbiddenValues = addslashes($this->sExplainForbiddenValues);
}
else
{
$sForbiddenValues = '[]'; //Empty JS array
$sForbiddenValues = 'null';
$sExplainForbiddenValues = 'null';
}
$sMandatory = $this->bMandatory ? 'true' : 'false';
$sCSSClasses = '';
if (count($this->aCSSClasses) > 0)
{
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
}
if (!$this->IsReadOnly())
{
$oP->add_ready_script(
$sReadOnly = $this->IsReadOnly() ? 'readonly' : '';
$oP->add_ready_script(
<<<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', '$sFormId', $sForbiddenValues, '$sExplainForbiddenValues'); } );
{
var myTimer = null;
$('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
}
EOF
);
$sValue = "<textarea $sCSSClasses id=\"$sId\" name=\"$sName\">".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."</textarea>";
}
else
);
$sCSSClasses = '';
if (count($this->aCSSClasses) > 0)
{
$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>");
}
}
@@ -1028,7 +848,7 @@ class DesignerIntegerField extends DesignerFormField
$sMandatory = $this->bMandatory ? 'true' : 'false';
$oP->add_ready_script(
<<<EOF
$('#$sId').bind('change keyup validate', function() { ValidateInteger('$sId', $sMandatory, $(this).closest('form').attr('id'), $sMin, $sMax); } );
$('#$sId').bind('change keyup validate', function() { ValidateInteger('$sId', $sMandatory, '$sFormId', $sMin, $sMax); } );
{
var myTimer = null;
$('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
@@ -1067,8 +887,6 @@ class DesignerComboField extends DesignerFormField
protected $aAllowedValues;
protected $bMultipleSelection;
protected $bOtherChoices;
protected $sNullLabel;
protected $bSorted;
public function __construct($sCode, $sLabel = '', $defaultValue = '')
{
@@ -1076,10 +894,8 @@ class DesignerComboField extends DesignerFormField
$this->aAllowedValues = array();
$this->bMultipleSelection = false;
$this->bOtherChoices = false;
$this->sNullLabel = Dict::S('UI:SelectOne');
$this->bAutoApply = true;
$this->bSorted = true; // Sorted by default
}
public function SetAllowedValues($aAllowedValues)
@@ -1096,36 +912,16 @@ class DesignerComboField extends DesignerFormField
{
$this->bOtherChoices = $bOtherChoices;
}
/**
* An empty label will disable the default empty value
*/
public function SetNullLabel($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')
{
$sId = $this->oForm->GetFieldId($this->sCode);
$sName = $this->oForm->GetFieldName($this->sCode);
$sChecked = $this->defaultValue ? 'checked' : '';
$sMandatory = $this->bMandatory ? 'true' : 'false';
$sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
if ($this->IsSorted())
{
asort($this->aAllowedValues);
}
$sCSSClasses = '';
if (count($this->aCSSClasses) > 0)
{
@@ -1165,10 +961,7 @@ class DesignerComboField extends DesignerFormField
else
{
$sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\">";
if ($this->sNullLabel != '')
{
$sHtml .= "<option value=\"\">".$this->sNullLabel."</option>";
}
$sHtml .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>";
}
foreach($this->aAllowedValues as $sKey => $sDisplayValue)
{
@@ -1191,7 +984,7 @@ class DesignerComboField extends DesignerFormField
}
$oP->add_ready_script(
<<<EOF
$('#$sId').bind('change validate', function() { ValidateWithPattern('$sId', $sMandatory, '', $(this).closest('form').attr('id'), null, null); } );
$('#$sId').bind('change validate', function() { ValidateWithPattern('$sId', $sMandatory, '', '$sFormId', null, null); } );
EOF
);
}
@@ -1268,6 +1061,7 @@ class DesignerBooleanField extends DesignerFormField
}
}
class DesignerHiddenField extends DesignerFormField
{
public function __construct($sCode, $sLabel = '', $defaultValue = '')
@@ -1329,37 +1123,27 @@ class DesignerIconSelectionField extends DesignerFormField
$sPostUploadTo = ($this->sUploadUrl == null) ? 'null' : "'{$this->sUploadUrl}'";
if (!$this->IsReadOnly())
{
$sValue = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"{$this->defaultValue}\"/>";
$oP->add_ready_script(
<<<EOF
$('#$sId').icon_select({current_idx: $idx, items: $sJSItems, post_upload_to: $sPostUploadTo});
EOF
);
}
else
{
$sValue = '<span style="display:inline-block;line-height:48px;height:48px;"><span><img style="vertical-align:middle" src="'.$this->aAllowedValues[$idx]['icon'].'" />&nbsp;'.htmlentities($this->aAllowedValues[$idx]['label'], ENT_QUOTES, 'UTF-8').'</span></span>';
}
$sReadOnly = $this->IsReadOnly() ? 'disabled' : '';
return array('label' => $this->sLabel, 'value' => $sValue);
return array('label' =>$this->sLabel, 'value' => "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"{$this->defaultValue}\"/>");
}
}
class RunTimeIconSelectionField extends DesignerIconSelectionField
{
static $aAllIcons = array();
public function __construct($sCode, $sLabel = '', $defaultValue = '')
{
parent::__construct($sCode, $sLabel, $defaultValue);
if (count(self::$aAllIcons) == 0)
{
self::$aAllIcons = self::FindIconsOnDisk(APPROOT.'env-'.utils::GetCurrentEnvironment());
ksort(self::$aAllIcons);
}
$aAllIcons = self::FindIconsOnDisk(APPROOT.'env-'.utils::GetCurrentEnvironment());
ksort($aAllIcons);
$aValues = array();
foreach(self::$aAllIcons as $sFilePath)
foreach($aAllIcons as $sFilePath)
{
$aValues[] = array('value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => utils::GetAbsoluteUrlModulesRoot().$sFilePath);
}
@@ -1452,40 +1236,11 @@ class DesignerFormSelectorField extends DesignerFormField
{
protected $aSubForms;
protected $defaultRealValue; // What's stored as default value is actually the index
protected $bSorted;
public function __construct($sCode, $sLabel = '', $defaultValue = '')
{
parent::__construct($sCode, $sLabel, 0);
$this->defaultRealValue = $defaultValue;
$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()
{
return 'selector_property_field';
}
public function AddSubForm($oSubForm, $sLabel, $sValue)
@@ -1505,18 +1260,12 @@ class DesignerFormSelectorField extends DesignerFormField
$sName = $this->oForm->GetFieldName($this->sCode);
$sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
$this->aCSSClasses[] = 'formSelector';
$sCSSClasses = '';
if (count($this->aCSSClasses) > 0)
{
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
}
if ($this->IsSorted())
{
uasort($this->aSubForms, array(get_class($this), 'SortOnFormLabel'));
}
if ($this->IsReadOnly())
{
@@ -1524,12 +1273,12 @@ class DesignerFormSelectorField extends DesignerFormField
$aHiddenValues = array();
$sDisplayValue = '';
$sHiddenValue = '';
foreach($this->aSubForms as $iKey => $aFormData)
foreach($this->aSubForms as $sKey => $aFormData)
{
if ($iKey == $this->defaultValue) // Default value is actually the index
if ($sKey == $this->defaultValue)
{
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');
$sHiddenValue = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($iKey, ENT_QUOTES, 'UTF-8')."\"/>";
$sHiddenValue = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\"/>";
break;
}
}
@@ -1540,12 +1289,11 @@ class DesignerFormSelectorField extends DesignerFormField
$sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\" $sReadOnly>";
foreach($this->aSubForms as $iKey => $aFormData)
foreach($this->aSubForms as $sKey => $aFormData)
{
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');
$sValue = htmlentities($aFormData['value'], ENT_QUOTES, 'UTF-8');
$sSelected = ($iKey == $this->defaultValue) ? 'selected' : '';
$sHtml .= "<option data-value=\"$sValue\" value=\"$iKey\" $sSelected>".$sDisplayValue."</option>";
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');;
$sSelected = ($sKey == $this->defaultValue) ? 'selected' : '';
$sHtml .= "<option value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\" $sSelected>".$sDisplayValue."</option>";
}
$sHtml .= "</select>";
}
@@ -1574,21 +1322,19 @@ class DesignerFormSelectorField extends DesignerFormField
// - data-path : uniquely identifies the combination of users choices that must be made to show the node
// - data-state : records the state, depending on the user choice on the FormSelectorField just above the node, but indepentantly from the visibility in the page (can be visible in the form itself being in a hidden form)
// Then a series of actions are performed to hide and show the relevant nodes, depending on the user choice
$sSelector = $this->oForm->GetHierarchyPath().'/'.$this->sCode.$this->oForm->GetSuffix();
$sSelector = $this->oForm->GetHierarchyPath().'/'.$this->sCode;
$oSubForm->SetHierarchyParent($sSelector);
$sPath = $this->oForm->GetHierarchyPath().'/'.$this->sCode.$this->oForm->GetSuffix().'-'.$sKey;
$sPath = $this->oForm->GetHierarchyPath().'/'.$this->sCode.':'.$sKey;
$oSubForm->SetHierarchyPath($sPath);
$oSubForm->SetDisplayed($sKey == $this->defaultValue);
$sState = ($sKey == $this->defaultValue) ? 'visible' : 'hidden';
//$sHtml .= "</tbody><tbody data-selector=\"$sSelector\" data-path=\"$sPath\" data-state=\"$sState\" $sStyle>";
$sHtml .= "</tbody><tbody data-selector=\"$sSelector\" data-path=\"$sPath\" data-state=\"$sState\" $sStyle>";
$sHtml .= $oSubForm->RenderAsPropertySheet($oP, true);
$sState = $this->oForm->IsDisplayed() ? 'visible' : 'hidden';
$sParentStyle = '';
if ($oParent = $this->oForm->GetParentForm())
{
$sParentStyle = ($oParent->IsDisplayed()) ? '' : 'style="display:none"';
$sParentSelector = $oParent->GetHierarchyParent();
$sParentPath = $oParent->GetHierarchyPath();
}
@@ -1597,8 +1343,7 @@ class DesignerFormSelectorField extends DesignerFormField
$sParentSelector = '';
$sParentPath = '';
}
//$sHtml .= "</tbody><tbody data-selector=\"$sParentSelector\" data-path=\"$sParentPath\" data-state=\"$sState\" $sParentStyle>";
$sHtml .= "</tbody><tbody data-selector=\"$sParentSelector\" data-path=\"$sParentPath\" data-state=\"$sState\" $sStyle>";
}
else
{
@@ -1610,8 +1355,27 @@ class DesignerFormSelectorField extends DesignerFormField
if ($sRenderMode == 'property')
{
$sSelector = $this->oForm->GetHierarchyPath().'/'.$this->sCode.$this->oForm->GetSuffix();
$this->aWidgetExtraParams['data_selector'] = $sSelector;
$sSelector = $this->oForm->GetHierarchyPath().'/'.$this->sCode;
$oP->add_ready_script(
<<<EOF
$('#$sId').bind('change reverted', function() {
// Mark all the direct children as hidden
$('tbody[data-selector="$sSelector"]').attr('data-state', 'hidden');
// Mark the selected one as visible
var sSelectedHierarchy = '$sSelector:'+this.value;
$('tbody[data-path="'+sSelectedHierarchy+'"]').attr('data-state', 'visible');
// Show all items behind the current one
$('tbody[data-path^="$sSelector"]').show();
// Hide items behind the current one as soon as it is behind a hidden node (or itself is marked as hidden)
$('tbody[data-path^="$sSelector"][data-state="hidden"]').each(function(){
$(this).hide();
var sPath = $(this).attr('data-path');
$('tbody[data-path^="'+sPath+'/"]').hide();
});
});
EOF
);
}
else
{
@@ -1634,44 +1398,6 @@ EOF
$this->aSubForms[$sKey]['form']->SetParentForm($this->oForm);
$this->aSubForms[$sKey]['form']->ReadParams($aValues);
}
public function SetDefaultValueFrom($aAllDefaultValues)
{
if (array_key_exists($this->GetCode(), $aAllDefaultValues))
{
$selectedValue = $aAllDefaultValues[$this->GetCode()];
foreach($this->aSubForms as $iKey => $aFormData)
{
$sId = $this->oForm->GetFieldId($this->sCode);
if ($selectedValue == $aFormData['value'])
{
$this->defaultValue =$iKey;
$aDefaultValues = $this->oForm->GetDefaultValues();
$oSubForm = $aFormData['form'];
$oSubForm->SetDefaultValues($aAllDefaultValues);
}
}
}
}
public function FindField($sFieldCode)
{
$oField = parent::FindField($sFieldCode);
if ($oField === false)
{
// Look in the subforms
foreach($this->aSubForms as $sKey => $aFormData)
{
$oSubForm = $aFormData['form'];
$oField = $oSubForm->FindField($sFieldCode);
if ($oField !== false)
{
break;
}
}
}
return $oField;
}
}
class DesignerSubFormField extends DesignerFormField
@@ -1704,31 +1430,6 @@ class DesignerSubFormField extends DesignerFormField
$this->oSubForm->SetParentForm($this->oForm);
$this->oSubForm->ReadParams($aValues);
}
public function FindField($sFieldCode)
{
$oField = parent::FindField($sFieldCode);
if ($oField === false)
{
// Look in the subform
$oField = $this->oSubForm->FindField($sFieldCode);
}
return $oField;
}
}
class DesignerStaticTextField extends DesignerFormField
{
public function __construct($sCode, $sLabel = '', $defaultValue = '')
{
parent::__construct($sCode, $sLabel, $defaultValue);
}
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
{
return array('label' => $this->sLabel, 'value' => $this->defaultValue);
}
}
?>

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2014 Combodo SARL
//
// This file is part of iTop.
//
@@ -20,7 +20,7 @@
/**
* Class iTopWebPage
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @copyright Copyright (C) 2010-2013 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -38,9 +38,9 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
private $m_sInitScript;
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();
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.dot.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/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'));
$sSearchNbSelected = addslashes(Dict::S('UI:SearchValue:NbSelected'));
$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);
$sInitClosed = '';
if (($bForceMenuPane !== null) && ($bForceMenuPane == 0))
@@ -126,17 +124,6 @@ EOF
myLayout.addPinBtn( "#tPinMenu", "west" );
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 =
<<< EOF
@@ -184,7 +171,6 @@ EOF;
var innerWidth = $(this).innerWidth() - 10;
$(this).find('.item').width(innerWidth);
});
$('.panel-resized').trigger('resized');
}
}
@@ -240,7 +226,9 @@ EOF;
});
}
});
$('.multiselect').multiselect($sJSMultiselectOptions);
$('.resizable').filter(':visible').resizable();
}
catch(err)
@@ -309,21 +297,6 @@ EOF
$.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,
// iterates over all tab widgets, changing the current tab as necessary.
$(window).bind( 'hashchange', function(e)
@@ -381,10 +354,7 @@ EOF
dateFormat: 'yy-mm-dd',
constrainInput: false,
changeMonth: true,
changeYear: true,
dayNamesMin: $sJSDaysMin,
monthNamesShort: $sJSMonthsShort,
firstDay: $iFirstDayOfWeek
changeYear: true
});
$(".datetime-pick").datepicker({
showOn: 'button',
@@ -393,10 +363,7 @@ EOF
dateFormat: 'yy-mm-dd 00:00:00',
constrainInput: false,
changeMonth: true,
changeYear: true,
dayNamesMin: $sJSDaysMin,
monthNamesShort: $sJSMonthsShort,
firstDay: $iFirstDayOfWeek
changeYear: true
});
// Make sortable, everything that claims to be sortable
@@ -417,26 +384,6 @@ EOF
$('#logOffBtn>ul').popupmenu();
$('.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
);
$sUserPrefs = appUserPreferences::GetAsJSON();
@@ -459,17 +406,12 @@ EOF
window.history.back();
}
function BackToDetails(sClass, id, sDefaultUrl, sOwnershipToken)
function BackToDetails(sClass, id, sDefaultUrl)
{
window.bInCancel = true;
if (id > 0)
{
sToken = '';
if (sOwnershipToken != undefined)
{
sToken = '&token='+sOwnershipToken;
}
window.location.href = AddAppContext(GetAbsoluteUrlAppRoot()+'pages/UI.php?operation=release_lock_and_details&class='+sClass+'&id='+id+sToken);
window.location.href = AddAppContext(GetAbsoluteUrlAppRoot()+'pages/UI.php?operation=details&class='+sClass+'&id='+id);
}
else
{
@@ -477,6 +419,7 @@ EOF
}
}
function BackToList(sClass)
{
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')),
null, 'select', false /* bSearchMultiple */);
$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
$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
@@ -625,21 +568,8 @@ EOF
{
$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
$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(
<<<EOF
// 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
$('table.listResults').each( function() { FixTableSorter($(this)); } );
$('.multiselect').multiselect($sJSMultiselectOptions);
FixSearchFormsDisposition();
EOF
);
if ($this->GetOutputFormat() == 'html')
@@ -662,7 +587,8 @@ EOF
header($s_header);
}
}
$s_captured_output = $this->ob_get_clean_safe();
$s_captured_output = ob_get_contents();
ob_end_clean();
$sHtml = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
$sHtml .= "<html>\n";
$sHtml .= "<head>\n";
@@ -675,26 +601,18 @@ EOF
// jQuery scripts may face some spurious problems (like failing on a 'reload')
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'] != "")
{
$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'] != "")
{
$sHtml .= "<![endif]-->\n";
}
}
// 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')
{
@@ -713,40 +631,7 @@ EOF
}
$sHtml .= "<script type=\"text/javascript\" src=\"$s_script\"></script>\n";
}
if (!$this->IsPrintableVersion())
{
$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
);
}
$this->add_script("var iPaneVisWatchDog = window.setTimeout('FixPaneVis()',5000);\n\$(document).ready(function() {\n{$this->m_sInitScript};\nwindow.setTimeout('onDelayedReady()',10)\n});");
if (count($this->m_aReadyScripts)>0)
{
$this->add_script("\nonDelayedReady = function() {\n".implode("\n", $this->m_aReadyScripts)."\n}\n");
@@ -772,25 +657,10 @@ EOF
$sHtml .= "</style>\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";
$sBodyClass = "";
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 .= '&nbsp;';
$sHtml .= '<button onclick="window.close()">'.htmlentities(Dict::S('UI:Button:Cancel'), ENT_QUOTES, 'UTF-8').'</button>';
$sHtml .= "</div>";
}
$sHtml .= "<body>\n";
// Render the revision number
if (ITOP_REVISION == '$WCREV$')
@@ -806,19 +676,19 @@ EOF
// Render the text of the global search form
$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 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");
$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())
{
$sHtml .= ' <!-- Beginning of page content -->';
$sHtml .= self::FilterXSS($this->s_content);
$sHtml .= ' <!-- End of page content -->';
}
elseif ($this->GetOutputFormat() == 'html')
if ($this->GetOutputFormat() == 'html')
{
$oAppContext = new ApplicationContext();
@@ -832,7 +702,7 @@ EOF
{
$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";
$aActions = array();
@@ -841,7 +711,7 @@ EOF
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();
}
if (UserRights::CanChangePassword())
@@ -905,10 +775,10 @@ EOF
$sOnlineHelpUrl = MetaModel::GetConfig()->Get('online_help');
//$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'))
{
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/main-logo.png?itopversion='.ITOP_VERSION;
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/main-logo.png';
}
$sHtml .= $sNorthPane;
@@ -935,17 +805,17 @@ EOF
$sHtml .= ' </div>';
$sHtml .= ' </div> <!-- /inner 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 .= '</div>';
$sHtml .= '<div class="ui-layout-center">';
$sHtml .= ' <div id="top-bar" style="width:100%">';
$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 .= '<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>'.self::FilterXSS($sLogOffMenu).'</td><td><input type="hidden" name="operation" value="full_text"/></td></tr></table></form></div>';
$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><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 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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="hidden" name="operation" value="full_text"/></td></tr></table></form></div>';
$sHtml .= ' </div>';
$sHtml .= ' <div class="ui-layout-content" style="overflow:auto;">';
@@ -982,31 +852,28 @@ EOF
}
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 = 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->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');
}
DBSearch::RecordQueryTrace();
MetaModel::RecordQueryTrace();
ExecutionKPI::ReportStats();
}
@@ -1172,3 +1039,4 @@ EOF
$this->m_sMessage = $sMessage;
}
}
?>

View File

@@ -25,7 +25,6 @@
*/
require_once(APPROOT."/application/nicewebpage.class.inc.php");
require_once(APPROOT.'/application/portaldispatcher.class.inc.php');
/**
* Web page used for displaying the login form
*/
@@ -92,10 +91,10 @@ class LoginWebPage extends NiceWebPage
}
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_VERSION);
$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))
{
$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");
}
@@ -306,16 +305,20 @@ class LoginWebPage extends NiceWebPage
{
$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
{
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-EnterPassword', $oUser->GetFriendlyName())."</p>\n");
$sInconsistenPwdMsg = Dict::S('UI:Login:RetypePwdDoesNotMatch');
$this->add_script(
$oEncryptedToken = $oUser->Get('reset_pwd_token');
if (!$oEncryptedToken->CheckPassword($sToken))
{
$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
function DoCheckPwd()
{
@@ -327,18 +330,19 @@ function DoCheckPwd()
return true;
}
EOF
);
$this->add("<form method=\"post\">\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=\"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("</table>\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=\"token\" value=\"".htmlentities($sToken, ENT_QUOTES, 'UTF-8')."\" />\n");
$this->add("</form>\n");
$this->add("</div\n");
);
$this->add("<form method=\"post\">\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=\"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("</table>\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=\"token\" value=\"".htmlentities($sToken, ENT_QUOTES, 'UTF-8')."\" />\n");
$this->add("</form>\n");
$this->add("</div\n");
}
}
}
@@ -358,21 +362,25 @@ EOF
{
$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
{
// 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>");
$oEncryptedPassword = $oUser->Get('reset_pwd_token');
if (!$oEncryptedPassword->CheckPassword($sToken))
{
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
}
else
{
// 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)
@@ -429,7 +437,6 @@ EOF
// Unset all of the session variables.
unset($_SESSION['auth_user']);
unset($_SESSION['login_mode']);
unset($_SESSION['profile_list']);
// 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!
}
@@ -581,13 +588,6 @@ EOF
{
$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'))
{
header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_VERSION));
@@ -663,22 +663,12 @@ EOF
/**
* 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
*/
protected static function ChangeLocation($sRequestedPortalId = null, $iOnExit = self::EXIT_PROMPT)
protected static function ChangeLocation($bIsAllowedToPortalUsers, $iOnExit = self::EXIT_PROMPT)
{
$fStart = microtime(true);
$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 ( (!$bIsAllowedToPortalUsers) && (UserRights::IsPortalUser()))
{
if ($iOnExit == self::EXIT_RETURN)
{
@@ -687,11 +677,16 @@ EOF
else
{
// 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:
* - if $bMustBeAdmin is true, then the user must be an administrator, otherwise an error is displayed
@@ -702,56 +697,9 @@ EOF
*/
static function DoLogin($bMustBeAdmin = false, $bIsAllowedToPortalUsers = false, $iOnExit = self::EXIT_PROMPT)
{
$sRequestedPortalId = $bIsAllowedToPortalUsers ? 'legacy_portal' : 'backoffice';
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)
{
$sMessage = ''; // In case we need to return a message to the calling web page
$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 (isset($_SESSION['login_mode']))
@@ -775,7 +723,7 @@ EOF
$oPage->DisplayLoginForm( $sLoginMode, false /* not a failed attempt */);
$oPage->output();
exit;
}
}
else if ($operation == 'forgot_pwd')
{
$oPage = self::NewLoginWebPage();
@@ -828,54 +776,36 @@ EOF
}
$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();
$aDispatchers = array();
foreach($aPortalsConf as $sPortalId => $aConf)
$iRet = self::Login($iOnExit);
if ($iRet == self::EXIT_CODE_OK)
{
$sHandlerClass = $aConf['handler'];
$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())
if ($bMustBeAdmin && !UserRights::IsAdministrator())
{
$aAllowedPortals[] = array(
'id' => $sPortalId,
'label' => $oDispatcher->GetLabel(),
'url' => $oDispatcher->GetUrl(),
);
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'), $bIsAllowedToPortalUsers, $iOnExit);
}
return $aAllowedPortals;
}
if ($iOnExit == self::EXIT_RETURN)
{
return $iRet;
}
else
{
return $sMessage;
}
}
} // End of class

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
@@ -20,7 +20,7 @@
/**
* Class NiceWebPage
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -33,9 +33,9 @@ class NiceWebPage extends WebPage
var $m_aReadyScripts;
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->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
@@ -94,7 +94,7 @@ class NiceWebPage extends WebPage
$("table.listResults").tableHover(); // hover tables
EOF
);
$this->add_saas("css/light-grey.scss");
$this->add_linked_stylesheet("../css/light-grey.css");
$this->m_sRootUrl = $this->GetAbsoluteUrlAppRoot();
$sAbsURLAppRoot = addslashes($this->m_sRootUrl);

View File

@@ -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');
}
}

View File

@@ -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'];
}
}

View File

@@ -88,21 +88,8 @@ class PortalWebPage extends NiceWebPage
$this->add_linked_script("../js/forms-json-utils.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.multiselect.js');
$this->add_linked_script('../js/jquery.multiselect.min.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(
<<<EOF
try
@@ -144,17 +131,14 @@ try
});
$(".date-pick").datepicker({
showOn: 'button',
buttonImage: '../images/calendar.png',
buttonImageOnly: true,
dateFormat: 'yy-mm-dd',
constrainInput: false,
changeMonth: true,
changeYear: true,
dayNamesMin: $sJSDaysMin,
monthNamesShort: $sJSMonthsShort,
firstDay: $iFirstDayOfWeek
});
showOn: 'button',
buttonImage: '../images/calendar.png',
buttonImageOnly: true,
dateFormat: 'yy-mm-dd',
constrainInput: false,
changeMonth: true,
changeYear: true
});
$(".datetime-pick").datepicker({
showOn: 'button',
@@ -163,33 +147,11 @@ try
dateFormat: 'yy-mm-dd 00:00:00',
constrainInput: false,
changeMonth: true,
changeYear: true,
dayNamesMin: $sJSDaysMin,
monthNamesShort: $sJSMonthsShort,
firstDay: $iFirstDayOfWeek
});
changeYear: true
});
//$('.resizable').resizable(); // Make resizable everything that claims to be resizable !
$('.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)
{
@@ -260,7 +222,7 @@ EOF
{
var form = $('FORM');
form.unbind('submit'); // De-activate validation
window.location.href = window.location.href.replace(/[&?]operation=[^&]*/, '');
window.location.href = '?operation=';
return false;
}
@@ -269,20 +231,6 @@ EOF
var next_step = $('input[id=next_step]');
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
);
@@ -348,7 +296,7 @@ EOF
$sMenu = '';
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)
{
@@ -807,17 +755,6 @@ EOF
$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);
$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))
{

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
@@ -22,7 +22,7 @@
* Application internal events
* 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
*/
@@ -92,18 +92,7 @@ class QueryOQL extends Query
if (!$bEditMode)
{
$sFields = trim($this->Get('fields'));
$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();
}
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?format=spreadsheet&login_mode=basic&query='.$this->GetKey();
$sOql = $this->Get('oql');
$sMessage = null;
try

View File

@@ -28,10 +28,11 @@ require_once(APPROOT.'/core/cmdbobject.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
session_name('itop-'.md5(APPROOT));
session_start();
if (isset($_REQUEST['switch_env']))
$sSwitchEnv = utils::ReadParam('switch_env', null);
if (($sSwitchEnv != null) && (file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE)))
{
$sEnv = $_REQUEST['switch_env'];
$_SESSION['itop_env'] = $sEnv;
$_SESSION['itop_env'] = $sSwitchEnv;
$sEnv = $sSwitchEnv;
// TODO: reset the credentials as well ??
}
else if (isset($_SESSION['itop_env']))

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
@@ -19,8 +19,7 @@
/**
* 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
* until the user's session expires. This class is actually a wrapper to the underlying implementation
* which choice is configured via the parameter 'transaction_storage'
* until the user's session expires
*
* @package iTop
* @copyright Copyright (C) 2010-2012 Combodo SARL
@@ -29,81 +28,6 @@
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
@@ -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);
}
}
}
}
?>

View File

@@ -164,7 +164,6 @@ class UIExtKeyWidget
break;
case 'select':
case 'list':
default:
$sSelectMode = 'true';
@@ -253,14 +252,14 @@ EOF
$sDisplayValue = $this->GetObjectName($value);
}
$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
$sHTMLValue = "<input count=\"".$oAllowedValues->Count()."\" type=\"text\" id=\"label_$this->iId\" size=\"$iFieldSize\" value=\"$sDisplayValue\"/>&nbsp;";
$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();\"/>&nbsp;";
// 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';
// Scripts to start the autocomplete and bind some events to it
@@ -281,7 +280,7 @@ EOF
}
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();\"/>&nbsp;";
$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();\"/>&nbsp;";
$oPage->add_ready_script(
<<<EOF
if ($('#ac_tree_{$this->iId}').length == 0)
@@ -293,7 +292,7 @@ EOF
}
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();\"/>&nbsp;";
$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();\"/>&nbsp;";
$oPage->add_ready_script(
<<<EOF
if ($('#ajax_{$this->iId}').length == 0)
@@ -303,7 +302,7 @@ EOF
EOF
);
}
if (($sDisplayStyle == 'select') || ($sDisplayStyle == 'list'))
if ($sDisplayStyle == 'select')
{
$sHTMLValue .= "<span id=\"v_{$this->iId}\"></span>";
}

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
@@ -21,15 +21,13 @@
* UI wdiget for displaying and editing one-way encrypted passwords
*
* @author Phil Eddies
* @author Romain Quetiez
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class UIHTMLEditorWidget
{
protected $m_iId;
protected $m_oAttDef;
protected $m_sAttCode;
protected $m_sNameSuffix;
protected $m_sFieldPrefix;
@@ -38,11 +36,10 @@ class UIHTMLEditorWidget
protected $m_sValue;
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_oAttDef = $oAttDef;
$this->m_sAttCode = $oAttDef->GetCode();
$this->m_sAttCode = $sAttCode;
$this->m_sNameSuffix = $sNameSuffix;
$this->m_sHelpText = $sHelpText;
$this->m_sValidationField = $sValidationField;
@@ -71,24 +68,8 @@ class UIHTMLEditorWidget
// To change the default settings of the editor,
// a) edit the file /js/ckeditor/config.js
// b) or override some of the configuration settings, using the second parameter of ckeditor()
$aConfig = array();
$sLanguage = strtolower(trim(UserRights::GetUserLanguage()));
$aConfig['language'] = $sLanguage;
$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
$oPage->add_ready_script("$('#$iId').ckeditor(function() { /* callback code */ }, { language : '$sLanguage' , contentsLanguage : '$sLanguage', extraPlugins: 'disabler' });"); // Transform $iId into a CKEdit
// Please read...
// ValidateCKEditField triggers a timer... calling itself indefinitely
@@ -99,7 +80,7 @@ class UIHTMLEditorWidget
// 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('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;
}

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
@@ -19,7 +19,7 @@
/**
* Class UILinksWidgetDirect
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -89,7 +89,7 @@ class UILinksWidgetDirect
$sDefault = "default[$sExtKeyToMe]=".$oCurrentObj->GetKey();
$oAppContext = new ApplicationContext();
$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*/);
break;
@@ -294,7 +294,7 @@ class UILinksWidgetDirect
$valuesDef = $oLinksetDef->GetValuesDef();
if ($valuesDef === null)
{
$oFilter = new DBObjectSearch($sRemoteClass);
$oFilter = new DBObjectSearch($this->sLinkedClass);
}
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
* @param DBObject $oSourceObj
* @param DBSearch $oSearch
* @param DBObjectSearch $oSearch
*/
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
{

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
@@ -20,7 +20,7 @@
/**
* Class UILinksWidget
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -163,12 +163,6 @@ class UILinksWidget
$aFieldsMap[$sFieldCode] = $sSafeId;
}
$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(
<<<EOF
$(".date-pick").datepicker({
@@ -178,11 +172,8 @@ $(".date-pick").datepicker({
dateFormat: 'yy-mm-dd',
constrainInput: false,
changeMonth: true,
changeYear: true,
dayNamesMin: $sJSDaysMin,
monthNamesShort: $sJSMonthsShort,
firstDay: $iFirstDayOfWeek
});
changeYear: true
});
$(".datetime-pick").datepicker({
showOn: 'button',
buttonImage: '../images/calendar.png',
@@ -190,10 +181,7 @@ $(".datetime-pick").datepicker({
dateFormat: 'yy-mm-dd 00:00:00',
constrainInput: false,
changeMonth: true,
changeYear: true,
dayNamesMin: $sJSDaysMin,
monthNamesShort: $sJSMonthsShort,
firstDay: $iFirstDayOfWeek
changeYear: true
});
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
* @param DBObject $oSourceObj
* @param DBSearch $oSearch
* @param DBObjectSearch $oSearch
*/
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
{

View File

@@ -275,7 +275,7 @@ EOF
$this->DisplayFormTable($oP, $this->m_aTableConfig, $aForm);
$oP->add("<span style=\"float:left;\">&nbsp;&nbsp;&nbsp;<img src=\"../images/tv-item-last.gif\">&nbsp;&nbsp;<input id=\"btnRemove\" type=\"button\" value=\"".Dict::S('UI:RemoveLinkedObjectsOf_Class')."\" onClick=\"RemoveSelected();\" >");
$oP->add("&nbsp;&nbsp;&nbsp;<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("&nbsp;&nbsp;&nbsp;<input id=\"btnOk\" type=\"submit\" value=\"".Dict::S('UI:Button:Ok')."\"></span>\n");
$oP->add("<span style=\"clear:both;\"><p>&nbsp;</p></span>\n");
$oP->add("</div>\n");

View File

@@ -130,7 +130,7 @@ class appUserPreferences extends DBObject
/**
* Call this function if the user has changed (like when doing a logoff...)
*/
static public function ResetPreferences()
static public function Reset()
{
self::$oUserPrefs = null;
}

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2013 Combodo SARL
//
// This file is part of iTop.
//
@@ -20,7 +20,7 @@
/**
* 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
*/
@@ -334,7 +334,7 @@ class utils
/**
* 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
*/
public static function ReadMultipleSelection($oFullSetFilter)
@@ -487,23 +487,19 @@ class utils
*/
static public function GetAbsoluteUrlAppRoot()
{
static $sUrl = null;
if ($sUrl === null)
$sUrl = self::GetConfig()->Get('app_root_url');
if (strpos($sUrl, SERVER_NAME_PLACEHOLDER) > -1)
{
$sUrl = self::GetConfig()->Get('app_root_url');
if (strpos($sUrl, SERVER_NAME_PLACEHOLDER) > -1)
if (isset($_SERVER['SERVER_NAME']))
{
if (isset($_SERVER['SERVER_NAME']))
{
$sServerName = $_SERVER['SERVER_NAME'];
}
else
{
// CLI mode ?
$sServerName = php_uname('n');
}
$sUrl = str_replace(SERVER_NAME_PLACEHOLDER, $sServerName, $sUrl);
$sServerName = $_SERVER['SERVER_NAME'];
}
else
{
// CLI mode ?
$sServerName = php_uname('n');
}
$sUrl = str_replace(SERVER_NAME_PLACEHOLDER, $sServerName, $sUrl);
}
return $sUrl;
}
@@ -787,53 +783,30 @@ class utils
$sOQL = addslashes($param->GetFilter()->ToOQL(true));
$sFilter = urlencode($param->GetFilter()->serialize());
$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/jquery.dragtable.js');
$oPage->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/dragtable.css');
$aResult = array(
new SeparatorPopupMenuItem(),
// 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:CSVExport', Dict::S('UI:Menu:CSVExport'), $sUrl."&format=csv"),
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;
case iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS:
// $param is a DBObject
$oObj = $param;
$sOQL = "SELECT ".get_class($oObj)." WHERE id=".$oObj->GetKey();
$oFilter = DBObjectSearch::FromOQL($sOQL);
$oFilter = DBobjectSearch::FromOQL("SELECT ".get_class($oObj)." WHERE id=".$oObj->GetKey());
$sFilter = $oFilter->serialize();
$sUrl = ApplicationContext::MakeObjectUrl(get_class($oObj), $oObj->GetKey());
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage(get_class($oObj));
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
$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');
$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(
new SeparatorPopupMenuItem(),
// 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 JSPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), "ExportListDlg('$sOQL', '', 'csv', ".json_encode(Dict::S('UI:Menu:CSVExport')).")"),
new JSPopupMenuItem('UI:Menu:ExportXLSX', Dict::S('ExcelExporter:ExportMenu'), "ExportListDlg('$sOQL', '', 'xlsx', ".json_encode(Dict::S('ExcelExporter:ExportMenu')).")"),
new SeparatorPopupMenuItem(),
new URLPopupMenuItem('UI:Menu:PrintableVersion', Dict::S('UI:Menu:PrintableVersion'), $sUrl.'&printable=1', '_blank'),
new URLPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".urlencode($sFilter)."&format=csv&{$sContext}"),
);
break;
@@ -980,11 +953,10 @@ class utils
* @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 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
* @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.
@@ -1002,7 +974,6 @@ class utils
$aHTTPHeaders[$aMatches[1]] = $aMatches[2];
}
}
// Default options, can be overloaded/extended with the 4th parameter of this method, see above $aCurlOptions
$aOptions = array(
CURLOPT_RETURNTRANSFER => true, // return the content of the request
CURLOPT_HEADER => false, // don't return the headers in the output
@@ -1014,17 +985,14 @@ class utils
CURLOPT_TIMEOUT => 120, // timeout on response
CURLOPT_MAXREDIRS => 10, // stop after 10 redirects
CURLOPT_SSL_VERIFYPEER => false, // Disabled SSL Cert checks
// SSLV3 (CURL_SSLVERSION_SSLv3 = 3) is now considered as obsolete/dangerous: http://disablessl3.com/#why
// 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_SSLVERSION => 3, // MUST to prevent a strange SSL error: http://stackoverflow.com/questions/18191672/php-curl-ssl-routinesssl23-get-server-helloreason1112
CURLOPT_POST => count($aData),
CURLOPT_POSTFIELDS => http_build_query($aData),
CURLOPT_HTTPHEADER => $aHTTPHeaders,
);
$aAllOptions = $aCurlOptions + $aOptions;
$ch = curl_init($sUrl);
curl_setopt_array($ch, $aAllOptions);
curl_setopt_array($ch, $aOptions);
$response = curl_exec($ch);
$iErr = curl_errno($ch);
$sErrMsg = curl_error( $ch );
@@ -1097,28 +1065,5 @@ class utils
}
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;
}
}
?>

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2013 Combodo SARL
//
// This file is part of iTop.
//
@@ -20,7 +20,7 @@
/**
* Class WebPage
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -68,12 +68,10 @@ class WebPage implements Page
protected $sContentType;
protected $sContentDisposition;
protected $sContentFileName;
protected $bTrashUnexpectedOutput;
protected $s_sOutputFormat;
protected $a_OutputOptions;
protected $bPrintable;
public function __construct($s_title, $bPrintable = false)
public function __construct($s_title)
{
$this->s_title = $s_title;
$this->s_content = "";
@@ -90,10 +88,8 @@ class WebPage implements Page
$this->sContentType = '';
$this->sContentDisposition = '';
$this->sContentFileName = '';
$this->bTrashUnexpectedOutput = false;
$this->s_OutputFormat = utils::ReadParam('output_format', 'html');
$this->a_OutputOptions = array();
$this->bPrintable = $bPrintable;
ob_start(); // Start capturing the output
}
@@ -270,33 +266,7 @@ class WebPage implements Page
{
$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
*/
@@ -322,15 +292,6 @@ class WebPage implements Page
$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
@@ -431,55 +392,22 @@ class WebPage implements Page
}
/**
* Discard unexpected output data (such as PHP warnings)
* Discard unexpected output data
* This is a MUST when the Page output is DATA (download of a document, download CSV export, download ...)
*/
public function TrashUnexpectedOutput()
{
$this->bTrashUnexpectedOutput = true;
}
/**
* Read the output buffer and deal with its contents:
* - trash unexpected output if the flag has been set
* - report unexpected behaviors such as the output buffering being stopped
*
* Possible improvement: I've noticed that several output buffers are stacked,
* if they are not empty, the output will be corrupted. The solution would
* consist in unstacking all of them (and concatenate the contents).
*/
protected function ob_get_clean_safe()
{
$sOutput = ob_get_contents();
if ($sOutput === false)
// This protection is redundant with a protection implemented in MetaModel::IncludeModule
// which detects such issues while loading module files
// Here, the purpose is to detect and discard characters produced by the code execution (echo)
$sPreviousContent = ob_get_clean();
if (trim($sPreviousContent) != '')
{
$sMsg = "Design/integration issue: No output buffer. Some piece of code has called ob_get_clean() or ob_end_clean() without calling ob_start()";
if ($this->bTrashUnexpectedOutput)
if (Utils::GetConfig() && Utils::GetConfig()->Get('debug_report_spurious_chars'))
{
IssueLog::Error($sMsg);
$sOutput = '';
}
else
{
$sOutput = $sMsg;
IssueLog::Error("Output already started before downloading file:\nContent was:'$sPreviousContent'\n");
}
}
else
{
ob_end_clean(); // on some versions of PHP doing so when the output buffering is stopped can cause a notice
if ($this->bTrashUnexpectedOutput)
{
if (trim($sOutput) != '')
{
if (Utils::GetConfig() && Utils::GetConfig()->Get('debug_report_spurious_chars'))
{
IssueLog::Error("Trashing unexpected output:'$s_captured_output'\n");
}
}
$sOutput = '';
}
}
return $sOutput;
}
/**
@@ -491,8 +419,8 @@ class WebPage implements Page
{
header($s_header);
}
$s_captured_output = $this->ob_get_clean_safe();
$s_captured_output = ob_get_contents();
ob_end_clean();
echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
echo "<html>\n";
echo "<head>\n";
@@ -525,19 +453,11 @@ class WebPage implements Page
$this->output_dict_entries();
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'] != "")
{
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'] != "")
{
echo "<![endif]-->\n";
@@ -555,7 +475,7 @@ class WebPage implements Page
}
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 "<body>\n";
@@ -568,9 +488,9 @@ class WebPage implements Page
echo "</body>\n";
echo "</html>\n";
if (class_exists('DBSearch'))
if (class_exists('MetaModel'))
{
DBSearch::RecordQueryTrace();
MetaModel::RecordQueryTrace();
}
if (class_exists('ExecutionKPI'))
{
@@ -697,16 +617,7 @@ class WebPage implements Page
}
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
* @param string $sFormat The format: html or pdf
@@ -743,34 +654,32 @@ class WebPage implements Page
{
$sPrevUrl = '';
$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']}\"" : "";
$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...
{
if ($sPrevUrl != '') // Don't output consecutively two separators...
{
$sHtml .= "<li>{$aAction['label']}</li>";
}
$sPrevUrl = '';
}
else
{
$sHtml .= "<li><a $sTarget href=\"{$aAction['url']}\"$sClass $sOnClick>{$aAction['label']}</a></li>";
$sPrevUrl = $aAction['url'];
$sHtml .= "<li>{$aAction['label']}</li>";
}
$sPrevUrl = '';
}
$sHtml .= "</ul></li></ul></div>";
foreach(array_reverse($aFavoriteActions) as $aAction)
else
{
$sTarget = isset($aAction['target']) ? " target=\"{$aAction['target']}\"" : "";
$sHtml .= "<div class=\"actions_button\"><a $sTarget href='{$aAction['url']}'>{$aAction['label']}</a></div>";
$sHtml .= "<li><a $sTarget href=\"{$aAction['url']}\"$sClass $sOnClick>{$aAction['label']}</a></li>";
$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;
}
@@ -994,11 +903,11 @@ class TabManager
/**
* 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)
{
$result = false;
$return = false;
if ($sTabContainer == null)
{
$sTabContainer = $this->m_sCurrentTabContainer;
@@ -1044,7 +953,7 @@ class TabManager
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)
foreach($this->m_aTabs as $sTabContainerName => $aTabs)
@@ -1054,86 +963,42 @@ class TabManager
$container_index = 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(
<<< EOF
oHiddeableChapters = {};
EOF
);
$sTabs = "<!-- tabs -->\n<div id=\"tabbedContent_{$sPrefix}{$container_index}\" class=\"light\">\n";
$i = 0;
foreach($aTabs['tabs'] as $sTabName => $aTabData)
switch($aTabData['type'])
{
$sTabNameEsc = addslashes($sTabName);
$sTabId = "tab_{$sPrefix}{$container_index}$i";
switch($aTabData['type'])
{
case 'ajax':
$sTabHtml = '';
$sUrl = $aTabData['url'];
$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++;
case 'ajax':
$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:
$sTabs .= "<li><a href=\"#tab_{$sPrefix}{$container_index}$i\" class=\"tab\"><span>".htmlentities($sTabName, ENT_QUOTES, 'UTF-8')."</span></a></li>\n";
}
$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";
$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'])
{
switch($aTabData['type'])
{
case 'ajax':
$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:
$sTabs .= "<li><a href=\"#tab_{$sPrefix}{$container_index}$i\" class=\"tab\"><span>".htmlentities($sTabName, ENT_QUOTES, 'UTF-8')."</span></a></li>\n";
}
$i++;
case 'ajax':
// Nothing to add
break;
case 'html':
default:
$sTabs .= "<div id=\"tab_{$sPrefix}{$container_index}$i\">".$aTabData['html']."</div>\n";
}
$sTabs .= "</ul>\n";
// 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";
$i++;
}
$sTabs .= "</div>\n<!-- end of tabs-->\n";
}
$sContent = str_replace("\$Tabs:$sTabContainerName\$", $sTabs, $sContent);
$container_index++;

View File

@@ -113,16 +113,8 @@ class WizardHelper
{
// For external keys: load the target object so that external fields
// get filled too
$oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value, false);
if ($oTargetObj)
{
$oObj->Set($sAttCode, $oTargetObj);
}
else
{
// May happen for security reasons (portal, see ticket #1074)
$oObj->Set($sAttCode, $value);
}
$oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value);
$oObj->Set($sAttCode, $oTargetObj);
}
else
{

View File

@@ -1,459 +0,0 @@
<?php
/* @author Mark Jones
* @license MIT License
* */
if (!class_exists('ZipArchive')) { throw new Exception('ZipArchive not found'); }
Class XLSXWriter
{
//------------------------------------------------------------------
protected $author ='Doc Author';
protected $sheets_meta = array();
protected $shared_strings = array();//unique set
protected $shared_string_count = 0;//count of non-unique references to the unique set
protected $temp_files = array();
public function __construct(){}
public function setAuthor($author='') { $this->author=$author; }
public function __destruct()
{
if (!empty($this->temp_files)) {
foreach($this->temp_files as $temp_file) {
@unlink($temp_file);
}
}
}
protected function tempFilename()
{
$filename = tempnam("/tmp", "xlsx_writer_");
$this->temp_files[] = $filename;
return $filename;
}
public function writeToStdOut()
{
$temp_file = $this->tempFilename();
self::writeToFile($temp_file);
readfile($temp_file);
}
public function writeToString()
{
$temp_file = $this->tempFilename();
self::writeToFile($temp_file);
$string = file_get_contents($temp_file);
return $string;
}
public function writeToFile($filename)
{
@unlink($filename);//if the zip already exists, overwrite it
$zip = new ZipArchive();
if (empty($this->sheets_meta)) { self::log("Error in ".__CLASS__."::".__FUNCTION__.", no worksheets defined."); return; }
if (!$zip->open($filename, ZipArchive::CREATE)) { self::log("Error in ".__CLASS__."::".__FUNCTION__.", unable to create zip."); return; }
$zip->addEmptyDir("docProps/");
$zip->addFromString("docProps/app.xml" , self::buildAppXML() );
$zip->addFromString("docProps/core.xml", self::buildCoreXML());
$zip->addEmptyDir("_rels/");
$zip->addFromString("_rels/.rels", self::buildRelationshipsXML());
$zip->addEmptyDir("xl/worksheets/");
foreach($this->sheets_meta as $sheet_meta) {
$zip->addFile($sheet_meta['filename'], "xl/worksheets/".$sheet_meta['xmlname'] );
}
if (!empty($this->shared_strings)) {
$zip->addFile($this->writeSharedStringsXML(), "xl/sharedStrings.xml" ); //$zip->addFromString("xl/sharedStrings.xml", self::buildSharedStringsXML() );
}
$zip->addFromString("xl/workbook.xml" , self::buildWorkbookXML() );
$zip->addFile($this->writeStylesXML(), "xl/styles.xml" ); //$zip->addFromString("xl/styles.xml" , self::buildStylesXML() );
$zip->addFromString("[Content_Types].xml" , self::buildContentTypesXML() );
$zip->addEmptyDir("xl/_rels/");
$zip->addFromString("xl/_rels/workbook.xml.rels", self::buildWorkbookRelsXML() );
$zip->close();
}
public function writeSheet(array $data, $sheet_name='', array $header_types=array(), array $header_row=array() )
{
$data = empty($data) ? array( array('') ) : $data;
$sheet_filename = $this->tempFilename();
$sheet_default = 'Sheet'.(count($this->sheets_meta)+1);
$sheet_name = !empty($sheet_name) ? $sheet_name : $sheet_default;
$this->sheets_meta[] = array('filename'=>$sheet_filename, 'sheetname'=>$sheet_name ,'xmlname'=>strtolower($sheet_default).".xml" );
$header_offset = empty($header_types) ? 0 : 1;
$row_count = count($data) + $header_offset;
$column_count = count($data[self::array_first_key($data)]);
$max_cell = self::xlsCell( $row_count-1, $column_count-1 );
$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);
if (empty($header_row) && !empty($header_types))
{
$header_row = empty($header_types) ? array() : array_keys($header_types);
}
$fd = fopen($sheet_filename, "w+");
if ($fd===false) { self::log("write failed in ".__CLASS__."::".__FUNCTION__."."); return; }
fwrite($fd,'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n");
fwrite($fd,'<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">');
fwrite($fd, '<sheetPr filterMode="false">');
fwrite($fd, '<pageSetUpPr fitToPage="false"/>');
fwrite($fd, '</sheetPr>');
fwrite($fd, '<dimension ref="A1:'.$max_cell.'"/>');
fwrite($fd, '<sheetViews>');
fwrite($fd, '<sheetView colorId="64" defaultGridColor="true" rightToLeft="false" showFormulas="false" showGridLines="true" showOutlineSymbols="true" showRowColHeaders="true" showZeros="true" tabSelected="'.$tabselected.'" topLeftCell="A1" view="normal" windowProtection="false" workbookViewId="0" zoomScale="100" zoomScaleNormal="100" zoomScalePageLayoutView="100">');
fwrite($fd, '<selection activeCell="A1" activeCellId="0" pane="topLeft" sqref="A1"/>');
fwrite($fd, '</sheetView>');
fwrite($fd, '</sheetViews>');
fwrite($fd, '<cols>');
fwrite($fd, '<col collapsed="false" hidden="false" max="1025" min="1" style="0" width="19"/>');
fwrite($fd, '</cols>');
fwrite($fd, '<sheetData>');
if (!empty($header_row))
{
fwrite($fd, '<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="'.(1).'">');
foreach($header_row as $k=>$v)
{
$this->writeCell($fd, 0, $k, $v, $cell_format='string');
}
fwrite($fd, '</row>');
}
foreach($data as $i=>$row)
{
fwrite($fd, '<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="'.($i+$header_offset+1).'">');
foreach($row as $k=>$v)
{
$this->writeCell($fd, $i+$header_offset, $k, $v, $cell_formats_arr[$k]);
}
fwrite($fd, '</row>');
}
fwrite($fd, '</sheetData>');
fwrite($fd, '<printOptions headings="false" gridLines="false" gridLinesSet="true" horizontalCentered="false" verticalCentered="false"/>');
fwrite($fd, '<pageMargins left="0.5" right="0.5" top="1.0" bottom="1.0" header="0.5" footer="0.5"/>');
fwrite($fd, '<pageSetup blackAndWhite="false" cellComments="none" copies="1" draft="false" firstPageNumber="1" fitToHeight="1" fitToWidth="1" horizontalDpi="300" orientation="portrait" pageOrder="downThenOver" paperSize="1" scale="100" useFirstPageNumber="true" usePrinterDefaults="false" verticalDpi="300"/>');
fwrite($fd, '<headerFooter differentFirst="false" differentOddEven="false">');
fwrite($fd, '<oddHeader>&amp;C&amp;&quot;Times New Roman,Regular&quot;&amp;12&amp;A</oddHeader>');
fwrite($fd, '<oddFooter>&amp;C&amp;&quot;Times New Roman,Regular&quot;&amp;12Page &amp;P</oddFooter>');
fwrite($fd, '</headerFooter>');
fwrite($fd,'</worksheet>');
fclose($fd);
}
protected function writeCell($fd, $row_number, $column_number, $value, $cell_format)
{
static $styles = array('money'=>1,'dollar'=>1,'datetime'=>2,'date'=>3,'string'=>0);
$cell = self::xlsCell($row_number, $column_number);
$s = isset($styles[$cell_format]) ? $styles[$cell_format] : '0';
if (is_int($value) || is_float($value)) {
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.($value*1).'</v></c>');//int,float, etc
} else if ($cell_format=='date') {
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.intval(self::convert_date_time($value)).'</v></c>');
} else if ($cell_format=='datetime') {
if ($value === '') {
fwrite($fd,'<c r="'.$cell.'" s="0"/>');
} else {
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.self::convert_date_time($value).'</v></c>');
}
} else if ($value==''){
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'"/>');
} else if ($value{0}=='='){
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="s"><f>'.self::xmlspecialchars($value).'</f></c>');
} else if ($value!==''){
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="s"><v>'.self::xmlspecialchars($this->setSharedString($value)).'</v></c>');
}
}
protected function writeStylesXML()
{
$tempfile = $this->tempFilename();
$fd = fopen($tempfile, "w+");
if ($fd===false) { self::log("write failed in ".__CLASS__."::".__FUNCTION__."."); return; }
fwrite($fd, '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n");
fwrite($fd, '<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">');
fwrite($fd, '<numFmts count="4">');
fwrite($fd, '<numFmt formatCode="GENERAL" numFmtId="164"/>');
fwrite($fd, '<numFmt formatCode="[$$-1009]#,##0.00;[RED]\-[$$-1009]#,##0.00" numFmtId="165"/>');
fwrite($fd, '<numFmt formatCode="YYYY-MM-DD\ HH:MM:SS" numFmtId="166"/>');
fwrite($fd, '<numFmt formatCode="YYYY-MM-DD" numFmtId="167"/>');
fwrite($fd, '</numFmts>');
fwrite($fd, '<fonts count="4">');
fwrite($fd, '<font><name val="Arial"/><charset val="1"/><family val="2"/><sz val="10"/></font>');
fwrite($fd, '<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
fwrite($fd, '<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
fwrite($fd, '<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
fwrite($fd, '</fonts>');
fwrite($fd, '<fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills>');
fwrite($fd, '<borders count="1"><border diagonalDown="false" diagonalUp="false"><left/><right/><top/><bottom/><diagonal/></border></borders>');
fwrite($fd, '<cellStyleXfs count="15">');
fwrite($fd, '<xf applyAlignment="true" applyBorder="true" applyFont="true" applyProtection="true" borderId="0" fillId="0" fontId="0" numFmtId="164">');
fwrite($fd, '<alignment horizontal="general" indent="0" shrinkToFit="false" textRotation="0" vertical="bottom" wrapText="false"/>');
fwrite($fd, '<protection hidden="false" locked="true"/>');
fwrite($fd, '</xf>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="43"/>');
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="41"/>');
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="44"/>');
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="42"/>');
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="9"/>');
fwrite($fd, '</cellStyleXfs>');
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="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="167" xfId="0"/>');
fwrite($fd, '</cellXfs>');
fwrite($fd, '<cellStyles count="1">');
fwrite($fd, '<cellStyle builtinId="0" customBuiltin="false" name="Normal" xfId="0"/>');
//fwrite($fd, '<cellStyle builtinId="3" customBuiltin="false" name="Comma" xfId="15"/>');
//fwrite($fd, '<cellStyle builtinId="6" customBuiltin="false" name="Comma [0]" xfId="16"/>');
//fwrite($fd, '<cellStyle builtinId="4" customBuiltin="false" name="Currency" xfId="17"/>');
//fwrite($fd, '<cellStyle builtinId="7" customBuiltin="false" name="Currency [0]" xfId="18"/>');
//fwrite($fd, '<cellStyle builtinId="5" customBuiltin="false" name="Percent" xfId="19"/>');
fwrite($fd, '</cellStyles>');
fwrite($fd, '</styleSheet>');
fclose($fd);
return $tempfile;
}
protected function setSharedString($v)
{
// Strip control characters which Excel does not seem to like...
$v = preg_replace('/[\x00-\x09\x0B\x0C\x0E-\x1F]/u', '', $v);
if (isset($this->shared_strings[$v]))
{
$string_value = $this->shared_strings[$v];
}
else
{
$string_value = count($this->shared_strings);
$this->shared_strings[$v] = $string_value;
}
$this->shared_string_count++;//non-unique count
return $string_value;
}
protected function writeSharedStringsXML()
{
$tempfile = $this->tempFilename();
$fd = fopen($tempfile, "w+");
if ($fd===false) { self::log("write failed in ".__CLASS__."::".__FUNCTION__."."); return; }
fwrite($fd,'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n");
fwrite($fd,'<sst count="'.($this->shared_string_count).'" uniqueCount="'.count($this->shared_strings).'" xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">');
foreach($this->shared_strings as $s=>$c)
{
fwrite($fd,'<si><t>'.self::xmlspecialchars($s).'</t></si>');
}
fwrite($fd, '</sst>');
fclose($fd);
return $tempfile;
}
protected function buildAppXML()
{
$app_xml="";
$app_xml.='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n";
$app_xml.='<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><TotalTime>0</TotalTime></Properties>';
return $app_xml;
}
protected function buildCoreXML()
{
$core_xml="";
$core_xml.='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n";
$core_xml.='<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">';
$core_xml.='<dcterms:created xsi:type="dcterms:W3CDTF">'.date("Y-m-d\TH:i:s.00\Z").'</dcterms:created>';//$date_time = '2013-07-25T15:54:37.00Z';
$core_xml.='<dc:creator>'.self::xmlspecialchars($this->author).'</dc:creator>';
$core_xml.='<cp:revision>0</cp:revision>';
$core_xml.='</cp:coreProperties>';
return $core_xml;
}
protected function buildRelationshipsXML()
{
$rels_xml="";
$rels_xml.='<?xml version="1.0" encoding="UTF-8"?>'."\n";
$rels_xml.='<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">';
$rels_xml.='<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>';
$rels_xml.='<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>';
$rels_xml.='<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>';
$rels_xml.="\n";
$rels_xml.='</Relationships>';
return $rels_xml;
}
protected function buildWorkbookXML()
{
$workbook_xml="";
$workbook_xml.='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n";
$workbook_xml.='<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">';
$workbook_xml.='<fileVersion appName="Calc"/><workbookPr backupFile="false" showObjects="all" date1904="false"/><workbookProtection/>';
$workbook_xml.='<bookViews><workbookView activeTab="0" firstSheet="0" showHorizontalScroll="true" showSheetTabs="true" showVerticalScroll="true" tabRatio="212" windowHeight="8192" windowWidth="16384" xWindow="0" yWindow="0"/></bookViews>';
$workbook_xml.='<sheets>';
foreach($this->sheets_meta as $i=>$sheet_meta) {
$workbook_xml.='<sheet name="'.self::xmlspecialchars($sheet_meta['sheetname']).'" sheetId="'.($i+1).'" state="visible" r:id="rId'.($i+2).'"/>';
}
$workbook_xml.='</sheets>';
$workbook_xml.='<calcPr iterateCount="100" refMode="A1" iterate="false" iterateDelta="0.001"/></workbook>';
return $workbook_xml;
}
protected function buildWorkbookRelsXML()
{
$wkbkrels_xml="";
$wkbkrels_xml.='<?xml version="1.0" encoding="UTF-8"?>'."\n";
$wkbkrels_xml.='<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">';
$wkbkrels_xml.='<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>';
foreach($this->sheets_meta as $i=>$sheet_meta) {
$wkbkrels_xml.='<Relationship Id="rId'.($i+2).'" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/'.($sheet_meta['xmlname']).'"/>';
}
if (!empty($this->shared_strings)) {
$wkbkrels_xml.='<Relationship Id="rId'.(count($this->sheets_meta)+2).'" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>';
}
$wkbkrels_xml.="\n";
$wkbkrels_xml.='</Relationships>';
return $wkbkrels_xml;
}
protected function buildContentTypesXML()
{
$content_types_xml="";
$content_types_xml.='<?xml version="1.0" encoding="UTF-8"?>'."\n";
$content_types_xml.='<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">';
$content_types_xml.='<Override PartName="/_rels/.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>';
$content_types_xml.='<Override PartName="/xl/_rels/workbook.xml.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>';
foreach($this->sheets_meta as $i=>$sheet_meta) {
$content_types_xml.='<Override PartName="/xl/worksheets/'.($sheet_meta['xmlname']).'" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>';
}
if (!empty($this->shared_strings)) {
$content_types_xml.='<Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>';
}
$content_types_xml.='<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>';
$content_types_xml.='<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>';
$content_types_xml.='<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>';
$content_types_xml.='<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>';
$content_types_xml.="\n";
$content_types_xml.='</Types>';
return $content_types_xml;
}
//------------------------------------------------------------------
/*
* @param $row_number int, zero based
* @param $column_number int, zero based
* @return Cell label/coordinates, ex: A1, C3, AA42
* */
public static function xlsCell($row_number, $column_number)
{
$n = $column_number;
for($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
$r = chr($n%26 + 0x41) . $r;
}
return $r . ($row_number+1);
}
//------------------------------------------------------------------
public static function log($string)
{
file_put_contents("php://stderr", date("Y-m-d H:i:s:").rtrim(is_array($string) ? json_encode($string) : $string)."\n");
}
//------------------------------------------------------------------
public static function xmlspecialchars($val)
{
return str_replace("'", "&#39;", htmlspecialchars($val));
}
//------------------------------------------------------------------
public static function array_first_key(array $arr)
{
reset($arr);
$first_key = key($arr);
return $first_key;
}
//------------------------------------------------------------------
public static function convert_date_time($date_input) //thanks to Excel::Writer::XLSX::Worksheet.pm (perl)
{
$days = 0; # Number of days since epoch
$seconds = 0; # Time expressed as fraction of 24h hours in seconds
$year=$month=$day=0;
$hour=$min =$sec=0;
$date_time = $date_input;
if (preg_match("/(\d{4})\-(\d{2})\-(\d{2})/", $date_time, $matches))
{
list($junk,$year,$month,$day) = $matches;
}
if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $date_time, $matches))
{
list($junk,$hour,$min,$sec) = $matches;
$seconds = ( $hour * 60 * 60 + $min * 60 + $sec ) / ( 24 * 60 * 60 );
}
//using 1900 as epoch, not 1904, ignoring 1904 special case
# Special cases for Excel.
if ("$year-$month-$day"=='1899-12-31') return $seconds ; # Excel 1900 epoch
if ("$year-$month-$day"=='1900-01-00') return $seconds ; # Excel 1900 epoch
if ("$year-$month-$day"=='1900-02-29') return 60 + $seconds ; # Excel false leapday
# We calculate the date by calculating the number of days since the epoch
# and adjust for the number of leap days. We calculate the number of leap
# days by normalising the year in relation to the epoch. Thus the year 2000
# becomes 100 for 4 and 100 year leapdays and 400 for 400 year leapdays.
$epoch = 1900;
$offset = 0;
$norm = 300;
$range = $year - $epoch;
# Set month days and check for leap year.
$leap = (($year % 400 == 0) || (($year % 4 == 0) && ($year % 100)) ) ? 1 : 0;
$mdays = array( 31, ($leap ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
# Some boundary checks
if($year < $epoch || $year > 9999) return 0;
if($month < 1 || $month > 12) return 0;
if($day < 1 || $day > $mdays[ $month - 1 ]) return 0;
# Accumulate the number of days since the epoch.
$days = $day; # Add days for current month
$days += array_sum( array_slice($mdays, 0, $month-1 ) ); # Add days for past months
$days += $range * 365; # Add days for past years
$days += intval( ( $range ) / 4 ); # Add leapdays
$days -= intval( ( $range + $offset ) / 100 ); # Subtract 100 year leapdays
$days += intval( ( $range + $offset + $norm ) / 400 ); # Add 400 year leapdays
$days -= $leap; # Already counted above
# Adjust for Excel erroneously treating 1900 as a leap year.
if ($days > 59) { $days++;}
return $days + $seconds;
}
//------------------------------------------------------------------
}

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2013 Combodo SARL
//
// This file is part of iTop.
//
@@ -20,7 +20,7 @@
/**
* Class XMLPage
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -51,9 +51,6 @@ class XMLPage extends WebPage
{
if (!$this->m_bPassThrough)
{
// Get the unexpected output but do nothing with it
$sTrash = $this->ob_get_clean_safe();
$this->s_content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n".trim($this->s_content);
$this->add_header("Content-Length: ".strlen($this->s_content));
foreach($this->a_headers as $s_header)
@@ -62,9 +59,9 @@ class XMLPage extends WebPage
}
echo $this->s_content;
}
if (class_exists('DBSearch'))
if (class_exists('MetaModel'))
{
DBSearch::RecordQueryTrace();
MetaModel::RecordQueryTrace();
}
}
@@ -82,7 +79,8 @@ class XMLPage extends WebPage
}
else
{
$s_captured_output = $this->ob_get_clean_safe();
$s_captured_output = ob_get_contents();
ob_end_clean();
foreach($this->a_headers as $s_header)
{
header($s_header);
@@ -103,4 +101,13 @@ class XMLPage extends WebPage
public function table($aConfig, $aData, $aParams = array())
{
}
public function TrashUnexpectedOutput()
{
if (!$this->m_bPassThrough)
{
parent::TrashUnexpectedOutput();
}
}
}
?>

View File

@@ -136,7 +136,7 @@ class ActionEmail extends ActionNotification
{
$aParams = array
(
"category" => "core/cmdb,application",
"category" => "core/cmdb,bizmodel",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2013 Combodo SARL
//
// This file is part of iTop.
//
@@ -20,7 +20,7 @@
/**
* 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
*/
@@ -295,7 +295,7 @@ class BulkChange
protected function ResolveExternalKey($aRowData, $sAttCode, &$aResults)
{
$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)
{
if ($sForeignAttCode == 'id')
@@ -366,7 +366,7 @@ class BulkChange
}
else
{
$oReconFilter = new DBObjectSearch($oExtKey->GetTargetClass());
$oReconFilter = new CMDBSearchFilter($oExtKey->GetTargetClass());
$aCacheKeys = array();
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
{
@@ -839,7 +839,7 @@ class BulkChange
}
try
{
$oReconciliationFilter = new DBObjectSearch($this->m_sClass);
$oReconciliationFilter = new CMDBSearchFilter($this->m_sClass);
$bSkipQuery = false;
foreach($this->m_aReconcilKeys as $sAttCode)
{

View File

@@ -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');

View File

@@ -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("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 AttributeObjectKey("objkey", array("allowed_values"=>null, "class_attcode"=>"objclass", "sql"=>"objkey", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("objkey", array("allowed_values"=>null, "sql"=>"objkey", "default_value"=>"", "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('list', array('change', 'date', 'userinfo')); // Attributes to be displayed for the complete details
@@ -237,7 +237,7 @@ class CMDBChangeOpSetAttributeScalar extends CMDBChangeOpSetAttribute
$sAttName = $oAttDef->GetLabel();
$sNewValue = $this->Get('newvalue');
$sOldValue = $this->Get('oldvalue');
$sResult = $oAttDef->DescribeChangeAsHTML($sOldValue, $sNewValue);
$sResult = $oAttDef->GetAsHTMLForHistory($sOldValue, $sNewValue);
}
return $sResult;
}
@@ -619,39 +619,10 @@ class CMDBChangeOpSetAttributeCaseLog extends CMDBChangeOpSetAttribute
// The attribute was renamed or removed from the object ?
$sAttName = $this->Get('attcode');
}
$oObj = $oMonoObjectSet->Fetch();
$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
$sBefore = $this->ToHtml(mb_strcut($sTextEntry, 0, $iMaxVisibleLength, 'UTF-8'));
$sAfter = $this->ToHtml(mb_strcut($sTextEntry, $iMaxVisibleLength, null, 'UTF-8'));
}
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);
$sResult = Dict::Format('Change:AttName_EntryAdded', $sAttName);
}
return $sResult;
}
protected function ToHtml($sRawText)
{
return str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sRawText, ENT_QUOTES, 'UTF-8'));
}
}
/**

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
@@ -20,7 +20,7 @@
/**
* Class cmdbObject
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @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('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/oqlexception.class.inc.php');
require_once('oql/oql-parser.php');
require_once('oql/oql-lexer.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('dbsearch.class.php');
require_once('dbobjectsearch.class.php');
require_once('dbobjectset.class.php');
require_once('backgroundprocess.inc.php');
@@ -524,18 +522,18 @@ abstract class CMDBObject extends DBObject
return $ret;
}
public static function BulkUpdate(DBSearch $oFilter, array $aValues)
public static function BulkUpdate(DBObjectSearch $oFilter, array $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);
$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
@@ -582,7 +580,7 @@ class CMDBObjectSet extends DBObjectSet
static public function FromScratch($sClass)
{
$oFilter = new DBObjectSearch($sClass);
$oFilter = new CMDBSearchFilter($sClass);
$oFilter->AddConditionExpression(new FalseExpression());
$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)
@@ -606,7 +604,7 @@ class CMDBObjectSet extends DBObjectSet
// let's create one search definition
$sClass = reset($aClasses);
$sAlias = key($aClasses);
$oFilter = new DBObjectSearch($sClass, $sAlias);
$oFilter = new CMDBSearchFilter($sClass, $sAlias);
$oRetSet = new CMDBObjectSet($oFilter);
$oRetSet->m_bLoaded = true; // no DB load
@@ -618,3 +616,16 @@ class CMDBObjectSet extends DBObjectSet
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 (?)
}
?>

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2014 Combodo SARL
//
// This file is part of iTop.
//
@@ -20,7 +20,7 @@
/**
* 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
*/
@@ -336,7 +336,7 @@ class CMDBSource
catch(mysqli_sql_exception $e)
{
$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);
if ($oResult === false)
@@ -368,7 +368,7 @@ class CMDBSource
catch(mysqli_sql_exception $e)
{
$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);
if ($oResult === false)
@@ -404,7 +404,7 @@ class CMDBSource
}
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)
{
@@ -430,7 +430,7 @@ class CMDBSource
}
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)
{
@@ -541,37 +541,6 @@ class CMDBSource
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)
{
$aTableInfo = self::GetTableInfo($sTable);

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2014 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
@@ -96,19 +96,11 @@ class DefaultWorkingTimeComputer implements iWorkingTimeComputer
*/
public function GetDeadline($oObject, $iDuration, DateTime $oStartDate)
{
if (class_exists('WorkingTimeRecorder'))
{
WorkingTimeRecorder::Trace(WorkingTimeRecorder::TRACE_DEBUG, __class__.'::'.__function__);
}
//echo "GetDeadline - default: ".$oStartDate->format('Y-m-d H:i:s')." + $iDuration<br/>\n";
// Default implementation: 24x7, no holidays: to compute the deadline, just add
// the specified duration to the given date/time
$oResult = clone $oStartDate;
$oResult->modify('+'.$iDuration.' seconds');
if (class_exists('WorkingTimeRecorder'))
{
WorkingTimeRecorder::SetValues($oStartDate->format('U'), $oResult->format('U'), $iDuration, WorkingTimeRecorder::COMPUTED_END);
}
return $oResult;
}
@@ -121,17 +113,8 @@ class DefaultWorkingTimeComputer implements iWorkingTimeComputer
*/
public function GetOpenDuration($oObject, DateTime $oStartDate, DateTime $oEndDate)
{
if (class_exists('WorkingTimeRecorder'))
{
WorkingTimeRecorder::Trace(WorkingTimeRecorder::TRACE_DEBUG, __class__.'::'.__function__);
}
//echo "GetOpenDuration - default: ".$oStartDate->format('Y-m-d H:i:s')." to ".$oEndDate->format('Y-m-d H:i:s')."<br/>\n";
$iDuration = abs($oEndDate->format('U') - $oStartDate->format('U'));
if (class_exists('WorkingTimeRecorder'))
{
WorkingTimeRecorder::SetValues($oStartDate->format('U'), $oEndDate->format('U'), $iDuration, WorkingTimeRecorder::COMPUTED_DURATION);
}
return $iDuration;
return abs($oEndDate->format('U') - $oStartDate->format('U'));
}
}

View File

@@ -162,7 +162,7 @@ class Config
'default' => '/usr/bin/dot',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => true,
'show_in_conf_sample' => false,
),
'php_path' => array(
'type' => 'string',
@@ -277,14 +277,6 @@ class Config
'source_of_value' => '',
'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(
'type' => 'string',
'description' => 'Synchronization details: none, display, save (includes \'display\')',
@@ -736,15 +728,6 @@ class Config
'source_of_value' => '',
'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(
'type' => 'integer',
'description' => 'Delay after which the results are displayed.',
@@ -786,22 +769,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'user_rights_legacy' => array(
'type' => 'bool',
'description' => 'Set to true to restore the buggy algorithm for the computation of user rights (within the same profile, ALLOW on the class itself has precedence on DENY of a parent class)',
'default' => false,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'xlsx_exporter_memory_limit' => array(
'type' => 'string',
'description' => 'Memory limit to use when (interactively) exporting data to Excel',
'default' => '2048M', // Huuuuuuge 2GB!
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'min_reload_interval' => array(
'type' => 'integer',
'description' => 'Minimum refresh interval (seconds) for dashboards, shortcuts, etc. Even if the interval is set programmatically, it is forced to that minimum',
@@ -810,62 +777,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'relations_max_depth' => array(
'type' => 'integer',
'description' => 'Maximum number of successive levels (depth) to explore when displaying the impact/depends on relations.',
'default' => 20, // In iTop 2.0.3, this was the hardcoded value
'value' => '',
'source_of_value' => '',
'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)
@@ -1006,8 +917,6 @@ class Config
'core/event.class.inc.php',
'core/action.class.inc.php',
'core/trigger.class.inc.php',
'core/bulkexport.class.inc.php',
'core/ownershiplock.class.inc.php',
'synchro/synchrodatasource.class.inc.php',
'core/backgroundtask.class.inc.php',
);
@@ -1047,8 +956,6 @@ class Config
$this->m_sExtAuthVariable = DEFAULT_EXT_AUTH_VARIABLE;
$this->m_sEncryptionKey = DEFAULT_ENCRYPTION_KEY;
$this->m_aCharsets = array();
$this->m_bLogQueries = DEFAULT_LOG_QUERIES;
$this->m_bQueryCacheEnabled = DEFAULT_QUERY_CACHE_ENABLED;
$this->m_aModuleSettings = array();
@@ -1211,24 +1118,9 @@ class Config
{
return $this->m_aModuleSettings[$sModule][$sProperty];
}
// Fall back to the predefined XML parameter, if any
return $this->GetModuleParameter($sModule, $sProperty, $defaultvalue);
return $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)
{
$this->m_aModuleSettings[$sModule][$sProperty] = $value;
@@ -1538,8 +1430,6 @@ class Config
$aSettings['log_notification'] = $this->m_bLogNotification;
$aSettings['log_issue'] = $this->m_bLogIssue;
$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['max_display_limit'] = $this->m_iMaxDisplayLimit;
$aSettings['standard_reload_interval'] = $this->m_iStandardReloadInterval;
@@ -1547,7 +1437,6 @@ class Config
$aSettings['secure_connection_required'] = $this->m_bSecureConnectionRequired;
$aSettings['default_language'] = $this->m_sDefaultLanguage;
$aSettings['allowed_login_types'] = $this->m_sAllowedLoginTypes;
$aSettings['ext_auth_variable'] = $this->m_sExtAuthVariable;
$aSettings['encryption_key'] = $this->m_sEncryptionKey;
$aSettings['csv_import_charsets'] = $this->m_aCharsets;
@@ -1613,8 +1502,6 @@ class Config
'log_notification' => $this->m_bLogNotification,
'log_issue' => $this->m_bLogIssue,
'log_web_service' => $this->m_bLogWebService,
'log_queries' => $this->m_bLogQueries,
'query_cache_enabled' => $this->m_bQueryCacheEnabled,
'secure_connection_required' => $this->m_bSecureConnectionRequired,
);
foreach($aBoolValues as $sKey => $bValue)
@@ -1653,7 +1540,6 @@ class Config
'db_collation' => $this->m_sDBCollation,
'default_language' => $this->m_sDefaultLanguage,
'allowed_login_types' => $this->m_sAllowedLoginTypes,
'ext_auth_variable' => $this->m_sExtAuthVariable,
'encryption_key' => $this->m_sEncryptionKey,
'csv_import_charsets' => $this->m_aCharsets,
);
@@ -1791,10 +1677,6 @@ class Config
{
$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 (($aParamValues['mode'] == 'install') || $this->GetDefaultLanguage() == '')
@@ -1816,46 +1698,32 @@ class Config
$this->SetDBName($sDBName);
$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 (isset($aParamValues['selected_modules']))
{
$aSelectedModules = explode(',', $aParamValues['selected_modules']);
}
else
{
$aSelectedModules = null;
}
// 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
$aAddOns = $oEmptyConfig->GetAddOns();
$aAppModules = $oEmptyConfig->GetAppModules();
if (file_exists(APPROOT.$sModulesDir.'/core/main.php'))
{
$aAppModules[] = $sModulesDir.'/core/main.php';
}
$aDataModels = $oEmptyConfig->GetDataModels();
$aWebServiceCategories = $oEmptyConfig->GetWebServiceCategories();
$aDictionaries = $oEmptyConfig->GetDictionaries();
// Merge the values with the ones provided by the modules
// Make sure when don't load the same file twice...
$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 (isset($aModuleInfo['datamodel']))
@@ -1868,10 +1736,10 @@ class Config
}
if (isset($aModuleInfo['settings']))
{
list ($sName, $sVersion) = ModuleDiscovery::GetModuleName($sModuleId);
foreach ($aModuleInfo['settings'] as $sProperty => $value)
list($sName, $sVersion) = ModuleDiscovery::GetModuleName($sModuleId);
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
}
@@ -1892,7 +1760,7 @@ class Config
{
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));
}
}
@@ -1901,13 +1769,16 @@ class Config
$this->SetAppModules($aAppModules);
$this->SetDataModels($aDataModels);
$this->SetWebServiceCategories($aWebServiceCategories);
// Scan dictionaries
//
foreach (glob(APPROOT.$sModulesDir.'/dictionaries/*.dict.php') as $sFilePath)
if (!is_null($sModulesDir))
{
$sFile = basename($sFilePath);
$aDictionaries[] = $sModulesDir.'/dictionaries/'.$sFile;
foreach(glob(APPROOT.$sModulesDir.'/dictionaries/*.dict.php') as $sFilePath)
{
$sFile = basename($sFilePath);
$aDictionaries[] = $sModulesDir.'/dictionaries/'.$sFile;
}
}
$this->SetDictionaries($aDictionaries);
}

View File

@@ -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.'/>&nbsp;'.$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.'/>&nbsp;'.$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'];
}
}

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
@@ -21,7 +21,7 @@
* data generator
* 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
*/
@@ -267,7 +267,7 @@ class cmdbDataGenerator
function GenerateKey($sClass, $aFilterCriteria)
{
$retKey = null;
$oFilter = new DBObjectSearch($sClass);
$oFilter = new CMDBSearchFilter($sClass);
foreach($aFilterCriteria as $sFilterCode => $filterValue)
{
$oFilter->AddCondition($sFilterCode, $filterValue, '=');
@@ -346,7 +346,7 @@ class cmdbDataGenerator
*/
protected function OrganizationExists($sCode)
{
$oFilter = new DBObjectSearch('Organization');
$oFilter = new CMDBSearchFilter('bizOrganization');
$oFilter->AddCondition('code', $sCode, '=');
$oSet = new CMDBObjectSet($oFilter);
return ($oSet->Count() > 0);
@@ -361,7 +361,7 @@ class cmdbDataGenerator
protected function GetOrganization($sId)
{
$oOrg = null;
$oFilter = new DBObjectSearch('Organization');
$oFilter = new CMDBSearchFilter('bizOrganization');
$oFilter->AddCondition('id', $sId, '=');
$oSet = new CMDBObjectSet($oFilter);
if ($oSet->Count() > 0)

View File

@@ -1,3 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design>
</itop_design>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2014 Combodo SARL
//
// This file is part of iTop.
//
@@ -20,7 +20,7 @@
/**
* 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
*/
@@ -47,14 +47,14 @@ class DBObjectSet
/**
* 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 hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
* @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
* @param hash $aArgs Values to substitute for the search/query parameters (if any). Format: param_name => value
* @param hash $aExtendedDataSpec
* @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)
*/
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_aAddedIds = array();
@@ -86,7 +86,7 @@ class DBObjectSet
$sRet = '';
$this->Rewind();
$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";
if ($this->Count() > 0)
@@ -237,7 +237,7 @@ class DBObjectSet
// let's create one search definition corresponding only to the first column
$sClass = reset($aClasses);
$sAlias = key($aClasses);
$oFilter = new DBObjectSearch($sClass, $sAlias);
$oFilter = new CMDBSearchFilter($sClass, $sAlias);
$oRetSet = new self($oFilter);
$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:
* 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)
{
$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
{
$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))
@@ -549,7 +549,7 @@ class DBObjectSet
{
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);
if (!$resQuery) return 0;
@@ -837,16 +837,74 @@ class DBObjectSet
* Compare two sets of objects to determine if their content is identical or not.
*
* 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 array $aExcludeColumns The list of columns to exclude frop the comparison
* @return boolean True if the sets are identical, false otherwise
*/
public function HasSameContents(DBObjectSet $oObjectSet, $aExcludeColumns = array())
{
$oComparator = new DBObjectSetComparator($this, $oObjectSet, $aExcludeColumns);
return $oComparator->SetsAreEquivalent();
public function HasSameContents(DBObjectSet $oObjectSet)
{
if ($this->GetRootClass() != $oObjectSet->GetRootClass())
{
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)
@@ -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)
{
@@ -933,50 +996,6 @@ class DBObjectSet
}
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
@@ -1138,216 +1157,4 @@ function HashCountComparison($a, $b) // Sort descending on 'count'
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;
}
}
?>

View File

@@ -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";
}
}
}

View File

@@ -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;
}
}

View File

@@ -101,7 +101,6 @@ class DeletionPlan
}
if ($aData['mode'] == DEL_MANUAL)
{
$this->m_aToDelete[$sClass][$iId]['issue'] = $sClass.'::'.$iId.' '.Dict::S('UI:Delete:MustBeDeletedManually');
$this->m_bFoundStopper = true;
$this->m_bFoundManualDelete = true;
}

File diff suppressed because it is too large Load Diff

View File

@@ -177,15 +177,6 @@ class EMail
}
break;
case 'Null':
$oTransport = Swift_NullTransport::newInstance();
break;
case 'LogFile':
$oTransport = Swift_LogFileTransport::newInstance();
$oTransport->setLogFile(APPROOT.'log/mail.log');
break;
case 'PHPMail':
default:
$oTransport = Swift_MailTransport::newInstance();
@@ -405,75 +396,4 @@ class EMail
}
/////////////////////////////////////////////////////////////////////////////////////
/**
* Extension to SwiftMailer: "debug" transport that pretends messages have been sent,
* but just log them to a file.
*
* @package Swift
* @author Denis Flaven
*/
class Swift_Transport_LogFileTransport extends Swift_Transport_NullTransport
{
protected $sLogFile;
/**
* Sends the given message.
*
* @param Swift_Mime_Message $message
* @param string[] $failedRecipients An array of failures by-reference
*
* @return int The number of sent emails
*/
public function send(Swift_Mime_Message $message, &$failedRecipients = null)
{
$hFile = @fopen($this->sLogFile, 'a');
if ($hFile)
{
$sTxt = "================== ".date('Y-m-d H:i:s')." ==================\n";
$sTxt .= $message->toString()."\n";
@fwrite($hFile, $sTxt);
@fclose($hFile);
}
return parent::send($message, $failedRecipients);
}
public function setLogFile($sFilename)
{
$this->sLogFile = $sFilename;
}
}
/**
* Pretends messages have been sent, but just log them to a file.
*
* @package Swift
* @author Denis Flaven
*/
class Swift_LogFileTransport extends Swift_Transport_LogFileTransport
{
/**
* Create a new LogFileTransport.
*/
public function __construct()
{
call_user_func_array(
array($this, 'Swift_Transport_LogFileTransport::__construct'),
Swift_DependencyContainer::getInstance()
->createDependenciesFor('transport.null')
);
}
/**
* Create a new LogFileTransport instance.
*
* @return Swift_LogFileTransport
*/
public static function newInstance()
{
return new self();
}
}
?>

View File

@@ -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
{
public static function Init()

View File

@@ -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

View File

@@ -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 === '') ? '&nbsp;' : $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';
}
}

View File

@@ -59,11 +59,8 @@ class FileLog
$hLogFile = @fopen($this->m_sFile, '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);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -23,21 +23,7 @@
* @copyright Copyright (C) 2013 Combodo SARL
* @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 public function GetClassIcon($sClass, $bImgTag = true);
@@ -76,9 +62,6 @@ abstract class ModelReflection
}
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
@@ -86,7 +69,7 @@ abstract class QueryReflection
/**
* 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 GetClassAlias();
@@ -239,7 +222,7 @@ class ModelReflectionRuntime extends ModelReflection
public function GetQuery($sOQL)
{
return new QueryReflectionRuntime($sOQL, $this);
return new QueryReflectionRuntime($sOQL);
}
public function DictString($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
@@ -251,16 +234,6 @@ class ModelReflectionRuntime extends ModelReflection
{
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
*/
public function __construct($sOQL, ModelReflection $oModelReflection)
public function __construct($sOQL)
{
$this->oFilter = DBObjectSearch::FromOQL($sOQL);
}

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2013-2016 Combodo SARL
// Copyright (C) 2013 Combodo SARL
//
// 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
* installed PHP.
*
* @copyright Copyright (C) 2013-2016 Combodo SARL
* @copyright Copyright (C) 2013 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class iTopMutex
@@ -37,19 +37,8 @@ class iTopMutex
public function __construct($sName, $sDBHost = null, $sDBUser = null, $sDBPwd = null)
{
// Compute the name of a lock for mysql
// Note: names are server-wide!!! So let's make the name specific to this iTop instance
$oConfig = utils::GetConfig(); // Will return an empty config when called during the setup
$sDBName = $oConfig->GetDBName();
$sDBSubname = $oConfig->GetDBSubname();
// Note: the name is server-wide!!!
$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
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
// 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;
$sDBUser = is_null($sDBUser) ? $oConfig->GetDBUser() : $sDBUser;
$sDBPwd = is_null($sDBPwd) ? $oConfig->GetDBPwd() : $sDBPwd;
@@ -131,43 +121,7 @@ class iTopMutex
$this->bLocked = true;
self::$aAcquiredLocks[$this->sName]++;
}
if (($res !== '1') && ($res !== '0'))
{
$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;
return ($res === '1');
}
/**

4
core/oql/build.bash Executable file
View 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
View 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

View File

@@ -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);
}
}

View File

@@ -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');
?>

View File

@@ -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 {}
?>

View File

@@ -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;
}
}
?>

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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');
}

View File

@@ -1,4 +0,0 @@
<?php
require_once 'PHP/LexerGenerator.php';
$a = new PHP_LexerGenerator($_SERVER['argv'][1]);
?>

View File

@@ -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";
}
}
}

View File

@@ -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;
}
}
?>

View File

@@ -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;
}
}
?>

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -1,5 +0,0 @@
<?php
require_once 'PHP/ParserGenerator.php';
$me = new PHP_ParserGenerator;
$me->main();
?>

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -2,6 +2,7 @@
# 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
#
build
build.cmd
build.bash
oql-lexer.plex
oql-parser.y
oql-parser.y

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
@@ -21,7 +21,7 @@
/**
* 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
*/
@@ -108,7 +108,6 @@ class OQLLexerRaw
do {
$rules = array(
'/\G[ \t\n\r]+/ ',
'/\GUNION/ ',
'/\GSELECT/ ',
'/\GFROM/ ',
'/\GAS/ ',
@@ -281,364 +280,359 @@ class OQLLexerRaw
function yy_r1_1($yy_subpatterns)
{
$this->token = OQLParser::UNION;
$this->token = OQLParser::SELECT;
}
function yy_r1_2($yy_subpatterns)
{
$this->token = OQLParser::SELECT;
$this->token = OQLParser::FROM;
}
function yy_r1_3($yy_subpatterns)
{
$this->token = OQLParser::FROM;
$this->token = OQLParser::AS_ALIAS;
}
function yy_r1_4($yy_subpatterns)
{
$this->token = OQLParser::AS_ALIAS;
$this->token = OQLParser::WHERE;
}
function yy_r1_5($yy_subpatterns)
{
$this->token = OQLParser::WHERE;
$this->token = OQLParser::JOIN;
}
function yy_r1_6($yy_subpatterns)
{
$this->token = OQLParser::JOIN;
$this->token = OQLParser::ON;
}
function yy_r1_7($yy_subpatterns)
{
$this->token = OQLParser::ON;
$this->token = OQLParser::MATH_DIV;
}
function yy_r1_8($yy_subpatterns)
{
$this->token = OQLParser::MATH_DIV;
$this->token = OQLParser::MATH_MULT;
}
function yy_r1_9($yy_subpatterns)
{
$this->token = OQLParser::MATH_MULT;
$this->token = OQLParser::MATH_PLUS;
}
function yy_r1_10($yy_subpatterns)
{
$this->token = OQLParser::MATH_PLUS;
$this->token = OQLParser::MATH_MINUS;
}
function yy_r1_11($yy_subpatterns)
{
$this->token = OQLParser::MATH_MINUS;
$this->token = OQLParser::LOG_AND;
}
function yy_r1_12($yy_subpatterns)
{
$this->token = OQLParser::LOG_AND;
$this->token = OQLParser::LOG_OR;
}
function yy_r1_13($yy_subpatterns)
{
$this->token = OQLParser::LOG_OR;
$this->token = OQLParser::BITWISE_OR;
}
function yy_r1_14($yy_subpatterns)
{
$this->token = OQLParser::BITWISE_OR;
$this->token = OQLParser::BITWISE_AND;
}
function yy_r1_15($yy_subpatterns)
{
$this->token = OQLParser::BITWISE_AND;
$this->token = OQLParser::BITWISE_XOR;
}
function yy_r1_16($yy_subpatterns)
{
$this->token = OQLParser::BITWISE_XOR;
$this->token = OQLParser::BITWISE_LEFT_SHIFT;
}
function yy_r1_17($yy_subpatterns)
{
$this->token = OQLParser::BITWISE_LEFT_SHIFT;
$this->token = OQLParser::BITWISE_RIGHT_SHIFT;
}
function yy_r1_18($yy_subpatterns)
{
$this->token = OQLParser::BITWISE_RIGHT_SHIFT;
$this->token = OQLParser::COMA;
}
function yy_r1_19($yy_subpatterns)
{
$this->token = OQLParser::COMA;
$this->token = OQLParser::PAR_OPEN;
}
function yy_r1_20($yy_subpatterns)
{
$this->token = OQLParser::PAR_OPEN;
$this->token = OQLParser::PAR_CLOSE;
}
function yy_r1_21($yy_subpatterns)
{
$this->token = OQLParser::PAR_CLOSE;
$this->token = OQLParser::REGEXP;
}
function yy_r1_22($yy_subpatterns)
{
$this->token = OQLParser::REGEXP;
$this->token = OQLParser::EQ;
}
function yy_r1_23($yy_subpatterns)
{
$this->token = OQLParser::EQ;
$this->token = OQLParser::NOT_EQ;
}
function yy_r1_24($yy_subpatterns)
{
$this->token = OQLParser::NOT_EQ;
$this->token = OQLParser::GT;
}
function yy_r1_25($yy_subpatterns)
{
$this->token = OQLParser::GT;
$this->token = OQLParser::LT;
}
function yy_r1_26($yy_subpatterns)
{
$this->token = OQLParser::LT;
$this->token = OQLParser::GE;
}
function yy_r1_27($yy_subpatterns)
{
$this->token = OQLParser::GE;
$this->token = OQLParser::LE;
}
function yy_r1_28($yy_subpatterns)
{
$this->token = OQLParser::LE;
$this->token = OQLParser::LIKE;
}
function yy_r1_29($yy_subpatterns)
{
$this->token = OQLParser::LIKE;
$this->token = OQLParser::NOT_LIKE;
}
function yy_r1_30($yy_subpatterns)
{
$this->token = OQLParser::NOT_LIKE;
$this->token = OQLParser::IN;
}
function yy_r1_31($yy_subpatterns)
{
$this->token = OQLParser::IN;
$this->token = OQLParser::NOT_IN;
}
function yy_r1_32($yy_subpatterns)
{
$this->token = OQLParser::NOT_IN;
$this->token = OQLParser::INTERVAL;
}
function yy_r1_33($yy_subpatterns)
{
$this->token = OQLParser::INTERVAL;
$this->token = OQLParser::F_IF;
}
function yy_r1_34($yy_subpatterns)
{
$this->token = OQLParser::F_IF;
$this->token = OQLParser::F_ELT;
}
function yy_r1_35($yy_subpatterns)
{
$this->token = OQLParser::F_ELT;
$this->token = OQLParser::F_COALESCE;
}
function yy_r1_36($yy_subpatterns)
{
$this->token = OQLParser::F_COALESCE;
$this->token = OQLParser::F_ISNULL;
}
function yy_r1_37($yy_subpatterns)
{
$this->token = OQLParser::F_ISNULL;
$this->token = OQLParser::F_CONCAT;
}
function yy_r1_38($yy_subpatterns)
{
$this->token = OQLParser::F_CONCAT;
$this->token = OQLParser::F_SUBSTR;
}
function yy_r1_39($yy_subpatterns)
{
$this->token = OQLParser::F_SUBSTR;
$this->token = OQLParser::F_TRIM;
}
function yy_r1_40($yy_subpatterns)
{
$this->token = OQLParser::F_TRIM;
$this->token = OQLParser::F_DATE;
}
function yy_r1_41($yy_subpatterns)
{
$this->token = OQLParser::F_DATE;
$this->token = OQLParser::F_DATE_FORMAT;
}
function yy_r1_42($yy_subpatterns)
{
$this->token = OQLParser::F_DATE_FORMAT;
$this->token = OQLParser::F_CURRENT_DATE;
}
function yy_r1_43($yy_subpatterns)
{
$this->token = OQLParser::F_CURRENT_DATE;
$this->token = OQLParser::F_NOW;
}
function yy_r1_44($yy_subpatterns)
{
$this->token = OQLParser::F_NOW;
$this->token = OQLParser::F_TIME;
}
function yy_r1_45($yy_subpatterns)
{
$this->token = OQLParser::F_TIME;
$this->token = OQLParser::F_TO_DAYS;
}
function yy_r1_46($yy_subpatterns)
{
$this->token = OQLParser::F_TO_DAYS;
$this->token = OQLParser::F_FROM_DAYS;
}
function yy_r1_47($yy_subpatterns)
{
$this->token = OQLParser::F_FROM_DAYS;
$this->token = OQLParser::F_YEAR;
}
function yy_r1_48($yy_subpatterns)
{
$this->token = OQLParser::F_YEAR;
$this->token = OQLParser::F_MONTH;
}
function yy_r1_49($yy_subpatterns)
{
$this->token = OQLParser::F_MONTH;
$this->token = OQLParser::F_DAY;
}
function yy_r1_50($yy_subpatterns)
{
$this->token = OQLParser::F_DAY;
$this->token = OQLParser::F_HOUR;
}
function yy_r1_51($yy_subpatterns)
{
$this->token = OQLParser::F_HOUR;
$this->token = OQLParser::F_MINUTE;
}
function yy_r1_52($yy_subpatterns)
{
$this->token = OQLParser::F_MINUTE;
$this->token = OQLParser::F_SECOND;
}
function yy_r1_53($yy_subpatterns)
{
$this->token = OQLParser::F_SECOND;
$this->token = OQLParser::F_DATE_ADD;
}
function yy_r1_54($yy_subpatterns)
{
$this->token = OQLParser::F_DATE_ADD;
$this->token = OQLParser::F_DATE_SUB;
}
function yy_r1_55($yy_subpatterns)
{
$this->token = OQLParser::F_DATE_SUB;
$this->token = OQLParser::F_ROUND;
}
function yy_r1_56($yy_subpatterns)
{
$this->token = OQLParser::F_ROUND;
$this->token = OQLParser::F_FLOOR;
}
function yy_r1_57($yy_subpatterns)
{
$this->token = OQLParser::F_FLOOR;
$this->token = OQLParser::F_INET_ATON;
}
function yy_r1_58($yy_subpatterns)
{
$this->token = OQLParser::F_INET_ATON;
$this->token = OQLParser::F_INET_NTOA;
}
function yy_r1_59($yy_subpatterns)
{
$this->token = OQLParser::F_INET_NTOA;
$this->token = OQLParser::BELOW;
}
function yy_r1_60($yy_subpatterns)
{
$this->token = OQLParser::BELOW;
$this->token = OQLParser::BELOW_STRICT;
}
function yy_r1_61($yy_subpatterns)
{
$this->token = OQLParser::BELOW_STRICT;
$this->token = OQLParser::NOT_BELOW;
}
function yy_r1_62($yy_subpatterns)
{
$this->token = OQLParser::NOT_BELOW;
$this->token = OQLParser::NOT_BELOW_STRICT;
}
function yy_r1_63($yy_subpatterns)
{
$this->token = OQLParser::NOT_BELOW_STRICT;
$this->token = OQLParser::ABOVE;
}
function yy_r1_64($yy_subpatterns)
{
$this->token = OQLParser::ABOVE;
$this->token = OQLParser::ABOVE_STRICT;
}
function yy_r1_65($yy_subpatterns)
{
$this->token = OQLParser::ABOVE_STRICT;
$this->token = OQLParser::NOT_ABOVE;
}
function yy_r1_66($yy_subpatterns)
{
$this->token = OQLParser::NOT_ABOVE;
$this->token = OQLParser::NOT_ABOVE_STRICT;
}
function yy_r1_67($yy_subpatterns)
{
$this->token = OQLParser::NOT_ABOVE_STRICT;
$this->token = OQLParser::HEXVAL;
}
function yy_r1_68($yy_subpatterns)
{
$this->token = OQLParser::HEXVAL;
$this->token = OQLParser::NUMVAL;
}
function yy_r1_69($yy_subpatterns)
{
$this->token = OQLParser::NUMVAL;
$this->token = OQLParser::STRVAL;
}
function yy_r1_70($yy_subpatterns)
{
$this->token = OQLParser::STRVAL;
$this->token = OQLParser::NAME;
}
function yy_r1_71($yy_subpatterns)
{
$this->token = OQLParser::NAME;
}
function yy_r1_72($yy_subpatterns)
{
$this->token = OQLParser::VARNAME;
}
function yy_r1_73($yy_subpatterns)
function yy_r1_72($yy_subpatterns)
{
$this->token = OQLParser::DOT;

View File

@@ -1,6 +1,6 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
@@ -21,7 +21,7 @@
/**
* 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
*/
@@ -80,7 +80,6 @@ class OQLLexerRaw
%line $this->line
%matchlongest 1
whitespace = /[ \t\n\r]+/
union = "UNION"
select = "SELECT"
from = "FROM"
as_alias = "AS"
@@ -177,9 +176,6 @@ dot = "."
whitespace {
return false;
}
union {
$this->token = OQLParser::UNION;
}
select {
$this->token = OQLParser::SELECT;
}

File diff suppressed because it is too large Load Diff

View File

@@ -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);
}
result ::= union(X). { $this->my_result = X; }
result ::= query(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). {
A = new OqlObjectQuery(X, X, W, J, array(X));
}

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
@@ -20,7 +20,7 @@
/**
* 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
*/
@@ -83,10 +83,10 @@ class OqlInterpreter
return $res;
}
public function ParseQuery()
public function ParseObjectQuery()
{
$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));
}

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2013 Combodo SARL
//
// This file is part of iTop.
//
@@ -20,20 +20,10 @@
/**
* 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
*/
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
// 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
@@ -276,35 +266,23 @@ class IntervalOqlExpression extends IntervalExpression implements CheckableExpre
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;
}
/**
* 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
*/
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();
public function GetJoins()
{
return $this->m_aJoins;
}
public function GetCondition()
{
return $this->m_oCondition;
}
}
class OqlObjectQuery extends OqlQuery
@@ -312,43 +290,23 @@ class OqlObjectQuery extends OqlQuery
protected $m_aSelect; // array of selected classes
protected $m_oClass;
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)
{
$this->m_aSelect = $aSelect;
$this->m_oClass = $oClass;
$this->m_oClassAlias = $oClassAlias;
$this->m_aJoins = $aJoins;
$this->m_oCondition = $oCondition;
parent::__construct();
parent::__construct($oCondition, $aJoins);
}
public function GetSelectedClasses()
{
return $this->m_aSelect;
}
/**
* Determine the class
*
* @param ModelReflection $oModelReflection MetaModel to consider
* @return string
* @throws Exception
*/
public function GetClass(ModelReflection $oModelReflection)
public function GetClass()
{
return $this->m_oClass->GetValue();
}
/**
* Determine the class alias
*
* @return string
* @throws Exception
*/
public function GetClassAlias()
{
return $this->m_oClassAlias->GetValue();
@@ -363,15 +321,6 @@ class OqlObjectQuery extends OqlQuery
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
* and the query in which it is used
@@ -381,7 +330,7 @@ class OqlObjectQuery extends OqlQuery
*/
public function Check(ModelReflection $oModelReflection, $sSourceQuery)
{
$sClass = $this->GetClass($oModelReflection);
$sClass = $this->GetClass();
$sClassAlias = $this->GetClassAlias();
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));
}
$aExtKeys = $oModelReflection->ListAttributes($aAliases[$sFromClass], 'AttributeExternalKey');
$aObjKeys = $oModelReflection->ListAttributes($aAliases[$sFromClass], 'AttributeObjectKey');
$aAllKeys = array_merge($aExtKeys, $aObjKeys);
if (!array_key_exists($sExtKeyAttCode, $aAllKeys))
if (!array_key_exists($sExtKeyAttCode, $aExtKeys))
{
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 (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');
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());
}
throw new OqlNormalizeException("The joined class ($aAliases[$sFromClass]) is not compatible with the external key, which is pointing to $sTargetClass", $sSourceQuery, $oLeftField->GetNameDetails());
}
}
else
@@ -490,13 +434,10 @@ class OqlObjectQuery extends OqlQuery
$iOperatorCode = TREE_OPERATOR_NOT_ABOVE_STRICT;
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');
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());
}
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]);
$sAttType = $aAttList[$sExtKeyAttCode];
@@ -510,6 +451,7 @@ class OqlObjectQuery extends OqlQuery
// Check the select information
//
$aSelected = array();
foreach ($this->GetSelectedClasses() as $oClassDetails)
{
$sClassToSelect = $oClassDetails->GetValue();
@@ -517,6 +459,7 @@ class OqlObjectQuery extends OqlQuery
{
throw new OqlNormalizeException('Unknown class [alias]', $sSourceQuery, $oClassDetails, array_keys($aAliases));
}
$aSelected[$sClassToSelect] = $aAliases[$sClassToSelect];
}
// Check the condition tree
@@ -526,222 +469,6 @@ class OqlObjectQuery extends OqlQuery
$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;
}
}
?>

View File

@@ -1 +0,0 @@
2015-08-31

Some files were not shown because too many files have changed in this diff Show More