mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-14 07:54:10 +01:00
Compare commits
320 Commits
2.1.0-beta
...
2.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e331e6e12 | ||
|
|
7105b7a5fa | ||
|
|
1992adfac2 | ||
|
|
c2e8eca577 | ||
|
|
0cc466dd7e | ||
|
|
3eec1d358c | ||
|
|
1cc38fb58e | ||
|
|
91479bba53 | ||
|
|
1d4a3e780d | ||
|
|
af9a419e84 | ||
|
|
b311e924cd | ||
|
|
5c9b221b4c | ||
|
|
e94282459e | ||
|
|
77a0c0a7c6 | ||
|
|
c8fa3870db | ||
|
|
4261923126 | ||
|
|
554a462809 | ||
|
|
1206cc42bc | ||
|
|
48ab835646 | ||
|
|
bcd9141db6 | ||
|
|
e1fd65fe47 | ||
|
|
b2fe3cb033 | ||
|
|
df9cb7f0d4 | ||
|
|
07fdeb9284 | ||
|
|
6d04633daf | ||
|
|
2d95c131fc | ||
|
|
853c96478b | ||
|
|
86a7d133f3 | ||
|
|
1a6559efde | ||
|
|
c7cf8a9f74 | ||
|
|
8593f00917 | ||
|
|
6fd2c81315 | ||
|
|
3cbb0e974e | ||
|
|
02aa8339f8 | ||
|
|
7f64982fc0 | ||
|
|
d2e78d0292 | ||
|
|
11b768dace | ||
|
|
972c94bff7 | ||
|
|
489820cfe7 | ||
|
|
a3c4454090 | ||
|
|
bc6acee1f1 | ||
|
|
49a189c920 | ||
|
|
a35488b540 | ||
|
|
6b7071726b | ||
|
|
7d0282e59d | ||
|
|
f26bcd812c | ||
|
|
33762796b8 | ||
|
|
38b6582080 | ||
|
|
cd3122d597 | ||
|
|
b28a4c029c | ||
|
|
85899e6ac0 | ||
|
|
e21656c550 | ||
|
|
8fec8b7f80 | ||
|
|
8b45928d11 | ||
|
|
98150db0b4 | ||
|
|
96a4b83e31 | ||
|
|
84c31da226 | ||
|
|
cad5e703f8 | ||
|
|
62959a89bc | ||
|
|
c29f2eccaf | ||
|
|
664cfbf014 | ||
|
|
e1acce6e6e | ||
|
|
5fa83c84d3 | ||
|
|
1bb2d168fa | ||
|
|
52ad33f5b2 | ||
|
|
d9adcf01cd | ||
|
|
81d19c8804 | ||
|
|
5cbcebb79e | ||
|
|
40990020b1 | ||
|
|
5153139581 | ||
|
|
c852cd8e09 | ||
|
|
680104109b | ||
|
|
5f0938d01b | ||
|
|
1e533b24d1 | ||
|
|
7fa99cedee | ||
|
|
09cbf63c5a | ||
|
|
be3bce26ed | ||
|
|
5425f55af7 | ||
|
|
b6341741c3 | ||
|
|
a4f1a8f5ff | ||
|
|
aa2ab1118a | ||
|
|
71048be499 | ||
|
|
cba724d676 | ||
|
|
6f1d186287 | ||
|
|
4674658cfa | ||
|
|
4cfcb60e59 | ||
|
|
a230861afa | ||
|
|
e2207dc74c | ||
|
|
16b68ee154 | ||
|
|
1331f91061 | ||
|
|
138423aeec | ||
|
|
20815f1a91 | ||
|
|
35c57bb10c | ||
|
|
5fd653dae5 | ||
|
|
5a9b8a7bb0 | ||
|
|
5277a9eb38 | ||
|
|
b1887ae431 | ||
|
|
a3aed6aafc | ||
|
|
6903a36298 | ||
|
|
ea4c654af8 | ||
|
|
ec61417e39 | ||
|
|
3ba2c3d657 | ||
|
|
8b5faf6b66 | ||
|
|
76149633a1 | ||
|
|
d8113a3304 | ||
|
|
3fc19bf160 | ||
|
|
a30cb0b4c4 | ||
|
|
d29e9b525d | ||
|
|
7c8a348ead | ||
|
|
712931b728 | ||
|
|
2f0b122101 | ||
|
|
628b7644b7 | ||
|
|
88717ac9ab | ||
|
|
e5e90b1faf | ||
|
|
1a970d1372 | ||
|
|
b87b33c955 | ||
|
|
cfe9675709 | ||
|
|
b5f75271b9 | ||
|
|
90d5f5b8cf | ||
|
|
f84f17a5be | ||
|
|
ea8b254bd9 | ||
|
|
cb5f6e1ada | ||
|
|
fa94dd257a | ||
|
|
2a9ae8335d | ||
|
|
567317386a | ||
|
|
24a54f146c | ||
|
|
742abab420 | ||
|
|
c1c3cd3dc9 | ||
|
|
5e5739e37e | ||
|
|
69c0bcd4ca | ||
|
|
d994bbffd0 | ||
|
|
8c5b020961 | ||
|
|
a426cf07e9 | ||
|
|
d64641127a | ||
|
|
cbc0e36057 | ||
|
|
26405f8299 | ||
|
|
606e462b53 | ||
|
|
d424addb4c | ||
|
|
5427d6a466 | ||
|
|
b04298916c | ||
|
|
d3990ee2be | ||
|
|
4e567585af | ||
|
|
3bafb01202 | ||
|
|
dd5454591a | ||
|
|
df9f25dc3c | ||
|
|
a6b74d6538 | ||
|
|
0b045e5dd0 | ||
|
|
7a139dddc0 | ||
|
|
baf54a7c02 | ||
|
|
20e4dbfc1d | ||
|
|
77388bed29 | ||
|
|
cf5adc5ae7 | ||
|
|
1070283349 | ||
|
|
4ee78ea59c | ||
|
|
b8f0ecb134 | ||
|
|
efec6f6ec9 | ||
|
|
7f460eda5a | ||
|
|
678786c76c | ||
|
|
9917d6355c | ||
|
|
7f65e9fd5e | ||
|
|
9f92e5e0be | ||
|
|
6e92438282 | ||
|
|
f25980bb0d | ||
|
|
dd7861c5b4 | ||
|
|
35a4112840 | ||
|
|
2982f9cc9b | ||
|
|
bbd83fba30 | ||
|
|
cd5e5da526 | ||
|
|
f8df72b329 | ||
|
|
19e5130441 | ||
|
|
9ba1914524 | ||
|
|
586ec4515d | ||
|
|
4c2543d6f4 | ||
|
|
1aa489890c | ||
|
|
7a5bbd0613 | ||
|
|
d9fcd83370 | ||
|
|
73cd1274a5 | ||
|
|
35e58f8cd2 | ||
|
|
0769b2c481 | ||
|
|
80c0312219 | ||
|
|
3949632339 | ||
|
|
1eb4b0cec4 | ||
|
|
a1ba5bec17 | ||
|
|
7ca7cb39ae | ||
|
|
d1a74589b1 | ||
|
|
3e6896b8e6 | ||
|
|
3595434a05 | ||
|
|
7f1f1337fa | ||
|
|
7077879194 | ||
|
|
f314036cef | ||
|
|
af2835e505 | ||
|
|
d63b4ef6d1 | ||
|
|
f69109bc43 | ||
|
|
255df92a30 | ||
|
|
95defedf08 | ||
|
|
ec97e6d2e0 | ||
|
|
161a92fef2 | ||
|
|
da7ae0660e | ||
|
|
ca794b421d | ||
|
|
520ccd361c | ||
|
|
aa93fde347 | ||
|
|
fedde33be1 | ||
|
|
dc356ae7b6 | ||
|
|
fa333504c6 | ||
|
|
7c210f4d1c | ||
|
|
df47e2a9e9 | ||
|
|
87a3b73024 | ||
|
|
eb379662ce | ||
|
|
7176d5a19c | ||
|
|
ff1514dc75 | ||
|
|
59ebc262a3 | ||
|
|
26eb4c7083 | ||
|
|
ef8888c679 | ||
|
|
34ff5d6ac4 | ||
|
|
e64b6d1d98 | ||
|
|
b9b5287b37 | ||
|
|
5df6009f08 | ||
|
|
cca4737b91 | ||
|
|
bf1812ae83 | ||
|
|
0000cfd234 | ||
|
|
a876cd2186 | ||
|
|
d9b1d0faf3 | ||
|
|
2856d53967 | ||
|
|
9772b58333 | ||
|
|
b74ab0614e | ||
|
|
61a21520d1 | ||
|
|
ebfc9aa1e0 | ||
|
|
ff54d6dd6c | ||
|
|
828e4d6297 | ||
|
|
030b4fa380 | ||
|
|
328a5e8077 | ||
|
|
e8cbb2d39d | ||
|
|
887e73ea1d | ||
|
|
e210996839 | ||
|
|
2ba3ab3057 | ||
|
|
3cf0fa3ee2 | ||
|
|
8b36699893 | ||
|
|
166f5ce73f | ||
|
|
92baec128e | ||
|
|
4919ca88ec | ||
|
|
fa0d408664 | ||
|
|
444d9e36c6 | ||
|
|
95fc4d867d | ||
|
|
6524a40eaa | ||
|
|
f53943e78c | ||
|
|
528a8901df | ||
|
|
acd6d9679a | ||
|
|
f7c7fc5dc4 | ||
|
|
d575c48579 | ||
|
|
930d833e1b | ||
|
|
f7f77911be | ||
|
|
508f82946f | ||
|
|
6bb9754628 | ||
|
|
44fad50031 | ||
|
|
ed2cd2cea3 | ||
|
|
eaf74a3f23 | ||
|
|
1a99146b7a | ||
|
|
4a8e9e71f4 | ||
|
|
6d2d0ff701 | ||
|
|
af3c93051f | ||
|
|
f594190005 | ||
|
|
546d181ea9 | ||
|
|
143cefe4e3 | ||
|
|
ece152173f | ||
|
|
b08de31b3c | ||
|
|
0f967a41df | ||
|
|
4c3bf70cc4 | ||
|
|
35dd3f9610 | ||
|
|
a7f7424e54 | ||
|
|
83e2974b10 | ||
|
|
715ba066d3 | ||
|
|
9502003ff4 | ||
|
|
57c827bb1a | ||
|
|
690ac9be75 | ||
|
|
4c3c31c44d | ||
|
|
3c9ace5b53 | ||
|
|
bd5268dc42 | ||
|
|
133b6d4d29 | ||
|
|
fba3990c61 | ||
|
|
53e997cfba | ||
|
|
e738ba35b7 | ||
|
|
0773455ebc | ||
|
|
cafc6a8baf | ||
|
|
48f222df0b | ||
|
|
88726a0634 | ||
|
|
0ac522fc4c | ||
|
|
aa97703b64 | ||
|
|
30af416394 | ||
|
|
1f2ad9ecdb | ||
|
|
d1f1889c42 | ||
|
|
ca7ee0f138 | ||
|
|
909729e9f1 | ||
|
|
a222ba998d | ||
|
|
aedddf9dcc | ||
|
|
74de0d33ab | ||
|
|
8d8510a412 | ||
|
|
4dd83a0eb6 | ||
|
|
671ee353e8 | ||
|
|
d714235d67 | ||
|
|
c4b039c9c6 | ||
|
|
29e751278e | ||
|
|
87ed5d4a05 | ||
|
|
c8a20d01da | ||
|
|
47add0eeea | ||
|
|
2a9f69d70e | ||
|
|
e90e570469 | ||
|
|
b0e56e5897 | ||
|
|
c96842d82c | ||
|
|
bc79aecd73 | ||
|
|
99755ad7e8 | ||
|
|
4867461f69 | ||
|
|
3d21eecbba | ||
|
|
b822cff269 | ||
|
|
3aa0b77751 | ||
|
|
f4b10d3e81 | ||
|
|
ca90f9b32a | ||
|
|
9ca051d9d0 | ||
|
|
291f05683c | ||
|
|
4889ed5ac2 | ||
|
|
3fa354d00d |
@@ -271,6 +271,19 @@ 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
// Copyright (C) 2010-2015 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-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -42,7 +42,10 @@ class ajax_page extends WebPage implements iTabbedPage
|
||||
*/
|
||||
function __construct($s_title)
|
||||
{
|
||||
parent::__construct($s_title);
|
||||
$sPrintable = utils::ReadParam('printable', '0');
|
||||
$bPrintable = ($sPrintable == '1');
|
||||
|
||||
parent::__construct($s_title, $bPrintable);
|
||||
$this->m_sReadyScript = "";
|
||||
//$this->add_header("Content-type: text/html; charset=utf-8");
|
||||
$this->add_header("Cache-control: no-cache");
|
||||
@@ -197,7 +200,7 @@ EOF
|
||||
);
|
||||
}
|
||||
// Render the tabs in the page (if any)
|
||||
$this->s_content = $this->m_oTabs->RenderIntoContent($this->s_content);
|
||||
$this->s_content = $this->m_oTabs->RenderIntoContent($this->s_content, $this);
|
||||
|
||||
// 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)) )
|
||||
@@ -278,9 +281,9 @@ EOF
|
||||
echo self::FilterXSS($s_captured_output);
|
||||
}
|
||||
|
||||
if (class_exists('MetaModel'))
|
||||
if (class_exists('DBSearch'))
|
||||
{
|
||||
MetaModel::RecordQueryTrace();
|
||||
DBSearch::RecordQueryTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -781,8 +781,12 @@ class RestUtils
|
||||
$oSearch = new DBObjectSearch($sClass);
|
||||
foreach ($oCriteria as $sAttCode => $value)
|
||||
{
|
||||
$realValue = self::MakeValue($sClass, $sAttCode, $value);
|
||||
$realValue = static::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);
|
||||
@@ -814,7 +818,7 @@ class RestUtils
|
||||
{
|
||||
if (is_object($key))
|
||||
{
|
||||
$res = self::FindObjectFromCriteria($sClass, $key);
|
||||
$res = static::FindObjectFromCriteria($sClass, $key);
|
||||
}
|
||||
elseif (is_numeric($key))
|
||||
{
|
||||
@@ -878,7 +882,7 @@ class RestUtils
|
||||
$oSearch = new DBObjectSearch($sClass);
|
||||
foreach ($key as $sAttCode => $value)
|
||||
{
|
||||
$realValue = self::MakeValue($sClass, $sAttCode, $value);
|
||||
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
||||
$oSearch->AddCondition($sAttCode, $realValue, '=');
|
||||
}
|
||||
}
|
||||
@@ -922,7 +926,7 @@ class RestUtils
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef instanceof AttributeExternalKey)
|
||||
{
|
||||
$oExtKeyObject = self::FindObjectFromKey($oAttDef->GetTargetClass(), $value, true /* allow null */);
|
||||
$oExtKeyObject = static::FindObjectFromKey($oAttDef->GetTargetClass(), $value, true /* allow null */);
|
||||
$value = ($oExtKeyObject != null) ? $oExtKeyObject->GetKey() : 0;
|
||||
}
|
||||
elseif ($oAttDef instanceof AttributeLinkedSet)
|
||||
@@ -935,7 +939,7 @@ class RestUtils
|
||||
$aLinks = array();
|
||||
foreach($value as $oValues)
|
||||
{
|
||||
$oLnk = self::MakeObjectFromFields($sLnkClass, $oValues);
|
||||
$oLnk = static::MakeObjectFromFields($sLnkClass, $oValues);
|
||||
$aLinks[] = $oLnk;
|
||||
}
|
||||
$value = DBObjectSet::FromArray($sLnkClass, $aLinks);
|
||||
@@ -966,7 +970,7 @@ class RestUtils
|
||||
$oObject = MetaModel::NewObject($sClass);
|
||||
foreach ($aFields as $sAttCode => $value)
|
||||
{
|
||||
$realValue = self::MakeValue($sClass, $sAttCode, $value);
|
||||
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
||||
try
|
||||
{
|
||||
$oObject->Set($sAttCode, $realValue);
|
||||
@@ -993,7 +997,7 @@ class RestUtils
|
||||
$sClass = get_class($oObject);
|
||||
foreach ($aFields as $sAttCode => $value)
|
||||
{
|
||||
$realValue = self::MakeValue($sClass, $sAttCode, $value);
|
||||
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
||||
try
|
||||
{
|
||||
$oObject->Set($sAttCode, $realValue);
|
||||
|
||||
@@ -47,7 +47,7 @@ class AuditCategory extends cmdbAbstractObject
|
||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("description"=>"Short name for this category", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("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)));
|
||||
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)));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'definition_set', 'rules_list')); // Attributes to be displayed for the complete details
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 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-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -35,9 +35,9 @@ class CLIPage implements Page
|
||||
|
||||
public function output()
|
||||
{
|
||||
if (class_exists('MetaModel'))
|
||||
if (class_exists('DBSearch'))
|
||||
{
|
||||
MetaModel::RecordQueryTrace();
|
||||
DBSearch::RecordQueryTrace();
|
||||
}
|
||||
if (class_exists('ExecutionKPI'))
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
// Copyright (C) 2010-2015 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-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -32,6 +32,8 @@ 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');
|
||||
@@ -60,6 +62,39 @@ 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
|
||||
@@ -90,136 +125,156 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
'message' => $sMessage
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function DisplayBareHeader(WebPage $oPage, $bEditMode = false)
|
||||
{
|
||||
// Standard Header with name, actions menu and history block
|
||||
//
|
||||
|
||||
// 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']))
|
||||
if (!$oPage->IsPrintableVersion())
|
||||
{
|
||||
// Is there a message for this object ??
|
||||
$aMessages = array();
|
||||
$aRanks = array();
|
||||
foreach ($_SESSION['obj_messages'][$sMessageKey] as $sMessageId => $aMessageData)
|
||||
if (MetaModel::GetConfig()->Get('concurrent_lock_enabled'))
|
||||
{
|
||||
$sMsgClass = 'message_'.$aMessageData['severity'];
|
||||
$aMessages[] = "<div class=\"header_message $sMsgClass\">".$aMessageData['message']."</div>";
|
||||
$aRanks[] = $aMessageData['rank'];
|
||||
$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]);
|
||||
}
|
||||
array_multisort($aRanks, $aMessages);
|
||||
foreach ($aMessages as $sMessage)
|
||||
{
|
||||
$oPage->add($sMessage);
|
||||
}
|
||||
unset($_SESSION['obj_messages'][$sMessageKey]);
|
||||
}
|
||||
|
||||
// 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 = '';
|
||||
$bSynchronized = false;
|
||||
$oCreatorTask = null;
|
||||
$bCanBeDeletedByTask = false;
|
||||
$bCanBeDeletedByUser = true;
|
||||
$aMasterSources = array();
|
||||
$aSyncData = $this->GetSynchroData();
|
||||
if (count($aSyncData) > 0)
|
||||
|
||||
if (!$oPage->IsPrintableVersion())
|
||||
{
|
||||
$bSynchronized = true;
|
||||
foreach ($aSyncData as $iSourceId => $aSourceData)
|
||||
{
|
||||
$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');
|
||||
}
|
||||
|
||||
if (is_object($oCreatorTask))
|
||||
{
|
||||
$sTaskUrl = $aMasterSources[$oCreatorTask->GetKey()]['url'];
|
||||
if (!$bCanBeDeletedByUser)
|
||||
{
|
||||
$sTip = "<p>".Dict::Format('Core:Synchro:TheObjectCannotBeDeletedByUser_Source', $sTaskUrl)."</p>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sTip = "<p>".Dict::Format('Core:Synchro:TheObjectWasCreatedBy_Source', $sTaskUrl)."</p>";
|
||||
}
|
||||
if ($bCanBeDeletedByTask)
|
||||
{
|
||||
$sTip .= "<p>".Dict::Format('Core:Synchro:TheObjectCanBeDeletedBy_Source', $sTaskUrl)."</p>";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sTip = "<p>".Dict::S('Core:Synchro:ThisObjectIsSynchronized')."</p>";
|
||||
}
|
||||
|
||||
$sTip .= "<p><b>".Dict::S('Core:Synchro:ListOfDataSources')."</b></p>";
|
||||
foreach($aMasterSources as $aStruct)
|
||||
{
|
||||
$oDataSource = $aStruct['datasource'];
|
||||
$sLink = $aStruct['url'];
|
||||
$sTip .= "<p style=\"white-space:nowrap\">".$oDataSource->GetIcon(true, 'style="vertical-align:middle"')." $sLink<br/>";
|
||||
$sTip .= Dict::S('Core:Synchro:LastSynchro').'<br/>'.$aStruct['last_synchro']."</p>";
|
||||
}
|
||||
$sSynchroIcon = ' <img style="vertical-align:middle;" id="synchro_icon" src="../images/locked.png"/>';
|
||||
$sTip = addslashes($sTip);
|
||||
$oPage->add_ready_script("$('#synchro_icon').qtip( { content: '$sTip', show: 'mouseover', hide: { fixed: true }, style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
|
||||
// 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()." \n");
|
||||
$sRefreshIcon = '';
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'GET')
|
||||
|
||||
// Master data sources
|
||||
$bSynchronized = false;
|
||||
$aIcons = array();
|
||||
if (!$oPage->IsPrintableVersion())
|
||||
{
|
||||
$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').'"/>';
|
||||
$oCreatorTask = null;
|
||||
$bCanBeDeletedByTask = false;
|
||||
$bCanBeDeletedByUser = true;
|
||||
$aMasterSources = array();
|
||||
$aSyncData = $this->GetSynchroData();
|
||||
if (count($aSyncData) > 0)
|
||||
{
|
||||
$bSynchronized = true;
|
||||
foreach ($aSyncData as $iSourceId => $aSourceData)
|
||||
{
|
||||
$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');
|
||||
}
|
||||
|
||||
if (is_object($oCreatorTask))
|
||||
{
|
||||
$sTaskUrl = $aMasterSources[$oCreatorTask->GetKey()]['url'];
|
||||
if (!$bCanBeDeletedByUser)
|
||||
{
|
||||
$sTip = "<p>".Dict::Format('Core:Synchro:TheObjectCannotBeDeletedByUser_Source', $sTaskUrl)."</p>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sTip = "<p>".Dict::Format('Core:Synchro:TheObjectWasCreatedBy_Source', $sTaskUrl)."</p>";
|
||||
}
|
||||
if ($bCanBeDeletedByTask)
|
||||
{
|
||||
$sTip .= "<p>".Dict::Format('Core:Synchro:TheObjectCanBeDeletedBy_Source', $sTaskUrl)."</p>";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sTip = "<p>".Dict::S('Core:Synchro:ThisObjectIsSynchronized')."</p>";
|
||||
}
|
||||
|
||||
$sTip .= "<p><b>".Dict::S('Core:Synchro:ListOfDataSources')."</b></p>";
|
||||
foreach($aMasterSources as $aStruct)
|
||||
{
|
||||
$oDataSource = $aStruct['datasource'];
|
||||
$sLink = $aStruct['url'];
|
||||
$sTip .= "<p style=\"white-space:nowrap\">".$oDataSource->GetIcon(true, 'style="vertical-align:middle"')." $sLink<br/>";
|
||||
$sTip .= Dict::S('Core:Synchro:LastSynchro').'<br/>'.$aStruct['last_synchro']."</p>";
|
||||
}
|
||||
$aIcons[] = ' <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' }} } );");
|
||||
}
|
||||
}
|
||||
$oPage->add(MetaModel::GetName(get_class($this)).": <span class=\"hilite\">".$this->GetName()."</span>$sRefreshIcon $sSynchroIcon</h1>\n");
|
||||
|
||||
$sIcons = implode(' ', $aIcons);
|
||||
$oPage->add(MetaModel::GetName(get_class($this)).": <span class=\"hilite\">".$this->GetName()."</span>$sIcons</h1>\n");
|
||||
$oPage->add("</div>\n");
|
||||
|
||||
}
|
||||
@@ -247,7 +302,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
$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)
|
||||
@@ -275,6 +330,8 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
|
||||
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'));
|
||||
@@ -340,6 +397,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
// 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))
|
||||
{
|
||||
@@ -359,7 +417,6 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
|
||||
$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);
|
||||
@@ -411,6 +468,29 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
$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('');
|
||||
|
||||
@@ -527,18 +607,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
|
||||
$sComments = isset($aFieldsComments[$sAttCode]) ? $aFieldsComments[$sAttCode] : ' ';
|
||||
$sInfos = ' ';
|
||||
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
|
||||
}
|
||||
$iFlags = $this->GetFormAttributeFlags($sAttCode);
|
||||
if (array_key_exists($sAttCode, $aExtraFlags))
|
||||
{
|
||||
// the caller may override some flags if needed
|
||||
@@ -693,6 +762,37 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
{
|
||||
$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
|
||||
@@ -703,6 +803,11 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
*/
|
||||
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 !!
|
||||
@@ -1402,14 +1507,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
$sHtml .= "<h2>".Dict::Format('UI:SearchFor_Class_Objects', $sClassesCombo)."</h2>\n";
|
||||
$index = 0;
|
||||
$sHtml .= "<p>\n";
|
||||
$aFilterCriteria = $oSet->GetFilter()->GetCriteria();
|
||||
$aMapCriteria = array();
|
||||
// Todo: Investigate... The search criteria is an expression, i.e. a tree!
|
||||
// I wonder if that code could work... cleanup required/recommended
|
||||
foreach($aFilterCriteria as $aCriteria)
|
||||
{
|
||||
$aMapCriteria[$aCriteria['filtercode']][] = array('value' => $aCriteria['value'], 'opcode' => $aCriteria['opcode']);
|
||||
}
|
||||
$aList = MetaModel::GetZListItems($sClassName, 'standard_search');
|
||||
$aConsts = $oSet->ListConstantFields(); // Some fields are constants based on the query/context
|
||||
$sClassAlias = $oSet->GetFilter()->GetClassAlias();
|
||||
@@ -1522,7 +1620,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
{
|
||||
if (is_scalar($sValue))
|
||||
{
|
||||
$sHtml .= "<input type=\"hidden\" name=\"$sName\" value=\"$sValue\" />\n";
|
||||
$sHtml .= "<input type=\"hidden\" name=\"$sName\" value=\"".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."\" />\n";
|
||||
}
|
||||
}
|
||||
$sHtml .= "<input type=\"hidden\" name=\"class\" value=\"$sClassName\" />\n";
|
||||
@@ -1732,7 +1830,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
break;
|
||||
|
||||
case 'HTML':
|
||||
$oWidget = new UIHTMLEditorWidget($iId, $sAttCode, $sNameSuffix, $sFieldPrefix, $sHelpText, $sValidationField, $value, $bMandatory);
|
||||
$oWidget = new UIHTMLEditorWidget($iId, $oAttDef, $sNameSuffix, $sFieldPrefix, $sHelpText, $sValidationField, $value, $bMandatory);
|
||||
$sHTMLValue = $oWidget->Display($oPage, $aArgs);
|
||||
break;
|
||||
|
||||
@@ -1796,6 +1894,20 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
$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';
|
||||
@@ -1854,7 +1966,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
{
|
||||
$sNullValue = "'$sNullValue'"; // Add quotes to turn this into a JS string if it's not a number
|
||||
}
|
||||
$sOriginalValue = ($iFlags & OPT_ATT_MUSTCHANGE) ? "'".addslashes($value)."'" : 'undefined';
|
||||
$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
|
||||
}
|
||||
$aDependencies = MetaModel::GetDependentAttributes($sClass, $sAttCode); // List of attributes that depend on the current one
|
||||
@@ -1872,6 +1984,53 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
|
||||
public function DisplayModifyForm(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
$sOwnershipToken = null;
|
||||
$iKey = $this->GetKey();
|
||||
$sClass = get_class($this);
|
||||
if ($iKey > 0)
|
||||
{
|
||||
// The concurrent access lock makes sense only for already existing objects
|
||||
$LockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled');
|
||||
if ($LockEnabled)
|
||||
{
|
||||
$sOwnershipToken = utils::ReadPostedParam('ownership_token', null, false, 'raw_data');
|
||||
if ($sOwnershipToken !== null)
|
||||
{
|
||||
// We're probably inside something like "apply_modify" where the validation failed and we must prompt the user again to edit the object
|
||||
// let's extend our lock
|
||||
$aLockInfo = iTopOwnershipLock::ExtendLock($sClass, $iKey, $sOwnershipToken);
|
||||
$sOwnershipDate = $aLockInfo['acquired'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$aLockInfo = iTopOwnershipLock::AcquireLock($sClass, $iKey);
|
||||
if ($aLockInfo['success'])
|
||||
{
|
||||
$sOwnershipToken = $aLockInfo['token'];
|
||||
$sOwnershipDate = $aLockInfo['acquired'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$oOwner = $aLockInfo['lock']->GetOwner();
|
||||
// If the object is locked by the current user, it's worth trying again, since
|
||||
// the lock may be released by 'onunload' which is called AFTER loading the current page.
|
||||
//$bTryAgain = $oOwner->GetKey() == UserRights::GetUserId();
|
||||
self::ReloadAndDisplay($oPage, $this, array('operation' => 'modify'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($aExtraParams['wizard_container']) && $aExtraParams['wizard_container'])
|
||||
{
|
||||
$sClassLabel = MetaModel::GetName($sClass);
|
||||
$oPage->set_title(Dict::Format('UI:ModificationPageTitle_Object_Class', $this->GetRawName(), $sClassLabel)); // Set title will take care of the encoding
|
||||
$oPage->add("<div class=\"page_header\">\n");
|
||||
$oPage->add("<h1>".$this->GetIcon()." ".Dict::Format('UI:ModificationTitle_Class_Object', $sClassLabel, $this->GetName())."</h1>\n");
|
||||
$oPage->add("</div>\n");
|
||||
$oPage->add("<div class=\"wizContainer\">\n");
|
||||
}
|
||||
self::$iGlobalFormId++;
|
||||
$this->aFieldsMap = array();
|
||||
$sPrefix = '';
|
||||
@@ -1882,15 +2041,13 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
$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 = $_SERVER['SCRIPT_NAME']; // No parameter in the URL, the only parameter will be the ones passed through the form
|
||||
$sFormAction = utils::GetAbsoluteUrlAppRoot().'pages/'.$this->GetUIPage(); // No parameter in the URL, the only parameter will be the ones passed through the form
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1986,9 +2143,10 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
}
|
||||
|
||||
$sConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
|
||||
$sJSToken = json_encode($sOwnershipToken);
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$(window).unload(function() { return OnUnload('$iTransactionId') } );
|
||||
$(window).unload(function() { return OnUnload('$iTransactionId', '$sClass', $iKey, $sJSToken) } );
|
||||
window.onbeforeunload = function() {
|
||||
if (!window.bInSubmit && !window.bInCancel)
|
||||
{
|
||||
@@ -2032,6 +2190,10 @@ 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')
|
||||
{
|
||||
@@ -2042,21 +2204,29 @@ 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')} );");
|
||||
$oPage->add_ready_script("$('#form_{$this->m_iFormId} button.cancel').click( function() { BackToDetails('$sClass', $iKey, '$sDefaultUrl', $sJSToken)} );");
|
||||
$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();
|
||||
@@ -2064,7 +2234,27 @@ 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())
|
||||
@@ -2143,6 +2333,7 @@ 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]))
|
||||
@@ -2150,6 +2341,28 @@ 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];
|
||||
@@ -2176,15 +2389,27 @@ EOF
|
||||
$iFieldIndex = 0;
|
||||
$aFieldsMap = array();
|
||||
|
||||
$aDetailsList =$this->FlattenZList(MetaModel::GetZListItems($sClass, 'details'));
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
// 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($aDetailsList as $sAttCode)
|
||||
foreach($aAttributes as $sAttCode => $trash)
|
||||
{
|
||||
$aDeps[$sAttCode] = MetaModel::GetPrequisiteAttributes($sClass, $sAttCode);
|
||||
}
|
||||
$aList =$this->OrderDependentFields($aDeps);
|
||||
$aList = $this->OrderDependentFields($aDeps);
|
||||
|
||||
foreach($aList as $sAttCode)
|
||||
{
|
||||
@@ -2238,10 +2463,15 @@ 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");
|
||||
$oPage->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::GetNewTransactionId()."\">\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");
|
||||
}
|
||||
$oAppContext = new ApplicationContext();
|
||||
$oPage->add($oAppContext->GetForForm());
|
||||
$oPage->add("<button type=\"button\" class=\"action\" onClick=\"BackToDetails('$sClass', ".$this->GetKey().")\"><span>".Dict::S('UI:Button:Cancel')."</span></button> \n");
|
||||
$oPage->add("<button type=\"button\" class=\"action cancel\" onClick=\"BackToDetails('$sClass', ".$this->GetKey().", '', '$sOwnershipToken')\"><span>".Dict::S('UI:Button:Cancel')."</span></button> \n");
|
||||
$oPage->add("<button type=\"submit\" class=\"action\"><span>$sActionLabel</span></button>\n");
|
||||
$oPage->add("</form>\n");
|
||||
$oPage->add("</div>\n");
|
||||
@@ -2264,12 +2494,19 @@ 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)
|
||||
@@ -2632,6 +2869,26 @@ 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)
|
||||
@@ -2826,6 +3083,10 @@ 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)) )
|
||||
{
|
||||
@@ -2944,7 +3205,7 @@ EOF
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected static function BulkUpdateTracked_Internal(DBObjectSearch $oFilter, array $aValues)
|
||||
protected static function BulkUpdateTracked_Internal(DBSearch $oFilter, array $aValues)
|
||||
{
|
||||
// Todo - invoke the extension
|
||||
return parent::BulkUpdateTracked_Internal($oFilter, $aValues);
|
||||
@@ -3427,7 +3688,7 @@ EOF
|
||||
$oP->Table($aHeaders, $aRows);
|
||||
if ($bPreview)
|
||||
{
|
||||
$sFormAction = $_SERVER['SCRIPT_NAME']; // No parameter in the URL, the only parameter will be the ones passed through the form
|
||||
$sFormAction = utils::GetAbsoluteUrlAppRoot().'pages/UI.php'; // 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());
|
||||
@@ -3748,5 +4009,77 @@ EOF
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find redundancy settings that can be viewed and modified in a tab
|
||||
* Settings are distributed to the corresponding link set attribute so as to be shown in the relevant tab
|
||||
*/
|
||||
protected function FindVisibleRedundancySettings()
|
||||
{
|
||||
$aRet = array();
|
||||
foreach (MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
|
||||
{
|
||||
if ($oAttDef instanceof AttributeRedundancySettings)
|
||||
{
|
||||
if ($oAttDef->IsVisible())
|
||||
{
|
||||
$aQueryInfo = $oAttDef->GetRelationQueryData();
|
||||
if (isset($aQueryInfo['sAttribute']))
|
||||
{
|
||||
$oUpperAttDef = MetaModel::GetAttributeDef($aQueryInfo['sFromClass'], $aQueryInfo['sAttribute']);
|
||||
$oHostAttDef = $oUpperAttDef->GetMirrorLinkAttribute();
|
||||
if ($oHostAttDef)
|
||||
{
|
||||
$sHostAttCode = $oHostAttDef->GetCode();
|
||||
$aRet[$sHostAttCode][] = $oAttDef;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the javascript code handle the "watchdog" associated with the concurrent access locking mechanism
|
||||
* @param Webpage $oPage
|
||||
* @param string $sOwnershipToken
|
||||
*/
|
||||
protected function GetOwnershipJSHandler($oPage, $sOwnershipToken)
|
||||
{
|
||||
$iInterval = max(MIN_WATCHDOG_INTERVAL, MetaModel::GetConfig()->Get('concurrent_lock_expiration_delay')) * 1000 / 2; // Minimum interval for the watchdog is MIN_WATCHDOG_INTERVAL
|
||||
$sJSClass = json_encode(get_class($this));
|
||||
$iKey = (int) $this->GetKey();
|
||||
$sJSToken = json_encode($sOwnershipToken);
|
||||
$sJSTitle = json_encode(Dict::S('UI:DisconnectedDlgTitle'));
|
||||
$sJSOk = json_encode(Dict::S('UI:Button:Ok'));
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
window.setInterval(function() {
|
||||
if (window.bInSubmit || window.bInCancel) return;
|
||||
|
||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'extend_lock', obj_class: $sJSClass, obj_key: $iKey, token: $sJSToken }, function(data) {
|
||||
if (!data.status)
|
||||
{
|
||||
if ($('.lock_owned').length == 0)
|
||||
{
|
||||
$('.ui-layout-content').prepend('<div class="header_message message_error lock_owned">'+data.message+'</div>');
|
||||
$('<div>'+data.popup_message+'</div>').dialog({title: $sJSTitle, modal: true, autoOpen: true, buttons:[ {text: $sJSOk, click: function() { $(this).dialog('close'); } }], close: function() { $(this).remove(); }});
|
||||
}
|
||||
$('.wizContainer form button.action:not(.cancel)').attr('disabled', 'disabled');
|
||||
}
|
||||
else if ((data.operation == 'lost') || (data.operation == 'expired'))
|
||||
{
|
||||
if ($('.lock_owned').length == 0)
|
||||
{
|
||||
$('.ui-layout-content').prepend('<div class="header_message message_error lock_owned">'+data.message+'</div>');
|
||||
$('<div>'+data.popup_message+'</div>').dialog({title: $sJSTitle, modal: true, autoOpen: true, buttons:[ {text: $sJSOk, click: function() { $(this).dialog('close'); } }], close: function() { $(this).remove(); }});
|
||||
}
|
||||
$('.wizContainer form button.action:not(.cancel)').attr('disabled', 'disabled');
|
||||
}
|
||||
}, 'json');
|
||||
}, $iInterval);
|
||||
EOF
|
||||
);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
// Copyright (C) 2010-2015 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-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -51,9 +51,9 @@ class CSVPage extends WebPage
|
||||
echo trim($this->s_content);
|
||||
echo "\n";
|
||||
|
||||
if (class_exists('MetaModel'))
|
||||
if (class_exists('DBSearch'))
|
||||
{
|
||||
MetaModel::RecordQueryTrace();
|
||||
DBSearch::RecordQueryTrace();
|
||||
}
|
||||
if (class_exists('ExecutionKPI'))
|
||||
{
|
||||
|
||||
@@ -91,7 +91,7 @@ abstract class Dashboard
|
||||
}
|
||||
if ($oAutoReloadInterval = $oAutoReloadNode->getElementsByTagName('interval')->item(0))
|
||||
{
|
||||
$this->iAutoReloadSec = max(5, (int)$oAutoReloadInterval->textContent);
|
||||
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$oAutoReloadInterval->textContent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ abstract class Dashboard
|
||||
$this->sLayoutClass = $aParams['layout_class'];
|
||||
$this->sTitle = $aParams['title'];
|
||||
$this->bAutoReload = $aParams['auto_reload'] == 'true';
|
||||
$this->iAutoReloadSec = max(5, (int) $aParams['auto_reload_sec']);
|
||||
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int) $aParams['auto_reload_sec']);
|
||||
|
||||
foreach($aParams['cells'] as $aCell)
|
||||
{
|
||||
@@ -300,7 +300,7 @@ abstract class Dashboard
|
||||
|
||||
public function SetAutoReloadInterval($iAutoReloadSec)
|
||||
{
|
||||
$this->iAutoReloadSec = max(5, (int)$iAutoReloadSec);
|
||||
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$iAutoReloadSec);
|
||||
}
|
||||
|
||||
public function AddDashlet($oDashlet)
|
||||
@@ -312,7 +312,7 @@ abstract class Dashboard
|
||||
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
|
||||
{
|
||||
$oPage->add('<h1>'.Dict::S($this->sTitle).'</h1>');
|
||||
$oPage->add('<h1>'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'</h1>');
|
||||
$oLayout = new $this->sLayoutClass;
|
||||
$oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams);
|
||||
if (!$bEditMode)
|
||||
@@ -357,16 +357,17 @@ abstract class Dashboard
|
||||
$oField = new DesignerBooleanField('auto_reload', Dict::S('UI:DashboardEdit:AutoReload'), $this->bAutoReload);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerTextField('auto_reload_sec', Dict::S('UI:DashboardEdit:AutoReloadSec'), $this->iAutoReloadSec);
|
||||
$oField->SetValidationPattern('^$|^0*([5-9]|[1-9][0-9]+)$'); // Can be empty, or a number > 4
|
||||
$oField = new DesignerIntegerField('auto_reload_sec', Dict::S('UI:DashboardEdit:AutoReloadSec'), $this->iAutoReloadSec);
|
||||
$oField->SetBoundaries(MetaModel::GetConfig()->Get('min_reload_interval'), null); // no upper limit
|
||||
$oForm->AddField($oField);
|
||||
|
||||
|
||||
$this->SetFormParams($oForm);
|
||||
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
|
||||
|
||||
$oPage->add('</div>');
|
||||
|
||||
$sRateTitle = addslashes(Dict::S('UI:DashboardEdit:AutoReloadSec+'));
|
||||
$sRateTitle = addslashes(Dict::Format('UI:DashboardEdit:AutoReloadSec+', MetaModel::GetConfig()->Get('min_reload_interval')));
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
// Note: the title gets deleted by the validation mechanism
|
||||
@@ -545,7 +546,9 @@ class RuntimeDashboard extends Dashboard
|
||||
|
||||
public function RenderEditionTools($oPage)
|
||||
{
|
||||
$sEditMenu = "<td><span id=\"DashboardMenu\"><ul><li><img src=\"../images/edit.png\"><ul>";
|
||||
$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>";
|
||||
|
||||
$aActions = array();
|
||||
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:Edit'), "return EditDashboard('{$this->sId}')");
|
||||
|
||||
@@ -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>Search for Server objects</a>');
|
||||
$oPage->add(' <a>'.Dict::Format('UI:SearchFor_Class', $sClassLabel).'</a>');
|
||||
$oPage->add('</p>');
|
||||
$oPage->add('</div>');
|
||||
|
||||
|
||||
22
application/datamodel.application.xml
Normal file
22
application/datamodel.application.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design>
|
||||
<portals>
|
||||
<portal id="legacy_portal" _delta="define">
|
||||
<url>portal/index.php</url>
|
||||
<rank>1.0</rank>
|
||||
<handler/>
|
||||
<allow>
|
||||
</allow>
|
||||
<deny/>
|
||||
</portal>
|
||||
<portal id="backoffice" _delta="define">
|
||||
<url>pages/UI.php</url>
|
||||
<rank>2.0</rank>
|
||||
<handler/>
|
||||
<allow/>
|
||||
<deny>
|
||||
<profile id="Portal user"/>
|
||||
</deny>
|
||||
</portal>
|
||||
</portals>
|
||||
</itop_design>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
// Copyright (C) 2010-2015 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-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -290,17 +290,24 @@ EOF;
|
||||
|
||||
protected function GetToolkitMenu(WebPage $oPage, $aExtraParams)
|
||||
{
|
||||
$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);
|
||||
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 = '';
|
||||
}
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
@@ -606,6 +613,34 @@ 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;
|
||||
@@ -713,6 +748,12 @@ 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)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
/**
|
||||
* DisplayBlock and derived class
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 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(DBObjectSearch $oFilter, $sStyle = 'list', $bAsynchronous = false, $aParams = array(), $oSet = null)
|
||||
public function __construct(DBSearch $oFilter, $sStyle = 'list', $bAsynchronous = false, $aParams = array(), $oSet = null)
|
||||
{
|
||||
$this->m_oFilter = $oFilter->DeepClone();
|
||||
$this->m_aConditions = array();
|
||||
@@ -73,6 +73,7 @@ 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();
|
||||
@@ -179,11 +180,11 @@ class DisplayBlock
|
||||
switch($sEncoding)
|
||||
{
|
||||
case 'text/serialize':
|
||||
$oFilter = CMDBSearchFilter::unserialize($sITopData);
|
||||
$oFilter = DBSearch::unserialize($sITopData);
|
||||
break;
|
||||
|
||||
case 'text/oql':
|
||||
$oFilter = CMDBSearchFilter::FromOQL($sITopData);
|
||||
$oFilter = DBSearch::FromOQL($sITopData);
|
||||
break;
|
||||
}
|
||||
return new $sBlockClass($oFilter, $sBlockType, $bAsynchronous, $aParams);
|
||||
@@ -226,7 +227,7 @@ class DisplayBlock
|
||||
if (is_numeric($aExtraParams['auto_reload']) && ($aExtraParams['auto_reload'] > 0))
|
||||
{
|
||||
$bAutoReload = true;
|
||||
$iReloadInterval = max(5, $aExtraParams['auto_reload'])*1000;
|
||||
$iReloadInterval = max(MetaModel::GetConfig()->Get('min_reload_interval'), $aExtraParams['auto_reload'])*1000;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -406,7 +407,7 @@ class DisplayBlock
|
||||
|
||||
$aGroupBy = array();
|
||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy, true);
|
||||
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
|
||||
$aGroupBy = array();
|
||||
@@ -445,6 +446,8 @@ 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
|
||||
{
|
||||
@@ -702,7 +705,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&class={$sClass}&$sParams\">".Dict::Format('UI:SearchFor_Class', MetaModel::GetName($sClass))."</a>\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 .= '</p>';
|
||||
break;
|
||||
|
||||
@@ -765,6 +768,7 @@ 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':
|
||||
@@ -848,21 +852,24 @@ class DisplayBlock
|
||||
break;
|
||||
|
||||
case 'search':
|
||||
$sStyle = (isset($aExtraParams['open']) && ($aExtraParams['open'] == 'true')) ? 'SearchDrawer' : 'SearchDrawer DrawerClosed';
|
||||
$sHtml .= "<div id=\"ds_$sId\" class=\"$sStyle\">\n";
|
||||
$oPage->add_ready_script(
|
||||
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(
|
||||
<<<EOF
|
||||
$("#dh_$sId").click( function() {
|
||||
$("#ds_$sId").slideToggle('normal', function() { $("#ds_$sId").parent().resize(); } );
|
||||
$("#dh_$sId").toggleClass('open');
|
||||
});
|
||||
$("#dh_$sId").click( function() {
|
||||
$("#ds_$sId").slideToggle('normal', function() { $("#ds_$sId").parent().resize(); FixSearchFormsDisposition(); $("#dh_$sId").trigger('toggle_complete'); } );
|
||||
$("#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':
|
||||
@@ -909,7 +916,7 @@ EOF
|
||||
|
||||
$aGroupBy = array();
|
||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy, true);
|
||||
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
|
||||
$aGroupBy = array();
|
||||
@@ -984,7 +991,7 @@ EOF
|
||||
|
||||
$aGroupBy = array();
|
||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy, true);
|
||||
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
|
||||
$aGroupBy = array();
|
||||
@@ -1066,7 +1073,7 @@ EOF
|
||||
$aGroupBy = array();
|
||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy, true);
|
||||
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
|
||||
$aGroupBy = array();
|
||||
@@ -1126,7 +1133,7 @@ EOF
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a condition (restriction) to the current DBObjectSearch on which the display block is based
|
||||
* Add a condition (restriction) to the current DBSearch 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)
|
||||
@@ -1214,7 +1221,7 @@ class HistoryBlock extends DisplayBlock
|
||||
protected $iLimitCount;
|
||||
protected $iLimitStart;
|
||||
|
||||
public function __construct(DBObjectSearch $oFilter, $sStyle = 'list', $bAsynchronous = false, $aParams = array(), $oSet = null)
|
||||
public function __construct(DBSearch $oFilter, $sStyle = 'list', $bAsynchronous = false, $aParams = array(), $oSet = null)
|
||||
{
|
||||
parent::__construct($oFilter, $sStyle, $bAsynchronous, $aParams, $oSet);
|
||||
$this->iLimitStart = 0;
|
||||
@@ -1232,12 +1239,15 @@ class HistoryBlock extends DisplayBlock
|
||||
$sHtml = '';
|
||||
$bTruncated = false;
|
||||
$oSet = new CMDBObjectSet($this->m_oFilter, array('date'=>false));
|
||||
if (($this->iLimitStart > 0) || ($this->iLimitCount > 0))
|
||||
if (!$oPage->IsPrintableVersion())
|
||||
{
|
||||
$oSet->SetLimit($this->iLimitCount, $this->iLimitStart);
|
||||
if (($this->iLimitCount - $this->iLimitStart) < $oSet->Count())
|
||||
if (($this->iLimitStart > 0) || ($this->iLimitCount > 0))
|
||||
{
|
||||
$bTruncated = true;
|
||||
$oSet->SetLimit($this->iLimitCount, $this->iLimitStart);
|
||||
if (($this->iLimitCount - $this->iLimitStart) < $oSet->Count())
|
||||
{
|
||||
$bTruncated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$sHtml .= "<!-- filter: ".($this->m_oFilter->ToOQL())."-->\n";
|
||||
@@ -1280,7 +1290,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;
|
||||
}
|
||||
@@ -1371,7 +1381,8 @@ class MenuBlock extends DisplayBlock
|
||||
$sDefault.= "&default[$sKey]=$sValue";
|
||||
}
|
||||
}
|
||||
$bIsCreationAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_CREATE) == UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
||||
$bIsCreationAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_CREATE) == UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
||||
$sRefreshAction = '';
|
||||
switch($oSet->Count())
|
||||
{
|
||||
case 0:
|
||||
@@ -1381,67 +1392,143 @@ class MenuBlock extends DisplayBlock
|
||||
|
||||
case 1:
|
||||
$oObj = $oSet->Fetch();
|
||||
$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 (is_null($oObj))
|
||||
{
|
||||
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 (!isset($aExtraParams['link_attr']))
|
||||
{
|
||||
$this->AddMenuSeparator($aActions);
|
||||
$aStimuli = Metamodel::EnumStimuli(get_class($oObj));
|
||||
foreach($aTransitions as $sStimulusCode => $aTransitionDef)
|
||||
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')
|
||||
{
|
||||
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass, $sStimulusCode, $oSet) : UR_ALLOWED_NO;
|
||||
switch($iActionAllowed)
|
||||
$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))
|
||||
{
|
||||
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);
|
||||
$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::EnumRelations($sClass);
|
||||
if (count($aRelations))
|
||||
{
|
||||
$this->AddMenuSeparator($aActions);
|
||||
foreach($aRelations as $sRelationCode)
|
||||
// Relations...
|
||||
$aRelations = MetaModel::EnumRelationsEx($sClass);
|
||||
if (count($aRelations))
|
||||
{
|
||||
$aActions[$sRelationCode] = array ('label' => MetaModel::GetRelationVerbUp($sRelationCode), 'url' => "{$sRootUrl}pages/$sUIPage?operation=swf_navigator&relation=$sRelationCode&class=$sClass&id=$id{$sContext}");
|
||||
$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')
|
||||
{
|
||||
// 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);
|
||||
// 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')
|
||||
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
|
||||
{
|
||||
// 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);
|
||||
$oSet->Rewind();
|
||||
foreach($oExtensionInstance->EnumAllowedActions($oSet) as $sLabel => $sUrl)
|
||||
{
|
||||
$aActions[$sLabel] = array ('label' => $sLabel, 'url' => $sUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1487,7 +1574,7 @@ class MenuBlock extends DisplayBlock
|
||||
{
|
||||
$aQueryParams = $aExtraParams['query_params'];
|
||||
}
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy);
|
||||
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
if (count($aRes) == 1)
|
||||
{
|
||||
@@ -1587,16 +1674,23 @@ class MenuBlock extends DisplayBlock
|
||||
$aShortcutActions = array();
|
||||
}
|
||||
|
||||
if (count($aFavoriteActions) > 0)
|
||||
if (!$oPage->IsPrintableVersion())
|
||||
{
|
||||
$sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li>".Dict::S('UI:Menu:OtherActions')."\n<ul>\n";
|
||||
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>";
|
||||
}
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -84,15 +84,14 @@ 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)
|
||||
@@ -108,7 +107,7 @@ class DesignerForm
|
||||
$aRow = $oField->Render($oP, $sFormId);
|
||||
if ($oField->IsVisible())
|
||||
{
|
||||
$sValidation = ' <span class="prop_apply">'.$this->GetValidationArea($oField->GetCode()).'</span>';
|
||||
$sValidation = ' <span class="prop_apply">'.$this->GetValidationArea($oField->GetFieldId()).'</span>';
|
||||
$sField = $aRow['value'].$sValidation;
|
||||
$aDetails[] = array('label' => $aRow['label'], 'value' => $sField);
|
||||
}
|
||||
@@ -207,17 +206,14 @@ 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"> </th></tr></thead><tbody>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sFormId = $this->oParentForm->sFormId;
|
||||
}
|
||||
|
||||
$sHiddenFields = '';
|
||||
foreach($this->aFieldSets as $sLabel => $aFields)
|
||||
{
|
||||
@@ -234,7 +230,7 @@ class DesignerForm
|
||||
if ($oField->IsVisible())
|
||||
{
|
||||
$sFieldId = $this->GetFieldId($oField->GetCode());
|
||||
$sValidation = $this->GetValidationArea($oField->GetCode(), '<span title="Apply" class="ui-icon ui-icon-circle-check"/>');
|
||||
$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();
|
||||
@@ -255,9 +251,21 @@ class DesignerForm
|
||||
$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').property_field({parent_selector: $sNotifyParentSelectorJS, field_id: '$sFieldId', equals: $sHandlerEquals, get_field_value: $sHandlerGetValue, auto_apply: $sAutoApply, value: '', submit_to: '$sActionUrl', submit_parameters: $sJSSubmitParams });
|
||||
$('#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 });
|
||||
EOF
|
||||
);
|
||||
}
|
||||
@@ -358,6 +366,14 @@ $('#$sDialogId').dialog({
|
||||
{ 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(); } },
|
||||
],
|
||||
@@ -475,6 +491,15 @@ EOF
|
||||
return $this->oParentForm;
|
||||
}
|
||||
|
||||
public function GetFormId()
|
||||
{
|
||||
if ($this->oParentForm)
|
||||
{
|
||||
$this->oParentForm->GetFormId();
|
||||
}
|
||||
return $this->sFormId;
|
||||
}
|
||||
|
||||
public function SetDisplayed($bDisplayed)
|
||||
{
|
||||
$this->bDisplayed = $bDisplayed;
|
||||
@@ -517,9 +542,9 @@ EOF
|
||||
return 'attr_'.$sCode.$this->GetSuffix();
|
||||
}
|
||||
|
||||
public function GetValidationArea($sCode, $sContent = '')
|
||||
public function GetValidationArea($sId, $sContent = '')
|
||||
{
|
||||
return "<span style=\"display:inline-block;width:20px;\" id=\"v_{$this->sFormPrefix}attr_$sCode\"><span class=\"ui-icon ui-icon-alert\"></span>$sContent</span>";
|
||||
return "<span style=\"display:inline-block;width:20px;\" id=\"v_{$sId}\"><span class=\"ui-icon ui-icon-alert\"></span>$sContent</span>";
|
||||
}
|
||||
public function GetAsyncActionClass()
|
||||
{
|
||||
@@ -660,6 +685,7 @@ class DesignerFormField
|
||||
protected $bAutoApply;
|
||||
protected $aCSSClasses;
|
||||
protected $bDisplayed;
|
||||
protected $aWidgetExtraParams;
|
||||
|
||||
public function __construct($sCode, $sLabel, $defaultValue)
|
||||
{
|
||||
@@ -671,6 +697,7 @@ class DesignerFormField
|
||||
$this->bAutoApply = false;
|
||||
$this->aCSSClasses = array();
|
||||
$this->bDisplayed = true;
|
||||
$this->aWidgetExtraParams = array();
|
||||
}
|
||||
|
||||
public function GetCode()
|
||||
@@ -724,6 +751,16 @@ class DesignerFormField
|
||||
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);
|
||||
@@ -830,13 +867,11 @@ 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 = null;
|
||||
$this->sExplainForbiddenValues = null;
|
||||
$this->aForbiddenValues = array();
|
||||
}
|
||||
|
||||
public function SetValidationPattern($sValidationPattern)
|
||||
@@ -846,17 +881,17 @@ class DesignerTextField extends DesignerFormField
|
||||
|
||||
public function SetForbiddenValues($aValues, $sExplain)
|
||||
{
|
||||
$this->aForbiddenValues = $aValues;
|
||||
$aForbiddenValues = $aValues;
|
||||
|
||||
$iDefaultKey = array_search($this->defaultValue, $this->aForbiddenValues);
|
||||
$iDefaultKey = array_search($this->defaultValue, $aForbiddenValues);
|
||||
if ($iDefaultKey !== false)
|
||||
{
|
||||
// The default (current) value is always allowed...
|
||||
unset($this->aForbiddenValues[$iDefaultKey]);
|
||||
unset($aForbiddenValues[$iDefaultKey]);
|
||||
|
||||
}
|
||||
|
||||
$this->sExplainForbiddenValues = $sExplain;
|
||||
$this->aForbiddenValues[] = array('values' => $aForbiddenValues, 'message' => $sExplain);
|
||||
}
|
||||
|
||||
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
|
||||
@@ -874,17 +909,15 @@ class DesignerTextField extends DesignerFormField
|
||||
if (is_array($this->aForbiddenValues))
|
||||
{
|
||||
$sForbiddenValues = json_encode($this->aForbiddenValues);
|
||||
$sExplainForbiddenValues = addslashes($this->sExplainForbiddenValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sForbiddenValues = 'null';
|
||||
$sExplainForbiddenValues = 'null';
|
||||
$sForbiddenValues = '[]'; //Empty JS array
|
||||
}
|
||||
$sMandatory = $this->bMandatory ? 'true' : 'false';
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', '$sFormId', $sForbiddenValues, '$sExplainForbiddenValues'); } );
|
||||
$('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', $(this).closest('form').attr('id'), $sForbiddenValues); } );
|
||||
{
|
||||
var myTimer = null;
|
||||
$('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
|
||||
@@ -927,30 +960,103 @@ class DesignerLongTextField extends DesignerTextField
|
||||
if (is_array($this->aForbiddenValues))
|
||||
{
|
||||
$sForbiddenValues = json_encode($this->aForbiddenValues);
|
||||
$sExplainForbiddenValues = addslashes($this->sExplainForbiddenValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sForbiddenValues = 'null';
|
||||
$sExplainForbiddenValues = 'null';
|
||||
$sForbiddenValues = '[]'; //Empty JS array
|
||||
}
|
||||
$sMandatory = $this->bMandatory ? 'true' : 'false';
|
||||
$sReadOnly = $this->IsReadOnly() ? 'readonly' : '';
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$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
|
||||
);
|
||||
$sCSSClasses = '';
|
||||
if (count($this->aCSSClasses) > 0)
|
||||
{
|
||||
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
||||
}
|
||||
return array('label' => $this->sLabel, 'value' => "<textarea $sCSSClasses id=\"$sId\" $sReadOnly name=\"$sName\">".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."</textarea>");
|
||||
if (!$this->IsReadOnly())
|
||||
{
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', $(this).closest('form').attr('id'), $sForbiddenValues); } );
|
||||
{
|
||||
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
|
||||
{
|
||||
$sValue = "<div $sCSSClasses id=\"$sId\">".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."</div>";
|
||||
}
|
||||
return array('label' => $this->sLabel, 'value' => $sValue);
|
||||
}
|
||||
}
|
||||
|
||||
class DesignerIntegerField extends DesignerFormField
|
||||
{
|
||||
protected $iMin; // Lower boundary, inclusive
|
||||
protected $iMax; // Higher boundary, inclusive
|
||||
|
||||
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
||||
{
|
||||
parent::__construct($sCode, $sLabel, $defaultValue);
|
||||
$this->iMin = 0; // Positive integer is the default
|
||||
$this->iMax = null;
|
||||
}
|
||||
|
||||
public function SetBoundaries($iMin = null, $iMax = null)
|
||||
{
|
||||
$this->iMin = $iMin;
|
||||
$this->iMax = $iMax;
|
||||
}
|
||||
|
||||
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
|
||||
{
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
|
||||
$sName = $this->oForm->GetFieldName($this->sCode);
|
||||
if ($this->IsReadOnly())
|
||||
{
|
||||
$sHtmlValue = "<span>".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\"/></span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sMin = json_encode($this->iMin);
|
||||
$sMax = json_encode($this->iMax);
|
||||
$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); } );
|
||||
{
|
||||
var myTimer = null;
|
||||
$('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
|
||||
}
|
||||
EOF
|
||||
);
|
||||
$sCSSClasses = '';
|
||||
if (count($this->aCSSClasses) > 0)
|
||||
{
|
||||
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
||||
}
|
||||
$sHtmlValue = "<input type=\"text\" $sCSSClasses id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">";
|
||||
}
|
||||
return array('label' => $this->sLabel, 'value' => $sHtmlValue);
|
||||
}
|
||||
|
||||
public function ReadParam(&$aValues)
|
||||
{
|
||||
parent::ReadParam($aValues);
|
||||
|
||||
if (!is_null($this->iMin) && ($aValues[$this->sCode] < $this->iMin))
|
||||
{
|
||||
// Reject the value...
|
||||
$aValues[$this->sCode] = $this->defaultValue;
|
||||
}
|
||||
if (!is_null($this->iMax) && ($aValues[$this->sCode] > $this->iMax))
|
||||
{
|
||||
// Reject the value...
|
||||
$aValues[$this->sCode] = $this->defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -960,6 +1066,7 @@ class DesignerComboField extends DesignerFormField
|
||||
protected $bMultipleSelection;
|
||||
protected $bOtherChoices;
|
||||
protected $sNullLabel;
|
||||
protected $bSorted;
|
||||
|
||||
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
||||
{
|
||||
@@ -970,6 +1077,7 @@ class DesignerComboField extends DesignerFormField
|
||||
$this->sNullLabel = Dict::S('UI:SelectOne');
|
||||
|
||||
$this->bAutoApply = true;
|
||||
$this->bSorted = true; // Sorted by default
|
||||
}
|
||||
|
||||
public function SetAllowedValues($aAllowedValues)
|
||||
@@ -995,6 +1103,16 @@ class DesignerComboField extends DesignerFormField
|
||||
$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);
|
||||
@@ -1002,6 +1120,10 @@ class DesignerComboField extends DesignerFormField
|
||||
$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)
|
||||
{
|
||||
@@ -1067,7 +1189,7 @@ class DesignerComboField extends DesignerFormField
|
||||
}
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$sId').bind('change validate', function() { ValidateWithPattern('$sId', $sMandatory, '', '$sFormId', null, null); } );
|
||||
$('#$sId').bind('change validate', function() { ValidateWithPattern('$sId', $sMandatory, '', $(this).closest('form').attr('id'), null, null); } );
|
||||
EOF
|
||||
);
|
||||
}
|
||||
@@ -1205,27 +1327,37 @@ 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 = '<img src="'.$this->MakeFileUrl($this->defaultValue).'" />';
|
||||
}
|
||||
$sReadOnly = $this->IsReadOnly() ? 'disabled' : '';
|
||||
return array('label' =>$this->sLabel, 'value' => "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"{$this->defaultValue}\"/>");
|
||||
return array('label' => $this->sLabel, 'value' => $sValue);
|
||||
}
|
||||
}
|
||||
|
||||
class RunTimeIconSelectionField extends DesignerIconSelectionField
|
||||
{
|
||||
static $aAllIcons = array();
|
||||
|
||||
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
||||
{
|
||||
parent::__construct($sCode, $sLabel, $defaultValue);
|
||||
|
||||
$aAllIcons = self::FindIconsOnDisk(APPROOT.'env-'.utils::GetCurrentEnvironment());
|
||||
ksort($aAllIcons);
|
||||
if (count(self::$aAllIcons) == 0)
|
||||
{
|
||||
self::$aAllIcons = self::FindIconsOnDisk(APPROOT.'env-'.utils::GetCurrentEnvironment());
|
||||
ksort(self::$aAllIcons);
|
||||
}
|
||||
$aValues = array();
|
||||
foreach($aAllIcons as $sFilePath)
|
||||
foreach(self::$aAllIcons as $sFilePath)
|
||||
{
|
||||
$aValues[] = array('value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => utils::GetAbsoluteUrlModulesRoot().$sFilePath);
|
||||
}
|
||||
@@ -1318,11 +1450,40 @@ 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)
|
||||
@@ -1350,6 +1511,10 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
||||
}
|
||||
|
||||
if ($this->IsSorted())
|
||||
{
|
||||
uasort($this->aSubForms, array(get_class($this), 'SortOnFormLabel'));
|
||||
}
|
||||
|
||||
if ($this->IsReadOnly())
|
||||
{
|
||||
@@ -1375,9 +1540,10 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
$sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\" $sReadOnly>";
|
||||
foreach($this->aSubForms as $iKey => $aFormData)
|
||||
{
|
||||
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');;
|
||||
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');
|
||||
$sValue = htmlentities($aFormData['value'], ENT_QUOTES, 'UTF-8');
|
||||
$sSelected = ($iKey == $this->defaultValue) ? 'selected' : '';
|
||||
$sHtml .= "<option value=\"$iKey\" $sSelected>".$sDisplayValue."</option>";
|
||||
$sHtml .= "<option data-value=\"$sValue\" value=\"$iKey\" $sSelected>".$sDisplayValue."</option>";
|
||||
}
|
||||
$sHtml .= "</select>";
|
||||
}
|
||||
@@ -1443,7 +1609,7 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
if ($sRenderMode == 'property')
|
||||
{
|
||||
$sSelector = $this->oForm->GetHierarchyPath().'/'.$this->sCode.$this->oForm->GetSuffix();
|
||||
$oP->add_ready_script("InitFormSelectorField('$sId', '$sSelector');");
|
||||
$this->aWidgetExtraParams['data_selector'] = $sSelector;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Class iTopWebPage
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2013 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 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)
|
||||
public function __construct($sTitle, $bPrintable = false)
|
||||
{
|
||||
parent::__construct($sTitle);
|
||||
parent::__construct($sTitle, $bPrintable);
|
||||
$this->m_oTabs = new TabManager();
|
||||
|
||||
ApplicationContext::SetUrlMakerClass('iTopStandardURLMaker');
|
||||
@@ -73,22 +73,24 @@ 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.min.js');
|
||||
$this->add_linked_script('../js/jquery.multiselect.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))
|
||||
@@ -124,6 +126,17 @@ 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
|
||||
@@ -171,6 +184,7 @@ EOF;
|
||||
var innerWidth = $(this).innerWidth() - 10;
|
||||
$(this).find('.item').width(innerWidth);
|
||||
});
|
||||
$('.panel-resized').trigger('resized');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -226,9 +240,7 @@ EOF;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$('.multiselect').multiselect($sJSMultiselectOptions);
|
||||
|
||||
|
||||
$('.resizable').filter(':visible').resizable();
|
||||
}
|
||||
catch(err)
|
||||
@@ -297,6 +309,21 @@ 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)
|
||||
@@ -354,7 +381,10 @@ EOF
|
||||
dateFormat: 'yy-mm-dd',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
changeYear: true,
|
||||
dayNamesMin: $sJSDaysMin,
|
||||
monthNamesShort: $sJSMonthsShort,
|
||||
firstDay: $iFirstDayOfWeek
|
||||
});
|
||||
$(".datetime-pick").datepicker({
|
||||
showOn: 'button',
|
||||
@@ -363,7 +393,10 @@ EOF
|
||||
dateFormat: 'yy-mm-dd 00:00:00',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
changeYear: true,
|
||||
dayNamesMin: $sJSDaysMin,
|
||||
monthNamesShort: $sJSMonthsShort,
|
||||
firstDay: $iFirstDayOfWeek
|
||||
});
|
||||
|
||||
// Make sortable, everything that claims to be sortable
|
||||
@@ -384,6 +417,26 @@ 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();
|
||||
@@ -406,12 +459,17 @@ EOF
|
||||
window.history.back();
|
||||
}
|
||||
|
||||
function BackToDetails(sClass, id, sDefaultUrl)
|
||||
function BackToDetails(sClass, id, sDefaultUrl, sOwnershipToken)
|
||||
{
|
||||
window.bInCancel = true;
|
||||
if (id > 0)
|
||||
{
|
||||
window.location.href = AddAppContext(GetAbsoluteUrlAppRoot()+'pages/UI.php?operation=details&class='+sClass+'&id='+id);
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -419,7 +477,6 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function BackToList(sClass)
|
||||
{
|
||||
window.location.href = AddAppContext(GetAbsoluteUrlAppRoot()+'pages/UI.php?operation=search_oql&oql_class='+sClass+'&oql_clause=WHERE id=0');
|
||||
@@ -513,7 +570,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() { $(this).val(''); $('#org_id').val(''); return true; } );\n");
|
||||
$this->add_ready_script("$('#label_org_id').click( function() { if ($('#org_id').val() == '') { $(this).val(''); } } );\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
|
||||
@@ -568,8 +625,21 @@ 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
|
||||
@@ -578,6 +648,11 @@ 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')
|
||||
@@ -600,18 +675,26 @@ 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=\"{$a_stylesheet['link']}\" />\n";
|
||||
$sHtml .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"{$s_stylesheet}\" />\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\" />\n";
|
||||
$sHtml .= "<link rel=\"stylesheet\" media=\"print\" type=\"text/css\" href=\"../css/print.css?itopversion=".ITOP_VERSION."\" />\n";
|
||||
|
||||
if ($this->GetOutputFormat() == 'html')
|
||||
{
|
||||
@@ -630,7 +713,40 @@ EOF
|
||||
}
|
||||
$sHtml .= "<script type=\"text/javascript\" src=\"$s_script\"></script>\n";
|
||||
}
|
||||
$this->add_script("var iPaneVisWatchDog = window.setTimeout('FixPaneVis()',5000);\n\$(document).ready(function() {\n{$this->m_sInitScript};\nwindow.setTimeout('onDelayedReady()',10)\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
|
||||
);
|
||||
}
|
||||
if (count($this->m_aReadyScripts)>0)
|
||||
{
|
||||
$this->add_script("\nonDelayedReady = function() {\n".implode("\n", $this->m_aReadyScripts)."\n}\n");
|
||||
@@ -656,10 +772,25 @@ 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\" />\n";
|
||||
$sHtml .= "<link rel=\"shortcut icon\" href=\"".utils::GetAbsoluteUrlAppRoot()."images/favicon.ico?itopversion=".ITOP_VERSION."\" />\n";
|
||||
|
||||
$sHtml .= "</head>\n";
|
||||
$sHtml .= "<body>\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 .= ' ';
|
||||
$sHtml .= '<button onclick="window.close()">'.htmlentities(Dict::S('UI:Button:Cancel'), ENT_QUOTES, 'UTF-8').'</button>';
|
||||
$sHtml .= "</div>";
|
||||
}
|
||||
|
||||
// Render the revision number
|
||||
if (ITOP_REVISION == '$WCREV$')
|
||||
@@ -675,19 +806,19 @@ EOF
|
||||
|
||||
// Render the text of the global search form
|
||||
$sText = htmlentities(utils::ReadParam('text', '', false, 'raw_data'), ENT_QUOTES, 'UTF-8');
|
||||
$sOnClick = "";
|
||||
$sOnClick = " onclick=\"if ($('#global-search-input').val() != '') { $('#global-search form').submit(); } \"";
|
||||
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->GetOutputFormat() == 'html')
|
||||
if ($this->IsPrintableVersion())
|
||||
{
|
||||
$sHtml .= ' <!-- Beginning of page content -->';
|
||||
$sHtml .= self::FilterXSS($this->s_content);
|
||||
$sHtml .= ' <!-- End of page content -->';
|
||||
}
|
||||
elseif ($this->GetOutputFormat() == 'html')
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
|
||||
@@ -701,7 +832,7 @@ EOF
|
||||
{
|
||||
$sLogonMessage = Dict::Format('UI:LoggedAsMessage', $sUserName);
|
||||
}
|
||||
$sLogOffMenu = "<span id=\"logOffBtn\"><ul><li><img src=\"../images/onOffBtn.png\"><ul>";
|
||||
$sLogOffMenu = "<span id=\"logOffBtn\"><ul><li><img src=\"../images/on-off-menu.png\"><ul>";
|
||||
$sLogOffMenu .= "<li><span>$sLogonMessage</span></li>\n";
|
||||
$aActions = array();
|
||||
|
||||
@@ -710,7 +841,7 @@ EOF
|
||||
|
||||
if (utils::CanLogOff())
|
||||
{
|
||||
$oLogOff = new URLPopupMenuItem('UI:LogOffMenu', Dict::S('UI:LogOffMenu'), utils::GetAbsoluteUrlAppRoot().'pages/logoff.php');
|
||||
$oLogOff = new URLPopupMenuItem('UI:LogOffMenu', Dict::S('UI:LogOffMenu'), utils::GetAbsoluteUrlAppRoot().'pages/logoff.php?operation=do_logoff');
|
||||
$aActions[$oLogOff->GetUID()] = $oLogOff->GetMenuItem();
|
||||
}
|
||||
if (UserRights::CanChangePassword())
|
||||
@@ -774,10 +905,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';
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/itop-logo.png?itopversion='.ITOP_VERSION;
|
||||
if (file_exists(MODULESROOT.'branding/main-logo.png'))
|
||||
{
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/main-logo.png';
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/main-logo.png?itopversion='.ITOP_VERSION;
|
||||
}
|
||||
|
||||
$sHtml .= $sNorthPane;
|
||||
@@ -804,17 +935,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"/></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?itopversion='.ITOP_VERSION.'"/></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 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>';
|
||||
$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>';
|
||||
//echo '<td> <input type="hidden" name="operation" value="full_text"/></td></tr></table></form></div>';
|
||||
$sHtml .= ' </div>';
|
||||
$sHtml .= ' <div class="ui-layout-content" style="overflow:auto;">';
|
||||
@@ -851,28 +982,31 @@ EOF
|
||||
}
|
||||
else if ($this->GetOutputFormat() == 'pdf' && $this->IsOutputFormatAvailable('pdf') )
|
||||
{
|
||||
require_once(APPROOT.'lib/MPDF/mpdf.php');
|
||||
$oMPDF = new mPDF('c');
|
||||
$oMPDF->mirroMargins = false;
|
||||
if ($this->a_base['href'] != '')
|
||||
if (@is_readable(APPROOT.'lib/MPDF/mpdf.php'))
|
||||
{
|
||||
$oMPDF->setBasePath($this->a_base['href']); // Seems that the <BASE> tag is not recognized by mPDF...
|
||||
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->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');
|
||||
}
|
||||
MetaModel::RecordQueryTrace();
|
||||
DBSearch::RecordQueryTrace();
|
||||
ExecutionKPI::ReportStats();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
|
||||
require_once(APPROOT."/application/nicewebpage.class.inc.php");
|
||||
require_once(APPROOT.'/application/portaldispatcher.class.inc.php');
|
||||
/**
|
||||
* Web page used for displaying the login form
|
||||
*/
|
||||
@@ -91,10 +92,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;
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/'.$sLogo.'?itopversion='.ITOP_VERSION;
|
||||
if (file_exists(MODULESROOT.'branding/'.$sBrandingLogo))
|
||||
{
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/'.$sBrandingLogo;
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/'.$sBrandingLogo.'?itopversion='.ITOP_VERSION;
|
||||
}
|
||||
$this->add("<div id=\"login-logo\"><a href=\"".htmlentities($sIconUrl, ENT_QUOTES, 'UTF-8')."\"><img title=\"$sVersionShort\" src=\"$sDisplayIcon\"></a></div>\n");
|
||||
}
|
||||
@@ -428,6 +429,7 @@ 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!
|
||||
}
|
||||
@@ -579,6 +581,13 @@ 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));
|
||||
@@ -654,12 +663,22 @@ EOF
|
||||
|
||||
/**
|
||||
* Overridable: depending on the user, head toward a dedicated portal
|
||||
* @param bool $bIsAllowedToPortalUsers Whether or not the current page is considered as part of the portal
|
||||
* @param string|null $sRequestedPortalId
|
||||
* @param int $iOnExit How to complete the call: redirect or return a code
|
||||
*/
|
||||
protected static function ChangeLocation($bIsAllowedToPortalUsers, $iOnExit = self::EXIT_PROMPT)
|
||||
protected static function ChangeLocation($sRequestedPortalId = null, $iOnExit = self::EXIT_PROMPT)
|
||||
{
|
||||
if ( (!$bIsAllowedToPortalUsers) && (UserRights::IsPortalUser()))
|
||||
$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 ($iOnExit == self::EXIT_RETURN)
|
||||
{
|
||||
@@ -668,16 +687,11 @@ EOF
|
||||
else
|
||||
{
|
||||
// No rights to be here, redirect to the portal
|
||||
header('Location: '.utils::GetAbsoluteUrlAppRoot().'portal/index.php');
|
||||
header('Location: '.$ret);
|
||||
}
|
||||
}
|
||||
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
|
||||
@@ -688,9 +702,56 @@ EOF
|
||||
*/
|
||||
static function DoLogin($bMustBeAdmin = false, $bIsAllowedToPortalUsers = false, $iOnExit = self::EXIT_PROMPT)
|
||||
{
|
||||
$sMessage = ''; // In case we need to return a message to the calling web page
|
||||
$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)
|
||||
{
|
||||
$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']))
|
||||
@@ -714,7 +775,7 @@ EOF
|
||||
$oPage->DisplayLoginForm( $sLoginMode, false /* not a failed attempt */);
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
else if ($operation == 'forgot_pwd')
|
||||
{
|
||||
$oPage = self::NewLoginWebPage();
|
||||
@@ -767,36 +828,54 @@ 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
|
||||
|
||||
$iRet = self::Login($iOnExit);
|
||||
|
||||
if ($iRet == self::EXIT_CODE_OK)
|
||||
$aPortalsConf = PortalDispatcherData::GetData();
|
||||
$aDispatchers = array();
|
||||
foreach($aPortalsConf as $sPortalId => $aConf)
|
||||
{
|
||||
if ($bMustBeAdmin && !UserRights::IsAdministrator())
|
||||
$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 ($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;
|
||||
}
|
||||
$aAllowedPortals[] = array(
|
||||
'id' => $sPortalId,
|
||||
'label' => $oDispatcher->GetLabel(),
|
||||
'url' => $oDispatcher->GetUrl(),
|
||||
);
|
||||
}
|
||||
$iRet = call_user_func(array(self::$sHandlerClass, 'ChangeLocation'), $bIsAllowedToPortalUsers, $iOnExit);
|
||||
}
|
||||
if ($iOnExit == self::EXIT_RETURN)
|
||||
{
|
||||
return $iRet;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $sMessage;
|
||||
}
|
||||
}
|
||||
return $aAllowedPortals;
|
||||
}
|
||||
} // End of class
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Class NiceWebPage
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 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)
|
||||
public function __construct($s_title, $bPrintable = false)
|
||||
{
|
||||
parent::__construct($s_title);
|
||||
parent::__construct($s_title, $bPrintable);
|
||||
$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_linked_stylesheet("../css/light-grey.css");
|
||||
$this->add_saas("css/light-grey.scss");
|
||||
|
||||
$this->m_sRootUrl = $this->GetAbsoluteUrlAppRoot();
|
||||
$sAbsURLAppRoot = addslashes($this->m_sRootUrl);
|
||||
|
||||
194
application/pdfpage.class.inc.php
Normal file
194
application/pdfpage.class.inc.php
Normal file
@@ -0,0 +1,194 @@
|
||||
<?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');
|
||||
}
|
||||
}
|
||||
70
application/portaldispatcher.class.inc.php
Normal file
70
application/portaldispatcher.class.inc.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
class PortalDispatcher
|
||||
{
|
||||
protected $sPortalid;
|
||||
protected $aData;
|
||||
|
||||
public function __construct($sPortalId)
|
||||
{
|
||||
$this->sPortalid = $sPortalId;
|
||||
$this->aData = PortalDispatcherData::GetData($sPortalId);
|
||||
}
|
||||
|
||||
public function IsUserAllowed()
|
||||
{
|
||||
$bRet = true;
|
||||
if (array_key_exists('profile_list', $_SESSION))
|
||||
{
|
||||
$aProfiles = $_SESSION['profile_list'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$oUser = UserRights::GetUserObject();
|
||||
$oSet = $oUser->Get('profile_list');
|
||||
while(($oLnkUserProfile = $oSet->Fetch()) !== null)
|
||||
{
|
||||
$aProfiles[] = $oLnkUserProfile->Get('profileid_friendlyname');
|
||||
}
|
||||
$_SESSION['profile_list'] = $aProfiles;
|
||||
}
|
||||
|
||||
foreach($this->aData['deny'] as $sDeniedProfile)
|
||||
{
|
||||
// If one denied profile is present, it's enough => return false
|
||||
if (in_array($sDeniedProfile, $aProfiles))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// If there are some "allow" profiles, then by default the result is false
|
||||
// since the user must have at least one of the profiles to be allowed
|
||||
if (count($this->aData['allow']) > 0)
|
||||
{
|
||||
$bRet = false;
|
||||
}
|
||||
foreach($this->aData['allow'] as $sAllowProfile)
|
||||
{
|
||||
// If one "allow" profile is present, it's enough => return true
|
||||
if (in_array($sAllowProfile, $aProfiles))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
public function GetURL()
|
||||
{
|
||||
return utils::GetAbsoluteUrlAppRoot().$this->aData['url'];
|
||||
}
|
||||
|
||||
public function GetLabel()
|
||||
{
|
||||
return Dict::S('portal:'.$this->sPortalid);
|
||||
}
|
||||
|
||||
public function GetRank()
|
||||
{
|
||||
return $this->aData['rank'];
|
||||
}
|
||||
}
|
||||
@@ -88,8 +88,21 @@ 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.min.js');
|
||||
$this->add_linked_script('../js/jquery.multiselect.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
|
||||
@@ -131,14 +144,17 @@ try
|
||||
});
|
||||
|
||||
$(".date-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true,
|
||||
dayNamesMin: $sJSDaysMin,
|
||||
monthNamesShort: $sJSMonthsShort,
|
||||
firstDay: $iFirstDayOfWeek
|
||||
});
|
||||
|
||||
$(".datetime-pick").datepicker({
|
||||
showOn: 'button',
|
||||
@@ -147,11 +163,33 @@ try
|
||||
dateFormat: 'yy-mm-dd 00:00:00',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
changeYear: true,
|
||||
dayNamesMin: $sJSDaysMin,
|
||||
monthNamesShort: $sJSMonthsShort,
|
||||
firstDay: $iFirstDayOfWeek
|
||||
});
|
||||
|
||||
//$('.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)
|
||||
{
|
||||
@@ -222,7 +260,7 @@ EOF
|
||||
{
|
||||
var form = $('FORM');
|
||||
form.unbind('submit'); // De-activate validation
|
||||
window.location.href = '?operation=';
|
||||
window.location.href = window.location.href.replace(/[&?]operation=[^&]*/, '');
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -231,6 +269,20 @@ 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
|
||||
);
|
||||
|
||||
@@ -296,7 +348,7 @@ EOF
|
||||
$sMenu = '';
|
||||
if ($this->m_bEnableDisconnectButton)
|
||||
{
|
||||
$this->AddMenuButton('logoff', 'Portal:Disconnect', utils::GetAbsoluteUrlAppRoot().'pages/logoff.php'); // This menu is always present and is the last one
|
||||
$this->AddMenuButton('logoff', 'Portal:Disconnect', utils::GetAbsoluteUrlAppRoot().'pages/logoff.php?operation=do_logoff'); // This menu is always present and is the last one
|
||||
}
|
||||
foreach($this->m_aMenuButtons as $aMenuItem)
|
||||
{
|
||||
@@ -755,6 +807,17 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -826,7 +889,7 @@ EOF
|
||||
}
|
||||
|
||||
$sStepHistory = implode(',', $aPreviousSteps);
|
||||
$this->add("<input type=\"hidden\" id=\"step_history\" name=\"step_history\" value=\"$sStepHistory\">");
|
||||
$this->add("<input type=\"hidden\" id=\"step_history\" name=\"step_history\" value=\"".htmlentities($sStepHistory, ENT_QUOTES, 'UTF-8')."\">");
|
||||
|
||||
if (!is_null($sNextStep))
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 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-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -92,7 +92,18 @@ class QueryOQL extends Query
|
||||
|
||||
if (!$bEditMode)
|
||||
{
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?format=spreadsheet&login_mode=basic&query='.$this->GetKey();
|
||||
$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();
|
||||
}
|
||||
$sOql = $this->Get('oql');
|
||||
$sMessage = null;
|
||||
try
|
||||
|
||||
@@ -254,8 +254,8 @@ class ShortcutOQL extends Shortcut
|
||||
$oField = new DesignerBooleanField('auto_reload', Dict::S('Class:ShortcutOQL/Attribute:auto_reload'), false);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerTextField('auto_reload_sec', Dict::S('Class:ShortcutOQL/Attribute:auto_reload_sec'), MetaModel::GetConfig()->GetStandardReloadInterval());
|
||||
$oField->SetValidationPattern('^$|^0*([5-9]|[1-9][0-9]+)$'); // Can be empty, or a number > 4
|
||||
$oField = new DesignerIntegerField('auto_reload_sec', Dict::S('Class:ShortcutOQL/Attribute:auto_reload_sec'), MetaModel::GetConfig()->GetStandardReloadInterval());
|
||||
$oField->SetBoundaries(MetaModel::GetConfig()->Get('min_reload_interval'), null); // no upper limit
|
||||
$oField->SetMandatory(false);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
@@ -284,7 +284,7 @@ class ShortcutOQL extends Shortcut
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
|
||||
$sRateTitle = addslashes(Dict::S('Class:ShortcutOQL/Attribute:auto_reload_sec+'));
|
||||
$sRateTitle = addslashes(Dict::Format('Class:ShortcutOQL/Attribute:auto_reload_sec/tip', MetaModel::GetConfig()->Get('min_reload_interval')));
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,8 @@
|
||||
/**
|
||||
* 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
|
||||
* 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'
|
||||
*
|
||||
* @package iTop
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
@@ -28,6 +29,81 @@
|
||||
|
||||
|
||||
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
|
||||
@@ -99,4 +175,178 @@ class privUITransaction
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
/**
|
||||
* An alternate implementation for storing the transactions as temporary files
|
||||
* Useful when using an in-memory storage for the session which do not
|
||||
* guarantee mutual exclusion for writing
|
||||
*/
|
||||
class privUITransactionFile
|
||||
{
|
||||
/**
|
||||
* Create a new transaction id, store it in the session and return its id
|
||||
* @param void
|
||||
* @return int The identifier of the new transaction
|
||||
*/
|
||||
public static function GetNewTransactionId()
|
||||
{
|
||||
if (!is_dir(APPROOT.'data/transactions'))
|
||||
{
|
||||
if (!is_writable(APPROOT.'data'))
|
||||
{
|
||||
throw new Exception('The directory "'.APPROOT.'data" must be writable to the application.');
|
||||
}
|
||||
if (!@mkdir(APPROOT.'data/transactions'))
|
||||
{
|
||||
throw new Exception('Failed to create the directory "'.APPROOT.'data/transactions". Ajust the rights on the parent directory or let an administrator create the transactions directory and give the web sever enough rights to write into it.');
|
||||
}
|
||||
}
|
||||
if (!is_writable(APPROOT.'data/transactions'))
|
||||
{
|
||||
throw new Exception('The directory "'.APPROOT.'data/transactions" must be writable to the application.');
|
||||
}
|
||||
self::CleanupOldTransactions();
|
||||
$id = basename(tempnam(APPROOT.'data/transactions', self::GetUserPrefix()));
|
||||
self::Info('GetNewTransactionId: Created transaction: '.$id);
|
||||
|
||||
return (string)$id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a transaction is valid or not and (optionally) remove the valid transaction from
|
||||
* the session so that another call to IsTransactionValid for the same transaction id
|
||||
* will return false
|
||||
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
|
||||
* @param bool $bRemoveTransaction True if the transaction must be removed
|
||||
* @return bool True if the transaction is valid, false otherwise
|
||||
*/
|
||||
public static function IsTransactionValid($id, $bRemoveTransaction = true)
|
||||
{
|
||||
$sFilepath = APPROOT.'data/transactions/'.$id;
|
||||
clearstatcache(true, $sFilepath);
|
||||
$bResult = file_exists($sFilepath);
|
||||
if ($bResult)
|
||||
{
|
||||
if ($bRemoveTransaction)
|
||||
{
|
||||
$bResult = @unlink($sFilepath);
|
||||
if (!$bResult)
|
||||
{
|
||||
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::Info('IsTransactionValid: OK. Removed transaction: '.$id);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self::Info("IsTransactionValid: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
|
||||
}
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the transaction specified by its id
|
||||
* @param int $id The Identifier (as returned by GetNewTransactionId) of the transaction to be removed.
|
||||
* @return void
|
||||
*/
|
||||
public static function RemoveTransaction($id)
|
||||
{
|
||||
$bSuccess = true;
|
||||
$sFilepath = APPROOT.'data/transactions/'.$id;
|
||||
clearstatcache(true, $sFilepath);
|
||||
if(!file_exists($sFilepath))
|
||||
{
|
||||
$bSuccess = false;
|
||||
self::Error("RemoveTransaction: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
|
||||
}
|
||||
$bSuccess = @unlink($sFilepath);
|
||||
if (!$bSuccess)
|
||||
{
|
||||
self::Error('RemoveTransaction: FAILED to remove transaction '.$id);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::Info('RemoveTransaction: OK '.$id);
|
||||
}
|
||||
return $bSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup old transactions which have been pending since more than 24 hours
|
||||
* Use filemtime instead of filectime since filectime may be affected by operations on the directory (like changing the access rights)
|
||||
*/
|
||||
protected static function CleanupOldTransactions()
|
||||
{
|
||||
$iLimit = time() - 24*3600;
|
||||
clearstatcache();
|
||||
$aTransactions = glob(APPROOT.'data/transactions/*-*');
|
||||
foreach($aTransactions as $sFileName)
|
||||
{
|
||||
if (filemtime($sFileName) < $iLimit)
|
||||
{
|
||||
@unlink($sFileName);
|
||||
self::Info('CleanupOldTransactions: Deleted transaction: '.$sFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For debugging purposes: gets the pending transactions of the current user
|
||||
* as an array, with the date of the creation of the transaction file
|
||||
*/
|
||||
protected static function GetPendingTransactions()
|
||||
{
|
||||
clearstatcache();
|
||||
$aResult = array();
|
||||
$aTransactions = glob(APPROOT.'data/transactions/'.self::GetUserPrefix().'*');
|
||||
foreach($aTransactions as $sFileName)
|
||||
{
|
||||
$aResult[] = date('Y-m-d H:i:s', filemtime($sFileName)).' - '.basename($sFileName);
|
||||
}
|
||||
sort($aResult);
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
protected static function GetUserPrefix()
|
||||
{
|
||||
$sPrefix = substr(UserRights::GetUser(), 0, 10);
|
||||
$sPrefix = preg_replace('/[^a-zA-Z0-9-_]/', '_', $sPrefix);
|
||||
return $sPrefix.'-';
|
||||
}
|
||||
|
||||
protected static function Info($sText)
|
||||
{
|
||||
self::Write('Info | '.$sText);
|
||||
}
|
||||
|
||||
protected static function Warning($sText)
|
||||
{
|
||||
self::Write('Warning | '.$sText);
|
||||
}
|
||||
|
||||
protected static function Error($sText)
|
||||
{
|
||||
self::Write('Error | '.$sText);
|
||||
}
|
||||
|
||||
protected static function Write($sText)
|
||||
{
|
||||
$bLogEnabled = MetaModel::GetConfig()->Get('log_transactions');
|
||||
if ($bLogEnabled)
|
||||
{
|
||||
$hLogFile = @fopen(APPROOT.'log/transactions.log', 'a');
|
||||
if ($hLogFile !== false)
|
||||
{
|
||||
flock($hLogFile, LOCK_EX);
|
||||
$sDate = date('Y-m-d H:i:s');
|
||||
fwrite($hLogFile, "$sDate | $sText\n");
|
||||
fflush($hLogFile);
|
||||
flock($hLogFile, LOCK_UN);
|
||||
fclose($hLogFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +164,7 @@ class UIExtKeyWidget
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
case 'list':
|
||||
default:
|
||||
$sSelectMode = 'true';
|
||||
|
||||
@@ -252,14 +253,14 @@ EOF
|
||||
$sDisplayValue = $this->GetObjectName($value);
|
||||
}
|
||||
$iMinChars = isset($aArgs['iMinChars']) ? $aArgs['iMinChars'] : 3; //@@@ $this->oAttDef->GetMinAutoCompleteChars();
|
||||
$iFieldSize = isset($aArgs['iFieldSize']) ? $aArgs['iFieldSize'] : 30; //@@@ $this->oAttDef->GetMaxSize();
|
||||
$iFieldSize = isset($aArgs['iFieldSize']) ? $aArgs['iFieldSize'] : 20; //@@@ $this->oAttDef->GetMaxSize();
|
||||
|
||||
// the input for the auto-complete
|
||||
$sHTMLValue = "<input count=\"".$oAllowedValues->Count()."\" type=\"text\" id=\"label_$this->iId\" size=\"$iFieldSize\" value=\"$sDisplayValue\"/> ";
|
||||
$sHTMLValue .= "<img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif\" 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?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.Search();\"/>";
|
||||
|
||||
// another hidden input to store & pass the object's Id
|
||||
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"$value\" />\n";
|
||||
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" />\n";
|
||||
|
||||
$JSSearchMode = $this->bSearchMode ? 'true' : 'false';
|
||||
// Scripts to start the autocomplete and bind some events to it
|
||||
@@ -280,7 +281,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\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"/> ";
|
||||
$sHTMLValue .= "<img id=\"mini_tree_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_tree.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"/> ";
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
if ($('#ac_tree_{$this->iId}').length == 0)
|
||||
@@ -292,7 +293,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\" onClick=\"oACWidget_{$this->iId}.CreateObject();\"/> ";
|
||||
$sHTMLValue .= "<img id=\"mini_add_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_add.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.CreateObject();\"/> ";
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
if ($('#ajax_{$this->iId}').length == 0)
|
||||
@@ -302,7 +303,7 @@ EOF
|
||||
EOF
|
||||
);
|
||||
}
|
||||
if ($sDisplayStyle == 'select')
|
||||
if (($sDisplayStyle == 'select') || ($sDisplayStyle == 'list'))
|
||||
{
|
||||
$sHTMLValue .= "<span id=\"v_{$this->iId}\"></span>";
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -21,13 +21,15 @@
|
||||
* UI wdiget for displaying and editing one-way encrypted passwords
|
||||
*
|
||||
* @author Phil Eddies
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @author Romain Quetiez
|
||||
* @copyright Copyright (C) 2010-2015 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;
|
||||
@@ -36,10 +38,11 @@ class UIHTMLEditorWidget
|
||||
protected $m_sValue;
|
||||
protected $m_sMandatory;
|
||||
|
||||
public function __construct($iInputId, $sAttCode, $sNameSuffix, $sFieldPrefix, $sHelpText, $sValidationField, $sValue, $sMandatory)
|
||||
public function __construct($iInputId, $oAttDef, $sNameSuffix, $sFieldPrefix, $sHelpText, $sValidationField, $sValue, $sMandatory)
|
||||
{
|
||||
$this->m_iId = $iInputId;
|
||||
$this->m_sAttCode = $sAttCode;
|
||||
$this->m_oAttDef = $oAttDef;
|
||||
$this->m_sAttCode = $oAttDef->GetCode();
|
||||
$this->m_sNameSuffix = $sNameSuffix;
|
||||
$this->m_sHelpText = $sHelpText;
|
||||
$this->m_sValidationField = $sValidationField;
|
||||
@@ -68,8 +71,24 @@ 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()));
|
||||
$oPage->add_ready_script("$('#$iId').ckeditor(function() { /* callback code */ }, { language : '$sLanguage' , contentsLanguage : '$sLanguage', extraPlugins: 'disabler' });"); // Transform $iId into a CKEdit
|
||||
$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
|
||||
|
||||
// Please read...
|
||||
// ValidateCKEditField triggers a timer... calling itself indefinitely
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
/**
|
||||
* Class UILinksWidgetDirect
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -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 DBObjectSearch $oSearch
|
||||
* @param DBSearch $oSearch
|
||||
*/
|
||||
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Class UILinksWidget
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -163,6 +163,12 @@ 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({
|
||||
@@ -172,8 +178,11 @@ $(".date-pick").datepicker({
|
||||
dateFormat: 'yy-mm-dd',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
changeYear: true,
|
||||
dayNamesMin: $sJSDaysMin,
|
||||
monthNamesShort: $sJSMonthsShort,
|
||||
firstDay: $iFirstDayOfWeek
|
||||
});
|
||||
$(".datetime-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
@@ -181,7 +190,10 @@ $(".datetime-pick").datepicker({
|
||||
dateFormat: 'yy-mm-dd 00:00:00',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
changeYear: true,
|
||||
dayNamesMin: $sJSDaysMin,
|
||||
monthNamesShort: $sJSMonthsShort,
|
||||
firstDay: $iFirstDayOfWeek
|
||||
});
|
||||
EOF
|
||||
);
|
||||
@@ -440,7 +452,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 DBObjectSearch $oSearch
|
||||
* @param DBSearch $oSearch
|
||||
*/
|
||||
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
|
||||
{
|
||||
|
||||
@@ -275,7 +275,7 @@ EOF
|
||||
$this->DisplayFormTable($oP, $this->m_aTableConfig, $aForm);
|
||||
$oP->add("<span style=\"float:left;\"> <img src=\"../images/tv-item-last.gif\"> <input id=\"btnRemove\" type=\"button\" value=\"".Dict::S('UI:RemoveLinkedObjectsOf_Class')."\" onClick=\"RemoveSelected();\" >");
|
||||
$oP->add(" <input id=\"btnAdd\" type=\"button\" value=\"".Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sLinkedClass))."\" onClick=\"AddObjects();\"></span>\n");
|
||||
$oP->add("<span style=\"float:right;\"><input id=\"btnCancel\" type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"BackToDetails('".$sTargetClass."', ".$this->m_iObjectId.");\">");
|
||||
$oP->add("<span style=\"float:right;\"><input id=\"btnCancel\" type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"BackToDetails('".$sTargetClass."', ".$this->m_iObjectId.", '', '');\">");
|
||||
$oP->add(" <input id=\"btnOk\" type=\"submit\" value=\"".Dict::S('UI:Button:Ok')."\"></span>\n");
|
||||
$oP->add("<span style=\"clear:both;\"><p> </p></span>\n");
|
||||
$oP->add("</div>\n");
|
||||
|
||||
@@ -130,7 +130,7 @@ class appUserPreferences extends DBObject
|
||||
/**
|
||||
* Call this function if the user has changed (like when doing a logoff...)
|
||||
*/
|
||||
static public function Reset()
|
||||
static public function ResetPreferences()
|
||||
{
|
||||
self::$oUserPrefs = null;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Static class utils
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 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 DBObjectSearch The criteria defining the whole sets of objects being selected
|
||||
* @param $oFullSetFilter DBSearch 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,19 +487,23 @@ class utils
|
||||
*/
|
||||
static public function GetAbsoluteUrlAppRoot()
|
||||
{
|
||||
$sUrl = self::GetConfig()->Get('app_root_url');
|
||||
if (strpos($sUrl, SERVER_NAME_PLACEHOLDER) > -1)
|
||||
static $sUrl = null;
|
||||
if ($sUrl === null)
|
||||
{
|
||||
if (isset($_SERVER['SERVER_NAME']))
|
||||
$sUrl = self::GetConfig()->Get('app_root_url');
|
||||
if (strpos($sUrl, SERVER_NAME_PLACEHOLDER) > -1)
|
||||
{
|
||||
$sServerName = $_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);
|
||||
}
|
||||
else
|
||||
{
|
||||
// CLI mode ?
|
||||
$sServerName = php_uname('n');
|
||||
}
|
||||
$sUrl = str_replace(SERVER_NAME_PLACEHOLDER, $sServerName, $sUrl);
|
||||
}
|
||||
return $sUrl;
|
||||
}
|
||||
@@ -783,38 +787,53 @@ 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/xlsx-export.js');
|
||||
$sXlsxFilter = $param->GetFilter()->serialize();
|
||||
$sXlsxJSFilter = addslashes($sXlsxFilter);
|
||||
$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('xlsx-export', Dict::S('ExcelExporter:ExportMenu'), "XlsxExportDialog('$sXlsxJSFilter');", array()),
|
||||
new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL')"),
|
||||
new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')"),
|
||||
);
|
||||
|
||||
if (UserRights::IsActionAllowed($param->GetFilter()->GetClass(), UR_ACTION_BULK_READ, $param) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
|
||||
{
|
||||
// Bulk export actions
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), "ExportListDlg('$sOQL', '$sDataTableId', 'csv', ".json_encode(Dict::S('UI:Menu:CSVExport')).")");
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportXLSX', Dict::S('ExcelExporter:ExportMenu'), "ExportListDlg('$sOQL', '$sDataTableId', 'xlsx', ".json_encode(Dict::S('ExcelExporter:ExportMenu')).")");
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportPDF', Dict::S('UI:Menu:ExportPDF'), "ExportListDlg('$sOQL', '$sDataTableId', 'pdf', ".json_encode(Dict::S('UI:Menu:ExportPDF')).")");
|
||||
}
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL')");
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')");
|
||||
|
||||
break;
|
||||
|
||||
case iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS:
|
||||
// $param is a DBObject
|
||||
$oObj = $param;
|
||||
$oFilter = DBobjectSearch::FromOQL("SELECT ".get_class($oObj)." WHERE id=".$oObj->GetKey());
|
||||
$sOQL = "SELECT ".get_class($oObj)." WHERE id=".$oObj->GetKey();
|
||||
$oFilter = DBObjectSearch::FromOQL($sOQL);
|
||||
$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/xlsx-export.js');
|
||||
$sXlsxJSFilter = addslashes($sFilter);
|
||||
$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 URLPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".urlencode($sFilter)."&format=csv&{$sContext}"),
|
||||
new JSPopupMenuItem('xlsx-export', Dict::S('ExcelExporter:ExportMenu'), "XlsxExportDialog('$sXlsxJSFilter');", array()),
|
||||
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'),
|
||||
);
|
||||
break;
|
||||
|
||||
@@ -961,10 +980,11 @@ 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)
|
||||
static public function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null, $aCurlOptions = array())
|
||||
{
|
||||
// $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
|
||||
|
||||
@@ -982,6 +1002,7 @@ 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
|
||||
@@ -993,14 +1014,17 @@ class utils
|
||||
CURLOPT_TIMEOUT => 120, // timeout on response
|
||||
CURLOPT_MAXREDIRS => 10, // stop after 10 redirects
|
||||
CURLOPT_SSL_VERIFYPEER => false, // Disabled SSL Cert checks
|
||||
CURLOPT_SSLVERSION => 3, // MUST to prevent a strange SSL error: http://stackoverflow.com/questions/18191672/php-curl-ssl-routinesssl23-get-server-helloreason1112
|
||||
// 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_POST => count($aData),
|
||||
CURLOPT_POSTFIELDS => http_build_query($aData),
|
||||
CURLOPT_HTTPHEADER => $aHTTPHeaders,
|
||||
);
|
||||
|
||||
$aAllOptions = $aCurlOptions + $aOptions;
|
||||
$ch = curl_init($sUrl);
|
||||
curl_setopt_array($ch, $aOptions);
|
||||
curl_setopt_array($ch, $aAllOptions);
|
||||
$response = curl_exec($ch);
|
||||
$iErr = curl_errno($ch);
|
||||
$sErrMsg = curl_error( $ch );
|
||||
@@ -1073,5 +1097,28 @@ 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;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Class WebPage
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -71,8 +71,9 @@ class WebPage implements Page
|
||||
protected $bTrashUnexpectedOutput;
|
||||
protected $s_sOutputFormat;
|
||||
protected $a_OutputOptions;
|
||||
protected $bPrintable;
|
||||
|
||||
public function __construct($s_title)
|
||||
public function __construct($s_title, $bPrintable = false)
|
||||
{
|
||||
$this->s_title = $s_title;
|
||||
$this->s_content = "";
|
||||
@@ -92,6 +93,7 @@ class WebPage implements Page
|
||||
$this->bTrashUnexpectedOutput = false;
|
||||
$this->s_OutputFormat = utils::ReadParam('output_format', 'html');
|
||||
$this->a_OutputOptions = array();
|
||||
$this->bPrintable = $bPrintable;
|
||||
ob_start(); // Start capturing the output
|
||||
}
|
||||
|
||||
@@ -268,7 +270,33 @@ 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
|
||||
*/
|
||||
@@ -294,6 +322,15 @@ 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
|
||||
@@ -488,11 +525,19 @@ 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=\"{$a_stylesheet['link']}\" />\n";
|
||||
echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"{$s_stylesheet}\" />\n";
|
||||
if ($a_stylesheet['condition'] != "")
|
||||
{
|
||||
echo "<![endif]-->\n";
|
||||
@@ -510,7 +555,7 @@ class WebPage implements Page
|
||||
}
|
||||
if (class_exists('MetaModel') && MetaModel::GetConfig())
|
||||
{
|
||||
echo "<link rel=\"shortcut icon\" href=\"".utils::GetAbsoluteUrlAppRoot()."images/favicon.ico\" />\n";
|
||||
echo "<link rel=\"shortcut icon\" href=\"".utils::GetAbsoluteUrlAppRoot()."images/favicon.ico?itopversion=".ITOP_VERSION."\" />\n";
|
||||
}
|
||||
echo "</head>\n";
|
||||
echo "<body>\n";
|
||||
@@ -523,9 +568,9 @@ class WebPage implements Page
|
||||
echo "</body>\n";
|
||||
echo "</html>\n";
|
||||
|
||||
if (class_exists('MetaModel'))
|
||||
if (class_exists('DBSearch'))
|
||||
{
|
||||
MetaModel::RecordQueryTrace();
|
||||
DBSearch::RecordQueryTrace();
|
||||
}
|
||||
if (class_exists('ExecutionKPI'))
|
||||
{
|
||||
@@ -652,7 +697,16 @@ 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
|
||||
@@ -689,32 +743,34 @@ class WebPage implements Page
|
||||
{
|
||||
$sPrevUrl = '';
|
||||
$sHtml = '';
|
||||
foreach ($aActions as $aAction)
|
||||
if (!$this->IsPrintableVersion())
|
||||
{
|
||||
$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']))
|
||||
foreach ($aActions as $aAction)
|
||||
{
|
||||
if ($sPrevUrl != '') // Don't output consecutively two separators...
|
||||
$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']))
|
||||
{
|
||||
$sHtml .= "<li>{$aAction['label']}</li>";
|
||||
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'];
|
||||
}
|
||||
$sPrevUrl = '';
|
||||
}
|
||||
else
|
||||
$sHtml .= "</ul></li></ul></div>";
|
||||
foreach(array_reverse($aFavoriteActions) as $aAction)
|
||||
{
|
||||
$sHtml .= "<li><a $sTarget href=\"{$aAction['url']}\"$sClass $sOnClick>{$aAction['label']}</a></li>";
|
||||
$sPrevUrl = $aAction['url'];
|
||||
$sTarget = isset($aAction['target']) ? " target=\"{$aAction['target']}\"" : "";
|
||||
$sHtml .= "<div class=\"actions_button\"><a $sTarget href='{$aAction['url']}'>{$aAction['label']}</a></div>";
|
||||
}
|
||||
}
|
||||
$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;
|
||||
}
|
||||
|
||||
@@ -938,11 +994,11 @@ class TabManager
|
||||
|
||||
/**
|
||||
* Finds the tab whose title matches a given pattern
|
||||
* @return mixed The name of the tab as a string or false if not found
|
||||
* @return mixed The actual name of the tab (as a string) or false if not found
|
||||
*/
|
||||
public function FindTab($sPattern, $sTabContainer = null)
|
||||
{
|
||||
$return = false;
|
||||
$result = false;
|
||||
if ($sTabContainer == null)
|
||||
{
|
||||
$sTabContainer = $this->m_sCurrentTabContainer;
|
||||
@@ -988,7 +1044,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)
|
||||
public function RenderIntoContent($sContent, WebPage $oPage)
|
||||
{
|
||||
// Render the tabs in the page (if any)
|
||||
foreach($this->m_aTabs as $sTabContainerName => $aTabs)
|
||||
@@ -998,42 +1054,86 @@ class TabManager
|
||||
$container_index = 0;
|
||||
if (count($aTabs['tabs']) > 0)
|
||||
{
|
||||
$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)
|
||||
if ($oPage->IsPrintableVersion())
|
||||
{
|
||||
switch($aTabData['type'])
|
||||
$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)
|
||||
{
|
||||
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";
|
||||
$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++;
|
||||
}
|
||||
$i++;
|
||||
$sTabs .= "</div>\n<!-- end of tabs-->\n";
|
||||
}
|
||||
$sTabs .= "</ul>\n";
|
||||
// Now add the content of the tabs themselves
|
||||
$i = 0;
|
||||
foreach($aTabs['tabs'] as $sTabName => $aTabData)
|
||||
else
|
||||
{
|
||||
switch($aTabData['type'])
|
||||
$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)
|
||||
{
|
||||
case 'ajax':
|
||||
// Nothing to add
|
||||
break;
|
||||
|
||||
case 'html':
|
||||
default:
|
||||
$sTabs .= "<div id=\"tab_{$sPrefix}{$container_index}$i\">".$aTabData['html']."</div>\n";
|
||||
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++;
|
||||
}
|
||||
$i++;
|
||||
$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";
|
||||
}
|
||||
$sTabs .= "</div>\n<!-- end of tabs-->\n";
|
||||
}
|
||||
$sContent = str_replace("\$Tabs:$sTabContainerName\$", $sTabs, $sContent);
|
||||
$container_index++;
|
||||
|
||||
@@ -113,8 +113,16 @@ class WizardHelper
|
||||
{
|
||||
// For external keys: load the target object so that external fields
|
||||
// get filled too
|
||||
$oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value);
|
||||
$oObj->Set($sAttCode, $oTargetObj);
|
||||
$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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -79,7 +79,7 @@ Class XLSXWriter
|
||||
}
|
||||
|
||||
|
||||
public function writeSheet(array $data, $sheet_name='', array $header_types=array() )
|
||||
public function writeSheet(array $data, $sheet_name='', array $header_types=array(), array $header_row=array() )
|
||||
{
|
||||
$data = empty($data) ? array( array('') ) : $data;
|
||||
|
||||
@@ -95,7 +95,10 @@ Class XLSXWriter
|
||||
|
||||
$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);
|
||||
$header_row = empty($header_types) ? array() : array_keys($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; }
|
||||
@@ -217,7 +220,7 @@ Class XLSXWriter
|
||||
//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="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="164" xfId="0"/>');
|
||||
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"/>');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Class XMLPage
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -62,9 +62,9 @@ class XMLPage extends WebPage
|
||||
}
|
||||
echo $this->s_content;
|
||||
}
|
||||
if (class_exists('MetaModel'))
|
||||
if (class_exists('DBSearch'))
|
||||
{
|
||||
MetaModel::RecordQueryTrace();
|
||||
DBSearch::RecordQueryTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ class ActionEmail extends ActionNotification
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb,bizmodel",
|
||||
"category" => "core/cmdb,application",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
// Copyright (C) 2010-2015 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-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 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 CMDBSearchFilter($oExtKey->GetTargetClass());
|
||||
$oReconFilter = new DBObjectSearch($oExtKey->GetTargetClass());
|
||||
foreach ($this->m_aExtKeys[$sAttCode] as $sForeignAttCode => $iCol)
|
||||
{
|
||||
if ($sForeignAttCode == 'id')
|
||||
@@ -366,7 +366,7 @@ class BulkChange
|
||||
}
|
||||
else
|
||||
{
|
||||
$oReconFilter = new CMDBSearchFilter($oExtKey->GetTargetClass());
|
||||
$oReconFilter = new DBObjectSearch($oExtKey->GetTargetClass());
|
||||
$aCacheKeys = array();
|
||||
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
|
||||
{
|
||||
@@ -432,7 +432,7 @@ class BulkChange
|
||||
break;
|
||||
|
||||
default:
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-FoundMany', $oExtObjects->Count());
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-FoundMany', $iCount);
|
||||
$aResults[$sAttCode]= new CellStatus_Ambiguous($oTargetObj->Get($sAttCode), $iCount, $sOQL);
|
||||
}
|
||||
}
|
||||
@@ -839,7 +839,7 @@ class BulkChange
|
||||
}
|
||||
try
|
||||
{
|
||||
$oReconciliationFilter = new CMDBSearchFilter($this->m_sClass);
|
||||
$oReconciliationFilter = new DBObjectSearch($this->m_sClass);
|
||||
$bSkipQuery = false;
|
||||
foreach($this->m_aReconcilKeys as $sAttCode)
|
||||
{
|
||||
|
||||
420
core/bulkexport.class.inc.php
Normal file
420
core/bulkexport.class.inc.php
Normal file
@@ -0,0 +1,420 @@
|
||||
<?php
|
||||
// Copyright (C) 2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
define('EXPORTER_DEFAULT_CHUNK_SIZE', 1000);
|
||||
|
||||
class BulkExportException extends Exception
|
||||
{
|
||||
protected $sLocalizedMessage;
|
||||
public function __construct($message, $sLocalizedMessage, $code = null, $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->sLocalizedMessage = $sLocalizedMessage;
|
||||
}
|
||||
|
||||
public function GetLocalizedMessage()
|
||||
{
|
||||
return $this->sLocalizedMessage;
|
||||
}
|
||||
}
|
||||
class BulkExportMissingParameterException extends BulkExportException
|
||||
{
|
||||
public function __construct($sFieldCode)
|
||||
{
|
||||
parent::__construct('Missing parameter: '.$sFieldCode, Dict::Format('Core:BulkExport:MissingParameter_Param', $sFieldCode));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Class BulkExport
|
||||
*
|
||||
* @copyright Copyright (C) 2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class BulkExportResult extends DBObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => 'core/cmdb',
|
||||
"key_type" => 'autoincrement',
|
||||
"name_attcode" => array('created'),
|
||||
"state_attcode" => '',
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => 'priv_bulk_export_result',
|
||||
"db_key_field" => 'id',
|
||||
"db_finalclass_field" => '',
|
||||
"display_template" => '',
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("user_id", array("allowed_values"=>null, "sql"=>"user_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("chunk_size", array("allowed_values"=>null, "sql"=>"chunk_size", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("format", array("allowed_values"=>null, "sql"=>"format", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("temp_file_path", array("allowed_values"=>null, "sql"=>"temp_file_path", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeLongText("search", array("allowed_values"=>null, "sql"=>"search", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeLongText("status_info", array("allowed_values"=>null, "sql"=>"status_info", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
}
|
||||
|
||||
public function ComputeValues()
|
||||
{
|
||||
$this->Set('user_id', UserRights::GetUserId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Garbage collector for cleaning "old" export results from the database and the disk.
|
||||
* This background process runs once per day and deletes the results of all exports which
|
||||
* are older than one day.
|
||||
*/
|
||||
class BulkExportResultGC implements iBackgroundProcess
|
||||
{
|
||||
public function GetPeriodicity()
|
||||
{
|
||||
return 24*3600; // seconds
|
||||
}
|
||||
|
||||
public function Process($iTimeLimit)
|
||||
{
|
||||
$sDateLimit = date('Y-m-d H:i:s', time() - 24*3600); // Every BulkExportResult older than one day will be deleted
|
||||
|
||||
$sOQL = "SELECT BulkExportResult WHERE created < '$sDateLimit'";
|
||||
$iProcessed = 0;
|
||||
while (time() < $iTimeLimit)
|
||||
{
|
||||
// Next one ?
|
||||
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('created' => true) /* order by*/, array(), null, 1 /* limit count */);
|
||||
$oSet->OptimizeColumnLoad(array('temp_file_path'));
|
||||
$oResult = $oSet->Fetch();
|
||||
if (is_null($oResult))
|
||||
{
|
||||
// Nothing to be done
|
||||
break;
|
||||
}
|
||||
$iProcessed++;
|
||||
@unlink($oResult->Get('temp_file_path'));
|
||||
$oResult->DBDelete();
|
||||
}
|
||||
return "Cleaned $iProcessed old export results(s).";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class BulkExport
|
||||
*
|
||||
* @copyright Copyright (C) 2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
abstract class BulkExport
|
||||
{
|
||||
protected $oSearch;
|
||||
protected $iChunkSize;
|
||||
protected $sFormatCode;
|
||||
protected $aStatusInfo;
|
||||
protected $oBulkExportResult;
|
||||
protected $sTmpFile;
|
||||
protected $bLocalizeOutput;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->oSearch = null;
|
||||
$this->iChunkSize = 0;
|
||||
$this->sFormatCode = null;
|
||||
$this->aStatusInfo = array();
|
||||
$this->oBulkExportResult = null;
|
||||
$this->sTmpFile = '';
|
||||
$this->bLocalizeOutput = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first class capable of exporting the data in the given format
|
||||
* @param string $sFormat The lowercase format (e.g. html, csv, spreadsheet, xlsx, xml, json, pdf...)
|
||||
* @param DBSearch $oSearch The search/filter defining the set of objects to export or null when listing the supported formats
|
||||
* @return iBulkExport|NULL
|
||||
*/
|
||||
static public function FindExporter($sFormatCode, $oSearch = null)
|
||||
{
|
||||
foreach(get_declared_classes() as $sPHPClass)
|
||||
{
|
||||
$oRefClass = new ReflectionClass($sPHPClass);
|
||||
if ($oRefClass->isSubclassOf('BulkExport') && !$oRefClass->isAbstract())
|
||||
{
|
||||
$oBulkExporter = new $sPHPClass();
|
||||
if ($oBulkExporter->IsFormatSupported($sFormatCode, $oSearch))
|
||||
{
|
||||
if ($oSearch)
|
||||
{
|
||||
$oBulkExporter->SetObjectList($oSearch);
|
||||
}
|
||||
return $oBulkExporter;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the exporter corresponding to the given persistent token
|
||||
* @param int $iPersistentToken The identifier of the BulkExportResult object storing the information
|
||||
* @return iBulkExport|NULL
|
||||
*/
|
||||
static public function FindExporterFromToken($iPersistentToken = null)
|
||||
{
|
||||
$oBulkExporter = null;
|
||||
$oInfo = MetaModel::GetObject('BulkExportResult', $iPersistentToken, false);
|
||||
if ($oInfo && ($oInfo->Get('user_id') == UserRights::GetUserId()))
|
||||
{
|
||||
$sFormatCode = $oInfo->Get('format');
|
||||
$oSearch = DBObjectSearch::unserialize($oInfo->Get('search'));
|
||||
|
||||
$oBulkExporter = self::FindExporter($sFormatCode, $oSearch);
|
||||
if ($oBulkExporter)
|
||||
{
|
||||
$oBulkExporter->SetFormat($sFormatCode);
|
||||
$oBulkExporter->SetObjectList($oSearch);
|
||||
$oBulkExporter->SetChunkSize($oInfo->Get('chunk_size'));
|
||||
$oBulkExporter->SetStatusInfo(json_decode($oInfo->Get('status_info'), true));
|
||||
$oBulkExporter->sTmpFile = $oInfo->Get('temp_file_path');
|
||||
$oBulkExporter->oBulkExportResult = $oInfo;
|
||||
}
|
||||
}
|
||||
return $oBulkExporter;
|
||||
}
|
||||
|
||||
public function AppendToTmpFile($data)
|
||||
{
|
||||
if ($this->sTmpFile == '')
|
||||
{
|
||||
$this->sTmpFile = $this->MakeTmpFile($this->GetFileExtension());
|
||||
}
|
||||
$hFile = fopen($this->sTmpFile, 'ab');
|
||||
if ($hFile !== false)
|
||||
{
|
||||
fwrite($hFile, $data);
|
||||
fclose($hFile);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetTmpFilePath()
|
||||
{
|
||||
return $this->sTmpFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all possible export formats. The output is a hash array in the form: 'format_code' => 'localized format label'
|
||||
* @return multitype:string
|
||||
*/
|
||||
static public function FindSupportedFormats()
|
||||
{
|
||||
$aSupportedFormats = array();
|
||||
foreach(get_declared_classes() as $sPHPClass)
|
||||
{
|
||||
$oRefClass = new ReflectionClass($sPHPClass);
|
||||
if ($oRefClass->isSubClassOf('BulkExport') && !$oRefClass->isAbstract())
|
||||
{
|
||||
$oBulkExporter = new $sPHPClass;
|
||||
$aFormats = $oBulkExporter->GetSupportedFormats();
|
||||
$aSupportedFormats = array_merge($aSupportedFormats, $aFormats);
|
||||
}
|
||||
}
|
||||
return $aSupportedFormats;
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
* @see iBulkExport::SetChunkSize()
|
||||
*/
|
||||
public function SetChunkSize($iChunkSize)
|
||||
{
|
||||
$this->iChunkSize = $iChunkSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
* @see iBulkExport::SetObjectList()
|
||||
*/
|
||||
public function SetObjectList(DBSearch $oSearch)
|
||||
{
|
||||
$this->oSearch = $oSearch;
|
||||
}
|
||||
|
||||
public function SetFormat($sFormatCode)
|
||||
{
|
||||
$this->sFormatCode = $sFormatCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
* @see iBulkExport::IsFormatSupported()
|
||||
*/
|
||||
public function IsFormatSupported($sFormatCode, $oSearch = null)
|
||||
{
|
||||
return array_key_exists($sFormatCode, $this->GetSupportedFormats());
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
* @see iBulkExport::GetSupportedFormats()
|
||||
*/
|
||||
public function GetSupportedFormats()
|
||||
{
|
||||
return array(); // return array('csv' => Dict::S('UI:ExportFormatCSV'));
|
||||
}
|
||||
|
||||
|
||||
public function SetHttpHeaders(WebPage $oPage)
|
||||
{
|
||||
}
|
||||
|
||||
public function GetHeader()
|
||||
{
|
||||
}
|
||||
abstract public function GetNextChunk(&$aStatus);
|
||||
public function GetFooter()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function SaveState()
|
||||
{
|
||||
if ($this->oBulkExportResult === null)
|
||||
{
|
||||
$this->oBulkExportResult = new BulkExportResult();
|
||||
$this->oBulkExportResult->Set('format', $this->sFormatCode);
|
||||
$this->oBulkExportResult->Set('search', $this->oSearch->serialize());
|
||||
$this->oBulkExportResult->Set('chunk_size', $this->iChunkSize);
|
||||
$this->oBulkExportResult->Set('temp_file_path', $this->sTmpFile);
|
||||
}
|
||||
$this->oBulkExportResult->Set('status_info', json_encode($this->GetStatusInfo()));
|
||||
return $this->oBulkExportResult->DBWrite();
|
||||
}
|
||||
|
||||
public function Cleanup()
|
||||
{
|
||||
if (($this->oBulkExportResult && (!$this->oBulkExportResult->IsNew())))
|
||||
{
|
||||
$sFilename = $this->oBulkExportResult->Get('temp_file_path');
|
||||
if ($sFilename != '')
|
||||
{
|
||||
@unlink($sFilename);
|
||||
}
|
||||
$this->oBulkExportResult->DBDelete();
|
||||
}
|
||||
}
|
||||
|
||||
public function EnumFormParts()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function DisplayFormPart(WebPage $oP, $sPartId)
|
||||
{
|
||||
}
|
||||
|
||||
public function DisplayUsage(Page $oP)
|
||||
{
|
||||
|
||||
}
|
||||
public function ReadParameters()
|
||||
{
|
||||
$this->bLocalizeOutput = !((bool)utils::ReadParam('no_localize', 0, true, 'integer'));
|
||||
}
|
||||
|
||||
public function GetResultAsHtml()
|
||||
{
|
||||
|
||||
}
|
||||
public function GetRawResult()
|
||||
{
|
||||
|
||||
}
|
||||
public function GetMimeType()
|
||||
{
|
||||
|
||||
}
|
||||
public function GetFileExtension()
|
||||
{
|
||||
|
||||
}
|
||||
public function GetCharacterSet()
|
||||
{
|
||||
return 'UTF-8';
|
||||
}
|
||||
|
||||
public function GetStatistics()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function GetDownloadFileName()
|
||||
{
|
||||
return Dict::Format('Core:BulkExportOf_Class', MetaModel::GetName($this->oSearch->GetClass())).'.'.$this->GetFileExtension();
|
||||
}
|
||||
|
||||
public function SetStatusInfo($aStatusInfo)
|
||||
{
|
||||
$this->aStatusInfo = $aStatusInfo;
|
||||
}
|
||||
|
||||
public function GetStatusInfo()
|
||||
{
|
||||
return $this->aStatusInfo;
|
||||
}
|
||||
|
||||
protected function MakeTmpFile($sExtension)
|
||||
{
|
||||
if(!is_dir(APPROOT."data/bulk_export"))
|
||||
{
|
||||
@mkdir(APPROOT."data/bulk_export", 0777, true /* recursive */);
|
||||
clearstatcache();
|
||||
}
|
||||
if (!is_writable(APPROOT."data/bulk_export"))
|
||||
{
|
||||
throw new Exception('Data directory "'.APPROOT.'data/bulk_export" could not be written.');
|
||||
}
|
||||
|
||||
$iNum = rand();
|
||||
$sFileName = '';
|
||||
do
|
||||
{
|
||||
$iNum++;
|
||||
$sToken = sprintf("%08x", $iNum);
|
||||
$sFileName = APPROOT."data/bulk_export/$sToken.".$sExtension;
|
||||
$hFile = @fopen($sFileName, 'x');
|
||||
}
|
||||
while($hFile === false);
|
||||
|
||||
fclose($hFile);
|
||||
return $sFileName;
|
||||
}
|
||||
}
|
||||
|
||||
// The built-in exports
|
||||
require_once(APPROOT.'core/tabularbulkexport.class.inc.php');
|
||||
require_once(APPROOT.'core/htmlbulkexport.class.inc.php');
|
||||
require_once(APPROOT.'core/pdfbulkexport.class.inc.php');
|
||||
require_once(APPROOT.'core/csvbulkexport.class.inc.php');
|
||||
require_once(APPROOT.'core/excelbulkexport.class.inc.php');
|
||||
require_once(APPROOT.'core/spreadsheetbulkexport.class.inc.php');
|
||||
require_once(APPROOT.'core/xmlbulkexport.class.inc.php');
|
||||
|
||||
@@ -55,7 +55,7 @@ class CMDBChangeOp extends DBObject
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("date", array("allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"date")));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("userinfo", array("allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"userinfo")));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("objclass", array("allowed_values"=>null, "sql"=>"objclass", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("objkey", array("allowed_values"=>null, "sql"=>"objkey", "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_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->GetAsHTMLForHistory($sOldValue, $sNewValue);
|
||||
$sResult = $oAttDef->DescribeChangeAsHTML($sOldValue, $sNewValue);
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
@@ -619,10 +619,39 @@ class CMDBChangeOpSetAttributeCaseLog extends CMDBChangeOpSetAttribute
|
||||
// The attribute was renamed or removed from the object ?
|
||||
$sAttName = $this->Get('attcode');
|
||||
}
|
||||
$sResult = Dict::Format('Change:AttName_EntryAdded', $sAttName);
|
||||
$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);
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
|
||||
protected function ToHtml($sRawText)
|
||||
{
|
||||
return str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sRawText, ENT_QUOTES, 'UTF-8'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Class cmdbObject
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -50,6 +50,8 @@ require_once('expression.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('oql/oqlquery.class.inc.php');
|
||||
require_once('oql/oqlexception.class.inc.php');
|
||||
require_once('oql/oql-parser.php');
|
||||
@@ -57,7 +59,7 @@ require_once('oql/oql-lexer.php');
|
||||
require_once('oql/oqlinterpreter.class.inc.php');
|
||||
|
||||
require_once('dbobject.class.php');
|
||||
require_once('dbobjectsearch.class.php');
|
||||
require_once('dbsearch.class.php');
|
||||
require_once('dbobjectset.class.php');
|
||||
|
||||
require_once('backgroundprocess.inc.php');
|
||||
@@ -522,18 +524,18 @@ abstract class CMDBObject extends DBObject
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function BulkUpdate(DBObjectSearch $oFilter, array $aValues)
|
||||
public static function BulkUpdate(DBSearch $oFilter, array $aValues)
|
||||
{
|
||||
return $this->BulkUpdateTracked_Internal($oFilter, $aValues);
|
||||
}
|
||||
|
||||
public static function BulkUpdateTracked(CMDBChange $oChange, DBObjectSearch $oFilter, array $aValues)
|
||||
public static function BulkUpdateTracked(CMDBChange $oChange, DBSearch $oFilter, array $aValues)
|
||||
{
|
||||
self::SetCurrentChange($oChange);
|
||||
$this->BulkUpdateTracked_Internal($oFilter, $aValues);
|
||||
}
|
||||
|
||||
protected static function BulkUpdateTracked_Internal(DBObjectSearch $oFilter, array $aValues)
|
||||
protected static function BulkUpdateTracked_Internal(DBSearch $oFilter, array $aValues)
|
||||
{
|
||||
// $aValues is an array of $sAttCode => $value
|
||||
|
||||
@@ -580,7 +582,7 @@ class CMDBObjectSet extends DBObjectSet
|
||||
|
||||
static public function FromScratch($sClass)
|
||||
{
|
||||
$oFilter = new CMDBSearchFilter($sClass);
|
||||
$oFilter = new DBObjectSearch($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)
|
||||
@@ -604,7 +606,7 @@ class CMDBObjectSet extends DBObjectSet
|
||||
// let's create one search definition
|
||||
$sClass = reset($aClasses);
|
||||
$sAlias = key($aClasses);
|
||||
$oFilter = new CMDBSearchFilter($sClass, $sAlias);
|
||||
$oFilter = new DBObjectSearch($sClass, $sAlias);
|
||||
|
||||
$oRetSet = new CMDBObjectSet($oFilter);
|
||||
$oRetSet->m_bLoaded = true; // no DB load
|
||||
@@ -616,16 +618,3 @@ 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 (?)
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* DB Server abstraction
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 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);
|
||||
MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||
throw new 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);
|
||||
MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||
throw new 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)
|
||||
{
|
||||
MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||
}
|
||||
if ($oResult === false)
|
||||
{
|
||||
@@ -430,7 +430,7 @@ class CMDBSource
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||
}
|
||||
if ($oResult === false)
|
||||
{
|
||||
@@ -541,6 +541,37 @@ 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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -96,11 +96,19 @@ 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;
|
||||
}
|
||||
|
||||
@@ -113,8 +121,17 @@ 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";
|
||||
return abs($oEndDate->format('U') - $oStartDate->format('U'));
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ class Config
|
||||
'default' => '/usr/bin/dot',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'php_path' => array(
|
||||
'type' => 'string',
|
||||
@@ -277,6 +277,14 @@ 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\')',
|
||||
@@ -728,6 +736,15 @@ 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.',
|
||||
@@ -777,14 +794,6 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'xlsx_exporter_cleanup_old_files_delay' => array(
|
||||
'type' => 'int',
|
||||
'description' => 'Delay (in seconds) for which to let the exported XLSX files on the server so that the user who initiated the export can download the result',
|
||||
'default' => 86400,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'xlsx_exporter_memory_limit' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'Memory limit to use when (interactively) exporting data to Excel',
|
||||
@@ -792,7 +801,71 @@ class Config
|
||||
'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',
|
||||
'default' => 5, // In iTop 2.0.3, this was the hardcoded value
|
||||
'value' => '',
|
||||
'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)
|
||||
@@ -933,6 +1006,8 @@ 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',
|
||||
);
|
||||
@@ -972,6 +1047,8 @@ 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();
|
||||
|
||||
@@ -1134,9 +1211,24 @@ class Config
|
||||
{
|
||||
return $this->m_aModuleSettings[$sModule][$sProperty];
|
||||
}
|
||||
return $defaultvalue;
|
||||
// Fall back to the predefined XML parameter, if any
|
||||
return $this->GetModuleParameter($sModule, $sProperty, $defaultvalue);
|
||||
}
|
||||
|
||||
public function GetModuleParameter($sModule, $sProperty, $defaultvalue = null)
|
||||
{
|
||||
$ret = $defaultvalue;
|
||||
if (class_exists('ModulesXMLParameters'))
|
||||
{
|
||||
$aAllParams = ModulesXMLParameters::GetData($sModule);
|
||||
if(array_key_exists($sProperty, $aAllParams))
|
||||
{
|
||||
$ret = $aAllParams[$sProperty];
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function SetModuleSetting($sModule, $sProperty, $value)
|
||||
{
|
||||
$this->m_aModuleSettings[$sModule][$sProperty] = $value;
|
||||
@@ -1446,6 +1538,8 @@ 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;
|
||||
@@ -1453,6 +1547,7 @@ 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;
|
||||
|
||||
@@ -1518,6 +1613,8 @@ 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)
|
||||
@@ -1556,6 +1653,7 @@ 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,
|
||||
);
|
||||
@@ -1693,6 +1791,10 @@ 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() == '')
|
||||
@@ -1714,32 +1816,46 @@ class Config
|
||||
$this->SetDBName($sDBName);
|
||||
$this->SetDBSubname($aParamValues['db_prefix']);
|
||||
}
|
||||
|
||||
if (!is_null($sModulesDir))
|
||||
|
||||
if (isset($aParamValues['selected_modules']))
|
||||
{
|
||||
if (isset($aParamValues['selected_modules']))
|
||||
{
|
||||
$aSelectedModules = explode(',', $aParamValues['selected_modules']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aSelectedModules = null;
|
||||
}
|
||||
|
||||
$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))
|
||||
{
|
||||
// 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']))
|
||||
@@ -1752,10 +1868,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 ($bPreserveModuleSettings && isset($this->m_aModuleSettings[$sName][$sProperty]))
|
||||
if (isset($this->m_aModuleSettings[$sName][$sProperty]))
|
||||
{
|
||||
// Do nothing keep the original value
|
||||
}
|
||||
@@ -1776,7 +1892,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));
|
||||
}
|
||||
}
|
||||
@@ -1785,16 +1901,13 @@ class Config
|
||||
$this->SetAppModules($aAppModules);
|
||||
$this->SetDataModels($aDataModels);
|
||||
$this->SetWebServiceCategories($aWebServiceCategories);
|
||||
|
||||
|
||||
// Scan dictionaries
|
||||
//
|
||||
if (!is_null($sModulesDir))
|
||||
foreach (glob(APPROOT.$sModulesDir.'/dictionaries/*.dict.php') as $sFilePath)
|
||||
{
|
||||
foreach(glob(APPROOT.$sModulesDir.'/dictionaries/*.dict.php') as $sFilePath)
|
||||
{
|
||||
$sFile = basename($sFilePath);
|
||||
$aDictionaries[] = $sModulesDir.'/dictionaries/'.$sFile;
|
||||
}
|
||||
$sFile = basename($sFilePath);
|
||||
$aDictionaries[] = $sModulesDir.'/dictionaries/'.$sFile;
|
||||
}
|
||||
$this->SetDictionaries($aDictionaries);
|
||||
}
|
||||
|
||||
310
core/csvbulkexport.class.inc.php
Normal file
310
core/csvbulkexport.class.inc.php
Normal file
@@ -0,0 +1,310 @@
|
||||
<?php
|
||||
// Copyright (C) 2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* Bulk export: CSV export
|
||||
*
|
||||
* @copyright Copyright (C) 2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class CSVBulkExport extends TabularBulkExport
|
||||
{
|
||||
public function DisplayUsage(Page $oP)
|
||||
{
|
||||
$oP->p(" * csv format options:");
|
||||
$oP->p(" *\tfields: (mandatory) the comma separated list of field codes to export (e.g: name,org_id,service_name...).");
|
||||
$oP->p(" *\tseparator: (optional) character to be used as the separator (default is ',').");
|
||||
$oP->p(" *\tcharset: (optional) character set for encoding the result (default is 'UTF-8').");
|
||||
$oP->p(" *\ttext-qualifier: (optional) character to be used around text strings (default is '\"').");
|
||||
$oP->p(" *\tno_localize: set to 1 to retrieve non-localized values (for instance for ENUM values). Default is 0 (= localized values)");
|
||||
}
|
||||
|
||||
public function ReadParameters()
|
||||
{
|
||||
parent::ReadParameters();
|
||||
$this->aStatusInfo['separator'] = utils::ReadParam('separator', ',', true, 'raw_data');
|
||||
if (strtolower($this->aStatusInfo['separator']) == 'tab')
|
||||
{
|
||||
$this->aStatusInfo['separator'] = "\t";
|
||||
}
|
||||
else if (strtolower($this->aStatusInfo['separator']) == 'other')
|
||||
{
|
||||
$this->aStatusInfo['separator'] = utils::ReadParam('other-separator', ',', true, 'raw_data');
|
||||
}
|
||||
|
||||
$this->aStatusInfo['text_qualifier'] = utils::ReadParam('text-qualifier', '"', true, 'raw_data');
|
||||
if (strtolower($this->aStatusInfo['text_qualifier']) == 'other')
|
||||
{
|
||||
$this->aStatusInfo['text_qualifier'] = utils::ReadParam('other-text-qualifier', '"', true, 'raw_data');
|
||||
}
|
||||
|
||||
$this->aStatusInfo['charset'] = strtoupper(utils::ReadParam('charset', 'UTF-8', true, 'raw_data'));
|
||||
}
|
||||
|
||||
|
||||
protected function SuggestField($sClass, $sAttCode)
|
||||
{
|
||||
switch($sAttCode)
|
||||
{
|
||||
case 'id': // replace 'id' by 'friendlyname'
|
||||
$sAttCode = 'friendlyname';
|
||||
break;
|
||||
|
||||
default:
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef instanceof AttributeExternalKey)
|
||||
{
|
||||
$sAttCode .= '_friendlyname';
|
||||
}
|
||||
}
|
||||
|
||||
return parent::SuggestField($sClass, $sAttCode);
|
||||
}
|
||||
|
||||
public function EnumFormParts()
|
||||
{
|
||||
return array_merge(parent::EnumFormParts(), array('csv_options' => array('separator', 'charset', 'text-qualifier', 'no_localize') ,'interactive_fields_csv' => array('interactive_fields_csv')));
|
||||
}
|
||||
|
||||
public function DisplayFormPart(WebPage $oP, $sPartId)
|
||||
{
|
||||
switch($sPartId)
|
||||
{
|
||||
case 'interactive_fields_csv':
|
||||
$this->GetInteractiveFieldsWidget($oP, 'interactive_fields_csv');
|
||||
break;
|
||||
|
||||
case 'csv_options':
|
||||
$oP->add('<fieldset><legend>'.Dict::S('Core:BulkExport:CSVOptions').'</legend>');
|
||||
$oP->add('<table class="export_parameters"><tr><td style="vertical-align:top">');
|
||||
$oP->add('<h3>'.Dict::S('UI:CSVImport:SeparatorCharacter').'</h3>');
|
||||
$sRawSeparator = utils::ReadParam('separator', ',', true, 'raw_data');
|
||||
$aSep = array(
|
||||
';' => Dict::S('UI:CSVImport:SeparatorSemicolon+'),
|
||||
',' => Dict::S('UI:CSVImport:SeparatorComma+'),
|
||||
'tab' => Dict::S('UI:CSVImport:SeparatorTab+'),
|
||||
);
|
||||
$sOtherSeparator = '';
|
||||
if (!array_key_exists($sRawSeparator, $aSep))
|
||||
{
|
||||
$sOtherSeparator = $sRawSeparator;
|
||||
$sRawSeparator = 'other';
|
||||
}
|
||||
$aSep['other'] = Dict::S('UI:CSVImport:SeparatorOther').' <input type="text" size="3" name="other-separator" value="'.htmlentities($sOtherSeparator, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
|
||||
foreach($aSep as $sVal => $sLabel)
|
||||
{
|
||||
$sChecked = ($sVal == $sRawSeparator) ? 'checked' : '';
|
||||
$oP->add('<input type="radio" name="separator" value="'.htmlentities($sVal, ENT_QUOTES, 'UTF-8').'" '.$sChecked.'/> '.$sLabel.'<br/>');
|
||||
}
|
||||
|
||||
$oP->add('</td><td style="vertical-align:top">');
|
||||
|
||||
$oP->add('<h3>'.Dict::S('UI:CSVImport:TextQualifierCharacter').'</h3>');
|
||||
|
||||
$sRawQualifier = utils::ReadParam('text-qualifier', '"', true, 'raw_data');
|
||||
$aQualifiers = array(
|
||||
'"' => Dict::S('UI:CSVImport:QualifierDoubleQuote+'),
|
||||
'\'' => Dict::S('UI:CSVImport:QualifierSimpleQuote+'),
|
||||
);
|
||||
$sOtherQualifier = '';
|
||||
if (!array_key_exists($sRawQualifier, $aQualifiers))
|
||||
{
|
||||
$sOtherQualifier = $sRawQualifier;
|
||||
$sRawQualifier = 'other';
|
||||
}
|
||||
$aQualifiers['other'] = Dict::S('UI:CSVImport:QualifierOther').' <input type="text" size="3" name="other-text-qualifier" value="'.htmlentities($sOtherQualifier, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
|
||||
foreach($aQualifiers as $sVal => $sLabel)
|
||||
{
|
||||
$sChecked = ($sVal == $sRawQualifier) ? 'checked' : '';
|
||||
$oP->add('<input type="radio" name="text-qualifier" value="'.htmlentities($sVal, ENT_QUOTES, 'UTF-8').'" '.$sChecked.'/> '.$sLabel.'<br/>');
|
||||
}
|
||||
|
||||
$sChecked = (utils::ReadParam('no_localize', 0) == 1) ? ' checked ' : '';
|
||||
$oP->add('</td><td style="vertical-align:top">');
|
||||
$oP->add('<h3>'.Dict::S('Core:BulkExport:CSVLocalization').'</h3>');
|
||||
$oP->add('<input type="checkbox" id="csv_no_localize" name="no_localize" value="1"'.$sChecked.'><label for="csv_no_localize"> '.Dict::S('Core:BulkExport:OptionNoLocalize').'</label>');
|
||||
$oP->add('<br/>');
|
||||
$oP->add('<br/>');
|
||||
$oP->add(Dict::S('UI:CSVImport:Encoding').': <select name="charset" style="font-family:Arial,Helvetica,Sans-serif">'); // IE 8 has some troubles if the font is different
|
||||
$aPossibleEncodings = utils::GetPossibleEncodings(MetaModel::GetConfig()->GetCSVImportCharsets());
|
||||
$sDefaultEncoding = MetaModel::GetConfig()->Get('csv_file_default_charset');
|
||||
foreach($aPossibleEncodings as $sIconvCode => $sDisplayName )
|
||||
{
|
||||
$sSelected = '';
|
||||
if ($sIconvCode == $sDefaultEncoding)
|
||||
{
|
||||
$sSelected = ' selected';
|
||||
}
|
||||
$oP->add('<option value="'.$sIconvCode.'"'.$sSelected.'>'.$sDisplayName.'</option>');
|
||||
}
|
||||
$oP->add('</select>');
|
||||
|
||||
$oP->add('</td></tr></table>');
|
||||
|
||||
$oP->add('</fieldset>');
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
return parent:: DisplayFormPart($oP, $sPartId);
|
||||
}
|
||||
}
|
||||
|
||||
protected function GetSampleData($oObj, $sAttCode)
|
||||
{
|
||||
return '<div class="text-preview">'.htmlentities($this->GetValue($oObj, $sAttCode), ENT_QUOTES, 'UTF-8').'</div>';
|
||||
}
|
||||
|
||||
protected function GetValue($oObj, $sAttCode)
|
||||
{
|
||||
switch($sAttCode)
|
||||
{
|
||||
case 'id':
|
||||
$sRet = $oObj->GetKey();
|
||||
break;
|
||||
|
||||
default:
|
||||
$sRet = trim($oObj->GetAsCSV($sAttCode), '"');
|
||||
}
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public function GetHeader()
|
||||
{
|
||||
$oSet = new DBObjectSet($this->oSearch);
|
||||
$this->aStatusInfo['status'] = 'running';
|
||||
$this->aStatusInfo['position'] = 0;
|
||||
$this->aStatusInfo['total'] = $oSet->Count();
|
||||
|
||||
$aData = array();
|
||||
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
|
||||
{
|
||||
$aData[] = $aFieldSpec['sColLabel'];
|
||||
}
|
||||
$sFrom = array("\r\n", $this->aStatusInfo['text_qualifier']);
|
||||
$sTo = array("\n", $this->aStatusInfo['text_qualifier'].$this->aStatusInfo['text_qualifier']);
|
||||
foreach($aData as $idx => $sData)
|
||||
{
|
||||
// Escape and encode (if needed) the headers
|
||||
$sEscaped = str_replace($sFrom, $sTo, (string)$sData);
|
||||
$aData[$idx] = $this->aStatusInfo['text_qualifier'].$sEscaped.$this->aStatusInfo['text_qualifier'];
|
||||
if ($this->aStatusInfo['charset'] != 'UTF-8')
|
||||
{
|
||||
// Note: due to bugs in the glibc library it's safer to call iconv on the smallest possible string
|
||||
// and thus to convert field by field and not the whole row or file at once (see ticket #991)
|
||||
$aData[$idx] = iconv('UTF-8', $this->aStatusInfo['charset'].'//IGNORE//TRANSLIT', $aData[$idx]);
|
||||
}
|
||||
}
|
||||
$sData = implode($this->aStatusInfo['separator'], $aData)."\n";
|
||||
|
||||
return $sData;
|
||||
}
|
||||
|
||||
public function GetNextChunk(&$aStatus)
|
||||
{
|
||||
$sRetCode = 'run';
|
||||
$iPercentage = 0;
|
||||
|
||||
$oSet = new DBObjectSet($this->oSearch);
|
||||
$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
|
||||
$this->OptimizeColumnLoad($oSet);
|
||||
|
||||
$iCount = 0;
|
||||
$sData = '';
|
||||
$iPreviousTimeLimit = ini_get('max_execution_time');
|
||||
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
|
||||
while($aRow = $oSet->FetchAssoc())
|
||||
{
|
||||
set_time_limit($iLoopTimeLimit);
|
||||
$aData = array();
|
||||
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
|
||||
{
|
||||
$sAlias = $aFieldSpec['sAlias'];
|
||||
$sAttCode = $aFieldSpec['sAttCode'];
|
||||
|
||||
$sField = '';
|
||||
$oObj = $aRow[$sAlias];
|
||||
if ($oObj != null)
|
||||
{
|
||||
switch($sAttCode)
|
||||
{
|
||||
case 'id':
|
||||
$sField = $oObj->GetKey();
|
||||
break;
|
||||
|
||||
default:
|
||||
$sField = $oObj->GetAsCSV($sAttCode, $this->aStatusInfo['separator'], $this->aStatusInfo['text_qualifier'], $this->bLocalizeOutput);
|
||||
}
|
||||
}
|
||||
if ($this->aStatusInfo['charset'] != 'UTF-8')
|
||||
{
|
||||
// Note: due to bugs in the glibc library it's safer to call iconv on the smallest possible string
|
||||
// and thus to convert field by field and not the whole row or file at once (see ticket #991)
|
||||
$aData[] = iconv('UTF-8', $this->aStatusInfo['charset'].'//IGNORE//TRANSLIT', $sField);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aData[] = $sField;
|
||||
}
|
||||
}
|
||||
$sData .= implode($this->aStatusInfo['separator'], $aData)."\n";
|
||||
$iCount++;
|
||||
}
|
||||
set_time_limit($iPreviousTimeLimit);
|
||||
$this->aStatusInfo['position'] += $this->iChunkSize;
|
||||
if ($this->aStatusInfo['total'] == 0)
|
||||
{
|
||||
$iPercentage = 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
$iPercentage = floor(min(100.0, 100.0*$this->aStatusInfo['position']/$this->aStatusInfo['total']));
|
||||
}
|
||||
|
||||
if ($iCount < $this->iChunkSize)
|
||||
{
|
||||
$sRetCode = 'done';
|
||||
}
|
||||
|
||||
$aStatus = array('code' => $sRetCode, 'message' => Dict::S('Core:BulkExport:RetrievingData'), 'percentage' => $iPercentage);
|
||||
return $sData;
|
||||
}
|
||||
|
||||
public function GetSupportedFormats()
|
||||
{
|
||||
return array('csv' => Dict::S('Core:BulkExport:CSVFormat'));
|
||||
}
|
||||
|
||||
public function GetMimeType()
|
||||
{
|
||||
return 'text/csv';
|
||||
}
|
||||
|
||||
public function GetFileExtension()
|
||||
{
|
||||
return 'csv';
|
||||
}
|
||||
public function GetCharacterSet()
|
||||
{
|
||||
return $this->aStatusInfo['charset'];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 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-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -267,7 +267,7 @@ class cmdbDataGenerator
|
||||
function GenerateKey($sClass, $aFilterCriteria)
|
||||
{
|
||||
$retKey = null;
|
||||
$oFilter = new CMDBSearchFilter($sClass);
|
||||
$oFilter = new DBObjectSearch($sClass);
|
||||
foreach($aFilterCriteria as $sFilterCode => $filterValue)
|
||||
{
|
||||
$oFilter->AddCondition($sFilterCode, $filterValue, '=');
|
||||
@@ -346,7 +346,7 @@ class cmdbDataGenerator
|
||||
*/
|
||||
protected function OrganizationExists($sCode)
|
||||
{
|
||||
$oFilter = new CMDBSearchFilter('bizOrganization');
|
||||
$oFilter = new DBObjectSearch('Organization');
|
||||
$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 CMDBSearchFilter('bizOrganization');
|
||||
$oFilter = new DBObjectSearch('Organization');
|
||||
$oFilter->AddCondition('id', $sId, '=');
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
if ($oSet->Count() > 0)
|
||||
|
||||
3
core/datamodel.core.xml
Normal file
3
core/datamodel.core.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design>
|
||||
</itop_design>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -52,7 +52,7 @@ interface iDisplay
|
||||
/**
|
||||
* Class dbObject: the root of persistent classes
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -89,13 +89,13 @@ abstract class DBObject implements iDisplay
|
||||
protected $m_aCheckIssues = null;
|
||||
protected $m_aDeleteIssues = null;
|
||||
|
||||
protected $m_aAsArgs = null; // The current object as a standard argument (cache)
|
||||
|
||||
private $m_bFullyLoaded = false; // Compound objects can be partially loaded
|
||||
private $m_aLoadedAtt = array(); // Compound objects can be partially loaded, array of sAttCode
|
||||
protected $m_aModifiedAtt = array(); // list of (potentially) modified sAttCodes
|
||||
protected $m_aTouchedAtt = array(); // list of (potentially) modified sAttCodes
|
||||
protected $m_aModifiedAtt = array(); // real modification status: for each attCode can be: unset => don't know, true => modified, false => not modified (the same value as the original value was set)
|
||||
protected $m_aSynchroData = null; // Set of Synch data related to this object
|
||||
protected $m_sHighlightCode = null;
|
||||
protected $m_aCallbacks = array();
|
||||
|
||||
// Use the MetaModel::NewObject to build an object (do we have to force it?)
|
||||
public function __construct($aRow = null, $sClassAlias = '', $aAttToLoad = null, $aExtendedDataSpec = null)
|
||||
@@ -104,6 +104,7 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
$this->FromRow($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec);
|
||||
$this->m_bFullyLoaded = $this->IsFullyLoaded();
|
||||
$this->m_aTouchedAtt = array();
|
||||
$this->m_aModifiedAtt = array();
|
||||
return;
|
||||
}
|
||||
@@ -229,6 +230,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
$this->m_bFullyLoaded = true;
|
||||
$this->m_aTouchedAtt = array();
|
||||
$this->m_aModifiedAtt = array();
|
||||
}
|
||||
|
||||
@@ -338,7 +340,7 @@ abstract class DBObject implements iDisplay
|
||||
if ($sAttCode == 'finalclass')
|
||||
{
|
||||
// Ignore it - this attribute is set upon object creation and that's it
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
@@ -408,14 +410,28 @@ abstract class DBObject implements iDisplay
|
||||
$realvalue = $oAttDef->MakeRealValue($value, $this);
|
||||
|
||||
$this->m_aCurrValues[$sAttCode] = $realvalue;
|
||||
$this->m_aModifiedAtt[$sAttCode] = true;
|
||||
|
||||
$this->m_aTouchedAtt[$sAttCode] = true;
|
||||
unset($this->m_aModifiedAtt[$sAttCode]);
|
||||
|
||||
// The object has changed, reset caches
|
||||
$this->m_bCheckStatus = null;
|
||||
$this->m_aAsArgs = null;
|
||||
|
||||
// Make sure we do not reload it anymore... before saving it
|
||||
$this->RegisterAsDirty();
|
||||
|
||||
// This function is eligible as a lifecycle action: returning true upon success is a must
|
||||
return true;
|
||||
}
|
||||
|
||||
public function SetTrim($sAttCode, $sValue)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
$iMaxSize = $oAttDef->GetMaxSize();
|
||||
if ($iMaxSize && (strlen($sValue) > $iMaxSize))
|
||||
{
|
||||
$sValue = substr($sValue, 0, $iMaxSize);
|
||||
}
|
||||
$this->Set($sAttCode, $sValue);
|
||||
}
|
||||
|
||||
public function GetLabel($sAttCode)
|
||||
@@ -1222,7 +1238,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
}
|
||||
|
||||
final public function CheckToDelete(&$oDeletionPlan)
|
||||
public function CheckToDelete(&$oDeletionPlan)
|
||||
{
|
||||
$this->MakeDeletionPlan($oDeletionPlan);
|
||||
$oDeletionPlan->ComputeResults();
|
||||
@@ -1239,18 +1255,29 @@ abstract class DBObject implements iDisplay
|
||||
// The value was not set
|
||||
$aDelta[$sAtt] = $proposedValue;
|
||||
}
|
||||
elseif(!array_key_exists($sAtt, $this->m_aModifiedAtt))
|
||||
elseif(!array_key_exists($sAtt, $this->m_aTouchedAtt) || (array_key_exists($sAtt, $this->m_aModifiedAtt) && $this->m_aModifiedAtt[$sAtt] == false))
|
||||
{
|
||||
// This attCode was never set, canno tbe modified
|
||||
// This attCode was never set, cannot be modified
|
||||
// or the same value - as the original value - was set, and has been verified as equivalent to the original value
|
||||
continue;
|
||||
}
|
||||
else if (array_key_exists($sAtt, $this->m_aModifiedAtt) && $this->m_aModifiedAtt[$sAtt] == true)
|
||||
{
|
||||
// We already know that the value is really modified
|
||||
$aDelta[$sAtt] = $proposedValue;
|
||||
}
|
||||
elseif(is_object($proposedValue))
|
||||
{
|
||||
$oLinkAttDef = MetaModel::GetAttributeDef(get_class($this), $sAtt);
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAtt);
|
||||
// The value is an object, the comparison is not strict
|
||||
if (!$oLinkAttDef->Equals($proposedValue, $this->m_aOrigValues[$sAtt]))
|
||||
if (!$oAttDef->Equals($this->m_aOrigValues[$sAtt], $proposedValue))
|
||||
{
|
||||
$aDelta[$sAtt] = $proposedValue;
|
||||
$this->m_aModifiedAtt[$sAtt] = true; // Really modified
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->m_aModifiedAtt[$sAtt] = false; // Not really modified
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1263,6 +1290,11 @@ abstract class DBObject implements iDisplay
|
||||
//var_dump($proposedValue);
|
||||
//echo "</pre>\n";
|
||||
$aDelta[$sAtt] = $proposedValue;
|
||||
$this->m_aModifiedAtt[$sAtt] = true; // Really modified
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->m_aModifiedAtt[$sAtt] = false; // Not really modified
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1331,49 +1363,35 @@ abstract class DBObject implements iDisplay
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||
{
|
||||
if (!$oAttDef->IsLinkSet()) continue;
|
||||
|
||||
$oOriginalSet = $this->m_aOrigValues[$sAttCode];
|
||||
if ($oOriginalSet != null)
|
||||
{
|
||||
$aOriginalList = $oOriginalSet->ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
$aOriginalList = array();
|
||||
}
|
||||
|
||||
$oLinks = $this->Get($sAttCode);
|
||||
$oLinks->Rewind();
|
||||
while ($oLinkedObject = $oLinks->Fetch())
|
||||
{
|
||||
if (!array_key_exists($oLinkedObject->GetKey(), $aOriginalList))
|
||||
{
|
||||
// New object added to the set, make it point properly
|
||||
$oLinkedObject->Set($oAttDef->GetExtKeyToMe(), $this->m_iKey);
|
||||
}
|
||||
if ($oLinkedObject->IsModified())
|
||||
{
|
||||
// Objects can be modified because:
|
||||
// 1) They've just been added into the set, so their ExtKey is modified
|
||||
// 2) They are about to be removed from the set BUT NOT deleted, their ExtKey has been reset
|
||||
$oLinkedObject->DBWrite();
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the objects that were initialy present and disappeared from the list
|
||||
// (if any)
|
||||
if (count($aOriginalList) > 0)
|
||||
{
|
||||
$aNewSet = $oLinks->ToArray();
|
||||
if (!array_key_exists($sAttCode, $this->m_aTouchedAtt)) continue;
|
||||
if (array_key_exists($sAttCode, $this->m_aModifiedAtt) && ($this->m_aModifiedAtt[$sAttCode] == false)) continue;
|
||||
|
||||
foreach($aOriginalList as $iId => $oObject)
|
||||
{
|
||||
if (!array_key_exists($iId, $aNewSet))
|
||||
{
|
||||
// It disappeared from the list
|
||||
$oObject->DBDelete();
|
||||
}
|
||||
}
|
||||
// Note: any change to this algorithm must be reproduced into the implementation of AttributeLinkSet::Equals()
|
||||
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
|
||||
$sAdditionalKey = null;
|
||||
if ($oAttDef->IsIndirect() && !$oAttDef->DuplicatesAllowed())
|
||||
{
|
||||
$sAdditionalKey = $oAttDef->GetExtKeyToRemote();
|
||||
}
|
||||
$oComparator = new DBObjectSetComparator($this->m_aOrigValues[$sAttCode], $this->Get($sAttCode), array($sExtKeyToMe), $sAdditionalKey);
|
||||
$aChanges = $oComparator->GetDifferences();
|
||||
|
||||
foreach($aChanges['added'] as $oLink)
|
||||
{
|
||||
// Make sure that the objects in the set point to "this"
|
||||
$oLink->Set($oAttDef->GetExtKeyToMe(), $this->m_iKey);
|
||||
$id = $oLink->DBWrite();
|
||||
}
|
||||
|
||||
foreach($aChanges['modified'] as $oLink)
|
||||
{
|
||||
// Objects in the set either remain attached or have been detached -> leave the link as is
|
||||
$oLink->DBWrite();
|
||||
}
|
||||
|
||||
foreach($aChanges['removed'] as $oLink)
|
||||
{
|
||||
$oLink->DBDelete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1566,9 +1584,6 @@ abstract class DBObject implements iDisplay
|
||||
$this->DBWriteLinks();
|
||||
$this->m_bIsInDB = true;
|
||||
$this->m_bDirty = false;
|
||||
|
||||
// Arg cache invalidated (in particular, it needs the object key -could be improved later)
|
||||
$this->m_aAsArgs = null;
|
||||
|
||||
$this->AfterInsert();
|
||||
|
||||
@@ -1581,6 +1596,15 @@ abstract class DBObject implements iDisplay
|
||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||
}
|
||||
|
||||
// Callbacks registered with RegisterCallback
|
||||
if (isset($this->m_aCallbacks[self::CALLBACK_AFTERINSERT]))
|
||||
{
|
||||
foreach ($this->m_aCallbacks[self::CALLBACK_AFTERINSERT] as $aCallBackData)
|
||||
{
|
||||
call_user_func_array($aCallBackData['callback'], $aCallBackData['params']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->RecordObjCreation();
|
||||
|
||||
return $this->m_iKey;
|
||||
@@ -1839,7 +1863,7 @@ abstract class DBObject implements iDisplay
|
||||
$oFilter = new DBObjectSearch(get_class($this));
|
||||
$oFilter->AddCondition('id', $this->m_iKey, '=');
|
||||
|
||||
$sSQL = MetaModel::MakeUpdateQuery($oFilter, $aChanges);
|
||||
$sSQL = $oFilter->MakeUpdateQuery($aChanges);
|
||||
CMDBSource::Query($sSQL);
|
||||
}
|
||||
}
|
||||
@@ -2075,12 +2099,12 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
if (is_string($actionHandler))
|
||||
{
|
||||
// Old (pre-2.0.4) action definition without any parameter
|
||||
$aActionCallSpec = array($this, $sActionHandler);
|
||||
// Old (pre-2.1.0 modules) action definition without any parameter
|
||||
$aActionCallSpec = array($this, $actionHandler);
|
||||
|
||||
if (!is_callable($aActionCallSpec))
|
||||
{
|
||||
throw new CoreException("Unable to call action: ".get_class($this)."::$sActionHandler");
|
||||
throw new CoreException("Unable to call action: ".get_class($this)."::$actionHandler");
|
||||
return;
|
||||
}
|
||||
$bRet = call_user_func($aActionCallSpec, $sStimulusCode);
|
||||
@@ -2183,84 +2207,217 @@ abstract class DBObject implements iDisplay
|
||||
$this->Set($sAttCode, $oSW);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create query parameters (SELECT ... WHERE service = :this->service_id)
|
||||
* to be used with the APIs DBObjectSearch/DBObjectSet
|
||||
*
|
||||
* Starting 2.0.2 the parameters are computed on demand, at the lowest level,
|
||||
* in VariableExpression::Render()
|
||||
*/
|
||||
/**
|
||||
* Lifecycle action: Recover the default value (aka when an object is being created)
|
||||
*/
|
||||
public function Reset($sAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
$this->Set($sAttCode, $oAttDef->GetDefaultValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle action: Copy an attribute to another
|
||||
*/
|
||||
public function Copy($sDestAttCode, $sSourceAttCode)
|
||||
{
|
||||
$this->Set($sDestAttCode, $this->Get($sSourceAttCode));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle action: Set the current date/time for the given attribute
|
||||
*/
|
||||
public function SetCurrentDate($sAttCode)
|
||||
{
|
||||
$this->Set($sAttCode, time());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle action: Set the current logged in user for the given attribute
|
||||
*/
|
||||
public function SetCurrentUser($sAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
if ($oAttDef instanceof AttributeString)
|
||||
{
|
||||
// Note: the user friendly name is the contact friendly name if a contact is attached to the logged in user
|
||||
$this->Set($sAttCode, UserRights::GetUserFriendlyName());
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($oAttDef->IsExternalKey())
|
||||
{
|
||||
if ($oAttDef->GetTargetClass() != 'User')
|
||||
{
|
||||
throw new Exception("SetCurrentUser: the attribute $sAttCode must be an external key to 'User', found '".$oAttDef->GetTargetClass()."'");
|
||||
}
|
||||
}
|
||||
$this->Set($sAttCode, UserRights::GetUserId());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle action: Set the current logged in CONTACT for the given attribute
|
||||
*/
|
||||
public function SetCurrentPerson($sAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
if ($oAttDef instanceof AttributeString)
|
||||
{
|
||||
$iPerson = UserRights::GetContactId();
|
||||
if ($iPerson == 0)
|
||||
{
|
||||
$this->Set($sAttCode, '');
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPerson = MetaModel::GetObject('Person', $iPerson);
|
||||
$this->Set($sAttCode, $oPerson->Get('friendlyname'));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($oAttDef->IsExternalKey())
|
||||
{
|
||||
if (!MetaModel::IsParentClass($oAttDef->GetTargetClass(), 'Person'))
|
||||
{
|
||||
throw new Exception("SetCurrentContact: the attribute $sAttCode must be an external key to 'Person' or any other class above 'Person', found '".$oAttDef->GetTargetClass()."'");
|
||||
}
|
||||
}
|
||||
$this->Set($sAttCode, UserRights::GetContactId());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle action: Set the time elapsed since a reference point
|
||||
*/
|
||||
public function SetElapsedTime($sAttCode, $sRefAttCode, $sWorkingTimeComputer = null)
|
||||
{
|
||||
if (is_null($sWorkingTimeComputer))
|
||||
{
|
||||
$sWorkingTimeComputer = class_exists('SLAComputation') ? 'SLAComputation' : 'DefaultWorkingTimeComputer';
|
||||
}
|
||||
$oComputer = new $sWorkingTimeComputer();
|
||||
$aCallSpec = array($oComputer, 'GetOpenDuration');
|
||||
if (!is_callable($aCallSpec))
|
||||
{
|
||||
throw new CoreException("Unknown class/verb '$sWorkingTimeComputer/GetOpenDuration'");
|
||||
}
|
||||
|
||||
$iStartTime = AttributeDateTime::GetAsUnixSeconds($this->Get($sRefAttCode));
|
||||
$oStartDate = new DateTime('@'.$iStartTime); // setTimestamp not available in PHP 5.2
|
||||
$oEndDate = new DateTime(); // now
|
||||
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
WorkingTimeRecorder::Start($this, time(), "DBObject-SetElapsedTime-$sAttCode-$sRefAttCode", 'Core:ExplainWTC:ElapsedTime', array("Class:$sClass/Attribute:$sAttCode"));
|
||||
}
|
||||
$iElapsed = call_user_func($aCallSpec, $this, $oStartDate, $oEndDate);
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
WorkingTimeRecorder::End();
|
||||
}
|
||||
|
||||
$this->Set($sAttCode, $iElapsed);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create query parameters (SELECT ... WHERE service = :this->service_id)
|
||||
* to be used with the APIs DBObjectSearch/DBObjectSet
|
||||
*
|
||||
* Starting 2.0.2 the parameters are computed on demand, at the lowest level,
|
||||
* in VariableExpression::Render()
|
||||
*/
|
||||
public function ToArgsForQuery($sArgName = 'this')
|
||||
{
|
||||
return array($sArgName.'->object()' => $this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create template placeholders
|
||||
* An improvement could be to compute the values on demand
|
||||
* (i.e. interpret the template to determine the placeholders)
|
||||
*/
|
||||
/**
|
||||
* Create template placeholders: now equivalent to ToArgsForQuery since the actual
|
||||
* template placeholders are computed on demand.
|
||||
*/
|
||||
public function ToArgs($sArgName = 'this')
|
||||
{
|
||||
if (is_null($this->m_aAsArgs))
|
||||
return $this->ToArgsForQuery($sArgName);
|
||||
}
|
||||
|
||||
public function GetForTemplate($sPlaceholderAttCode)
|
||||
{
|
||||
$ret = null;
|
||||
if (($iPos = strpos($sPlaceholderAttCode, '->')) !== false)
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$aScalarArgs = $this->ToArgsForQuery($sArgName);
|
||||
$aScalarArgs[$sArgName] = $this->GetKey();
|
||||
$aScalarArgs[$sArgName.'->id'] = $this->GetKey();
|
||||
$aScalarArgs[$sArgName.'->hyperlink()'] = $this->GetHyperlink('iTopStandardURLMaker', false);
|
||||
$aScalarArgs[$sArgName.'->hyperlink(portal)'] = $this->GetHyperlink('PortalURLMaker', false);
|
||||
$aScalarArgs[$sArgName.'->name()'] = $this->GetName();
|
||||
|
||||
$sClass = get_class($this);
|
||||
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
$sExtKeyAttCode = substr($sPlaceholderAttCode, 0, $iPos);
|
||||
$sRemoteAttCode = substr($sPlaceholderAttCode, $iPos + 2);
|
||||
if (!MetaModel::IsValidAttCode(get_class($this), $sExtKeyAttCode))
|
||||
{
|
||||
if ($oAttDef instanceof AttributeCaseLog)
|
||||
{
|
||||
$oCaseLog = $this->Get($sAttCode);
|
||||
$aScalarArgs[$sArgName.'->'.$sAttCode] = $oCaseLog->GetText();
|
||||
$sHead = $oCaseLog->GetLatestEntry();
|
||||
$aScalarArgs[$sArgName.'->head('.$sAttCode.')'] = $sHead;
|
||||
$aScalarArgs[$sArgName.'->head_html('.$sAttCode.')'] = '<div class="caselog_entry">'.str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sHead, ENT_QUOTES, 'UTF-8')).'</div>';
|
||||
$aScalarArgs[$sArgName.'->html('.$sAttCode.')'] = $oCaseLog->GetAsEmailHtml();
|
||||
}
|
||||
elseif ($oAttDef->IsScalar())
|
||||
{
|
||||
$aScalarArgs[$sArgName.'->'.$sAttCode] = $this->Get($sAttCode);
|
||||
// #@# Note: This has been proven to be quite slow, this can slow down bulk load
|
||||
$sAsHtml = $this->GetAsHtml($sAttCode);
|
||||
$aScalarArgs[$sArgName.'->html('.$sAttCode.')'] = $sAsHtml;
|
||||
$aScalarArgs[$sArgName.'->label('.$sAttCode.')'] = $this->GetEditValue($sAttCode); // "Nice" display value, but without HTML tags and entities
|
||||
}
|
||||
elseif ($oAttDef->IsLinkSet())
|
||||
{
|
||||
$sRemoteName = $oAttDef->IsIndirect() ? $oAttDef->GetExtKeyToRemote().'_friendlyname' : 'friendlyname';
|
||||
throw new CoreException("Unknown attribute '$sExtKeyAttCode' for the class ".get_class($this));
|
||||
}
|
||||
|
||||
$oKeyAttDef = MetaModel::GetAttributeDef(get_class($this), $sExtKeyAttCode);
|
||||
if (!$oKeyAttDef instanceof AttributeExternalKey)
|
||||
{
|
||||
throw new CoreException("'$sExtKeyAttCode' is not an external key of the class ".get_class($this));
|
||||
}
|
||||
$sRemoteClass = $oKeyAttDef->GetTargetClass();
|
||||
$oRemoteObj = MetaModel::GetObject($sRemoteClass, $this->GetStrict($sExtKeyAttCode), false);
|
||||
if (is_null($oRemoteObj))
|
||||
{
|
||||
$ret = Dict::S('UI:UndefinedObject');
|
||||
}
|
||||
else
|
||||
{
|
||||
// Recurse
|
||||
$ret = $oRemoteObj->GetForTemplate($sRemoteAttCode);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch($sPlaceholderAttCode)
|
||||
{
|
||||
case 'id':
|
||||
$ret = $this->GetKey();
|
||||
break;
|
||||
|
||||
case 'hyperlink()':
|
||||
$ret = $this->GetHyperlink('iTopStandardURLMaker', false);
|
||||
break;
|
||||
|
||||
$oLinkSet = clone $this->Get($sAttCode); // Workaround/Safety net for Trac #887
|
||||
$iLimit = MetaModel::GetConfig()->Get('max_linkset_output');
|
||||
if ($iLimit > 0)
|
||||
{
|
||||
$oLinkSet->SetLimit($iLimit);
|
||||
}
|
||||
$aNames = $oLinkSet->GetColumnAsArray($sRemoteName);
|
||||
if ($iLimit > 0)
|
||||
{
|
||||
$iTotal = $oLinkSet->Count();
|
||||
if ($iTotal > count($aNames))
|
||||
{
|
||||
$aNames[] = '... '.Dict::Format('UI:TruncatedResults', count($aNames), $iTotal);
|
||||
}
|
||||
}
|
||||
$sNames = implode("\n", $aNames);
|
||||
$aScalarArgs[$sArgName.'->'.$sAttCode] = $sNames;
|
||||
$aScalarArgs[$sArgName.'->html('.$sAttCode.')'] = '<ul><li>'.implode("</li><li>", $aNames).'</li></ul>';
|
||||
case 'hyperlink(portal)':
|
||||
$ret = $this->GetHyperlink('PortalURLMaker', false);
|
||||
break;
|
||||
|
||||
case 'name()':
|
||||
$ret = $this->GetName();
|
||||
break;
|
||||
|
||||
default:
|
||||
if (preg_match('/^([^(]+)\\((.+)\\)$/', $sPlaceholderAttCode, $aMatches))
|
||||
{
|
||||
$sVerb = $aMatches[1];
|
||||
$sAttCode = $aMatches[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sVerb = '';
|
||||
$sAttCode = $sPlaceholderAttCode;
|
||||
}
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
$ret = $oAttDef->GetForTemplate($this->Get($sAttCode), $sVerb, $this);
|
||||
}
|
||||
|
||||
$this->m_aAsArgs = $aScalarArgs;
|
||||
$oKPI->ComputeStats('ToArgs', get_class($this));
|
||||
}
|
||||
return $this->m_aAsArgs;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
// To be optionaly overloaded
|
||||
@@ -2440,46 +2597,119 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
// Return an empty set for the parent of all
|
||||
// May be overloaded.
|
||||
// Anyhow, this way of implementing the relations suffers limitations (not handling the redundancy)
|
||||
// and you should consider defining those things in XML.
|
||||
public static function GetRelationQueries($sRelCode)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
// Reserved: do not overload
|
||||
public static function GetRelationQueriesEx($sRelCode)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be deprecated soon - use GetRelatedObjectsDown/Up instead to take redundancy into account
|
||||
*/
|
||||
public function GetRelatedObjects($sRelCode, $iMaxDepth = 99, &$aResults = array())
|
||||
{
|
||||
foreach (MetaModel::EnumRelationQueries(get_class($this), $sRelCode) as $sDummy => $aQueryInfo)
|
||||
// Temporary patch: until the impact analysis GUI gets rewritten,
|
||||
// let's consider that "depends on" is equivalent to "impacts/up"
|
||||
// The current patch has been implemented in DBObject and MetaModel
|
||||
$sHackedRelCode = $sRelCode;
|
||||
$bDown = true;
|
||||
if ($sRelCode == 'depends on')
|
||||
{
|
||||
MetaModel::DbgTrace("object=".$this->GetKey().", depth=$iMaxDepth, rel=".$aQueryInfo["sQuery"]);
|
||||
$sQuery = $aQueryInfo["sQuery"];
|
||||
$bPropagate = $aQueryInfo["bPropagate"];
|
||||
$iDistance = $aQueryInfo["iDistance"];
|
||||
$sHackedRelCode = 'impacts';
|
||||
$bDown = false;
|
||||
}
|
||||
foreach (MetaModel::EnumRelationQueries(get_class($this), $sHackedRelCode, $bDown) as $sDummy => $aQueryInfo)
|
||||
{
|
||||
$sQuery = $bDown ? $aQueryInfo['sQueryDown'] : $aQueryInfo['sQueryUp'];
|
||||
//$bPropagate = $aQueryInfo["bPropagate"];
|
||||
//$iDepth = $bPropagate ? $iMaxDepth - 1 : 0;
|
||||
$iDepth = $iMaxDepth - 1;
|
||||
|
||||
$iDepth = $bPropagate ? $iMaxDepth - 1 : 0;
|
||||
|
||||
$oFlt = DBObjectSearch::FromOQL($sQuery);
|
||||
$oObjSet = new DBObjectSet($oFlt, array(), $this->ToArgsForQuery());
|
||||
while ($oObj = $oObjSet->Fetch())
|
||||
// Note: the loop over the result set has been written in an unusual way for error reporting purposes
|
||||
// In the case of a wrong query parameter name, the error occurs on the first call to Fetch,
|
||||
// thus we need to have this first call into the try/catch, but
|
||||
// we do NOT want to nest the try/catch for the error message to be clear
|
||||
try
|
||||
{
|
||||
$sRootClass = MetaModel::GetRootClass(get_class($oObj));
|
||||
$sObjKey = $oObj->GetKey();
|
||||
if (array_key_exists($sRootClass, $aResults))
|
||||
$oFlt = DBObjectSearch::FromOQL($sQuery);
|
||||
$oObjSet = new DBObjectSet($oFlt, array(), $this->ToArgsForQuery());
|
||||
$oObj = $oObjSet->Fetch();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$sClassOfDefinition = $aQueryInfo['_legacy_'] ? get_class($this).'(or a parent)::GetRelationQueries()' : $aQueryInfo['sDefinedInClass'];
|
||||
throw new Exception("Wrong query for the relation $sRelCode/$sClassOfDefinition/{$aQueryInfo['sNeighbour']}: ".$e->getMessage());
|
||||
}
|
||||
if ($oObj)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (array_key_exists($sObjKey, $aResults[$sRootClass]))
|
||||
$sRootClass = MetaModel::GetRootClass(get_class($oObj));
|
||||
$sObjKey = $oObj->GetKey();
|
||||
if (array_key_exists($sRootClass, $aResults))
|
||||
{
|
||||
continue; // already visited, skip
|
||||
if (array_key_exists($sObjKey, $aResults[$sRootClass]))
|
||||
{
|
||||
continue; // already visited, skip
|
||||
}
|
||||
}
|
||||
|
||||
$aResults[$sRootClass][$sObjKey] = $oObj;
|
||||
if ($iDepth > 0)
|
||||
{
|
||||
$oObj->GetRelatedObjects($sRelCode, $iDepth, $aResults);
|
||||
}
|
||||
}
|
||||
|
||||
$aResults[$sRootClass][$sObjKey] = $oObj;
|
||||
if ($iDepth > 0)
|
||||
{
|
||||
$oObj->GetRelatedObjects($sRelCode, $iDepth, $aResults);
|
||||
}
|
||||
while ($oObj = $oObjSet->Fetch());
|
||||
}
|
||||
}
|
||||
return $aResults;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute the "RelatedObjects" (forward or "down" direction) for the object
|
||||
* for the specified relation
|
||||
*
|
||||
* @param string $sRelCode The code of the relation to use for the computation
|
||||
* @param int $iMaxDepth Maximum recursion depth
|
||||
* @param boolean $bEnableReduncancy Whether or not to take into account the redundancy
|
||||
*
|
||||
* @return RelationGraph The graph of all the related objects
|
||||
*/
|
||||
public function GetRelatedObjectsDown($sRelCode, $iMaxDepth = 99, $bEnableRedundancy = true)
|
||||
{
|
||||
$oGraph = new RelationGraph();
|
||||
$oGraph->AddSourceObject($this);
|
||||
$oGraph->ComputeRelatedObjectsDown($sRelCode, $iMaxDepth, $bEnableRedundancy);
|
||||
return $oGraph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the "RelatedObjects" (reverse or "up" direction) for the object
|
||||
* for the specified relation
|
||||
*
|
||||
* @param string $sRelCode The code of the relation to use for the computation
|
||||
* @param int $iMaxDepth Maximum recursion depth
|
||||
* @param boolean $bEnableReduncancy Whether or not to take into account the redundancy
|
||||
*
|
||||
* @return RelationGraph The graph of all the related objects
|
||||
*/
|
||||
public function GetRelatedObjectsUp($sRelCode, $iMaxDepth = 99, $bEnableRedundancy = true)
|
||||
{
|
||||
$oGraph = new RelationGraph();
|
||||
$oGraph->AddSourceObject($this);
|
||||
$oGraph->ComputeRelatedObjectsUp($sRelCode, $iMaxDepth, $bEnableRedundancy);
|
||||
return $oGraph;
|
||||
}
|
||||
|
||||
public function GetReferencingObjects($bAllowAllData = false)
|
||||
{
|
||||
$aDependentObjects = array();
|
||||
@@ -2610,7 +2840,7 @@ abstract class DBObject implements iDisplay
|
||||
*/
|
||||
public function GetSynchroData()
|
||||
{
|
||||
if ($this->m_aSynchroData == null)
|
||||
if (is_null($this->m_aSynchroData))
|
||||
{
|
||||
$sOQL = "SELECT replica,datasource FROM SynchroReplica AS replica JOIN SynchroDataSource AS datasource ON replica.sync_source_id=datasource.id WHERE replica.dest_class = :dest_class AND replica.dest_id = :dest_id";
|
||||
$oReplicaSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array() /* order by*/, array('dest_class' => get_class($this), 'dest_id' => $this->GetKey()));
|
||||
@@ -2733,6 +2963,50 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
$oPage->details($aValues);
|
||||
}
|
||||
|
||||
|
||||
|
||||
const CALLBACK_AFTERINSERT = 0;
|
||||
|
||||
/**
|
||||
* Register a call back that will be called when some internal event happens
|
||||
*
|
||||
* @param $iType string Any of the CALLBACK_x constants
|
||||
* @param $callback callable Call specification like a function name, or array('<class>', '<method>') or array($object, '<method>')
|
||||
* @param $aParameters Array Values that will be passed to the callback, after $this
|
||||
*/
|
||||
public function RegisterCallback($iType, $callback, $aParameters = array())
|
||||
{
|
||||
$sCallBackName = '';
|
||||
if (!is_callable($callback, false, $sCallBackName))
|
||||
{
|
||||
throw new Exception('Registering an unknown/protected function or wrong syntax for the call spec: '.$sCallBackName);
|
||||
}
|
||||
$this->m_aCallbacks[$iType][] = array(
|
||||
'callback' => $callback,
|
||||
'params' => $aParameters
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a text-like fingerprint identifying the content of the object
|
||||
* but excluding the specified columns
|
||||
* @param $aExcludedColumns array The list of columns to exclude
|
||||
* @return string
|
||||
*/
|
||||
public function Fingerprint($aExcludedColumns = array())
|
||||
{
|
||||
$sFingerprint = '';
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
|
||||
{
|
||||
if (!in_array($sAttCode, $aExcludedColumns))
|
||||
{
|
||||
if ($oAttDef->IsPartOfFingerprint())
|
||||
{
|
||||
$sFingerprint .= chr(0).$oAttDef->Fingerprint($this->Get($sAttCode));
|
||||
}
|
||||
}
|
||||
}
|
||||
return $sFingerprint;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Object set management
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 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 DBObjectSearch $oFilter The search filter defining the objects which are part of the set (multiple columns/objects per row are supported)
|
||||
* @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 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(DBObjectSearch $oFilter, $aOrderBy = array(), $aArgs = array(), $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0)
|
||||
public function __construct(DBSearch $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;\">".MetaModel::MakeSelectQuery($this->m_oFilter, array()).")</pre>\n";
|
||||
$sRet .= "Query: <pre style=\"font-size: smaller; display:inline;\">".$this->m_oFilter->MakeSelectQuery().")</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 CMDBSearchFilter($sClass, $sAlias);
|
||||
$oFilter = new DBObjectSearch($sClass, $sAlias);
|
||||
|
||||
$oRetSet = new self($oFilter);
|
||||
$oRetSet->m_bLoaded = true; // no DB load
|
||||
@@ -353,7 +353,7 @@ class DBObjectSet
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the DBObjectSearch corresponding to the objects present in this set
|
||||
* Retrieve the DBSearch 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 = MetaModel::MakeSelectQuery($this->m_oFilter, $this->GetRealSortOrder(), $this->m_aArgs, $this->m_aAttToLoad, $this->m_aExtendedDataSpec, $this->m_iLimitCount, $this->m_iLimitStart);
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery($this->GetRealSortOrder(), $this->m_aArgs, $this->m_aAttToLoad, $this->m_aExtendedDataSpec, $this->m_iLimitCount, $this->m_iLimitStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSQL = MetaModel::MakeSelectQuery($this->m_oFilter, $this->GetRealSortOrder(), $this->m_aArgs, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery($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 = MetaModel::MakeSelectQuery($this->m_oFilter, array(), $this->m_aArgs, null, null, 0, 0, true);
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, 0, 0, true);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
if (!$resQuery) return 0;
|
||||
|
||||
@@ -837,74 +837,16 @@ class DBObjectSet
|
||||
* Compare two sets of objects to determine if their content is identical or not.
|
||||
*
|
||||
* Limitation:
|
||||
* Works only on objects written to the DB, since we rely on their identifiers
|
||||
* Works only for sets of 1 column (i.e. one class of object selected)
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
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;
|
||||
public function HasSameContents(DBObjectSet $oObjectSet, $aExcludeColumns = array())
|
||||
{
|
||||
$oComparator = new DBObjectSetComparator($this, $oObjectSet, $aExcludeColumns);
|
||||
return $oComparator->SetsAreEquivalent();
|
||||
}
|
||||
|
||||
protected function GetObjectAt($iIndex)
|
||||
@@ -967,12 +909,7 @@ class DBObjectSet
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* Will be deprecated soon - use MetaModel::GetRelatedObjectsDown/Up instead to take redundancy into account
|
||||
*/
|
||||
public function GetRelatedObjects($sRelCode, $iMaxDepth = 99)
|
||||
{
|
||||
@@ -996,6 +933,50 @@ 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
|
||||
@@ -1156,3 +1137,217 @@ 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;
|
||||
}
|
||||
}
|
||||
751
core/dbsearch.class.php
Normal file
751
core/dbsearch.class.php
Normal file
@@ -0,0 +1,751 @@
|
||||
<?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');
|
||||
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
432
core/dbunionsearch.class.php
Normal file
432
core/dbunionsearch.class.php
Normal file
@@ -0,0 +1,432 @@
|
||||
<?php
|
||||
// Copyright (C) 2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* A union of DBObjectSearches
|
||||
*
|
||||
* @copyright Copyright (C) 2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class DBUnionSearch extends DBSearch
|
||||
{
|
||||
protected $aSearches; // source queries
|
||||
protected $aSelectedClasses; // alias => classes (lowest common ancestors) computed at construction
|
||||
|
||||
public function __construct($aSearches)
|
||||
{
|
||||
if (count ($aSearches) == 0)
|
||||
{
|
||||
throw new CoreException('A DBUnionSearch must be made of at least one search');
|
||||
}
|
||||
|
||||
$this->aSearches = array();
|
||||
foreach ($aSearches as $oSearch)
|
||||
{
|
||||
if ($oSearch instanceof DBUnionSearch)
|
||||
{
|
||||
foreach ($oSearch->aSearches as $oSubSearch)
|
||||
{
|
||||
$this->aSearches[] = $oSubSearch->DeepClone();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->aSearches[] = $oSearch->DeepClone();
|
||||
}
|
||||
}
|
||||
|
||||
// 1 - Collect all the column/classes
|
||||
$aColumnToClasses = array();
|
||||
foreach ($this->aSearches as $iPos => $oSearch)
|
||||
{
|
||||
$aSelected = array_values($oSearch->GetSelectedClasses());
|
||||
|
||||
if ($iPos != 0)
|
||||
{
|
||||
if (count($aSelected) < count($aColumnToClasses))
|
||||
{
|
||||
throw new Exception('Too few selected classes in the subquery #'.($iPos+1));
|
||||
}
|
||||
if (count($aSelected) > count($aColumnToClasses))
|
||||
{
|
||||
throw new Exception('Too many selected classes in the subquery #'.($iPos+1));
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($aSelected as $iColumn => $sClass)
|
||||
{
|
||||
$aColumnToClasses[$iColumn][] = $sClass;
|
||||
}
|
||||
}
|
||||
|
||||
// 2 - Build the index column => alias
|
||||
$oFirstSearch = $this->aSearches[0];
|
||||
$aColumnToAlias = array_keys($oFirstSearch->GetSelectedClasses());
|
||||
|
||||
// 3 - Compute alias => lowest common ancestor
|
||||
$this->aSelectedClasses = array();
|
||||
foreach ($aColumnToClasses as $iColumn => $aClasses)
|
||||
{
|
||||
$sAlias = $aColumnToAlias[$iColumn];
|
||||
$sAncestor = MetaModel::GetLowestCommonAncestor($aClasses);
|
||||
if (is_null($sAncestor))
|
||||
{
|
||||
throw new Exception('Could not find a common ancestor for the column '.($iColumn+1).' (Classes: '.implode(', ', $aClasses).')');
|
||||
}
|
||||
$this->aSelectedClasses[$sAlias] = $sAncestor;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetSearches()
|
||||
{
|
||||
return $this->aSearches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limited to the selected classes
|
||||
*/
|
||||
public function GetClassName($sAlias)
|
||||
{
|
||||
if (array_key_exists($sAlias, $this->aSelectedClasses))
|
||||
{
|
||||
return $this->aSelectedClasses[$sAlias];
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new CoreException("Invalid class alias '$sAlias'");
|
||||
}
|
||||
}
|
||||
|
||||
public function GetClass()
|
||||
{
|
||||
return reset($this->aSelectedClasses);
|
||||
}
|
||||
|
||||
public function GetClassAlias()
|
||||
{
|
||||
reset($this->aSelectedClasses);
|
||||
return key($this->aSelectedClasses);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Change the class (only subclasses are supported as of now, because the conditions must fit the new class)
|
||||
* Defaults to the first selected class
|
||||
* Only the selected classes can be changed
|
||||
*/
|
||||
public function ChangeClass($sNewClass, $sAlias = null)
|
||||
{
|
||||
if (is_null($sAlias))
|
||||
{
|
||||
$sAlias = $this->GetClassAlias();
|
||||
}
|
||||
elseif (!array_key_exists($sAlias, $this->aSelectedClasses))
|
||||
{
|
||||
// discard silently - necessary when recursing (??? copied from DBObjectSearch)
|
||||
return;
|
||||
}
|
||||
|
||||
// 1 - identify the impacted column
|
||||
$iColumn = array_search($sAlias, array_keys($this->aSelectedClasses));
|
||||
|
||||
// 2 - change for each search
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$aSearchAliases = array_keys($oSearch->GetSelectedClasses());
|
||||
$sSearchAlias = $aSearchAliases[$iColumn];
|
||||
$oSearch->ChangeClass($sNewClass, $sSearchAlias);
|
||||
}
|
||||
|
||||
// 3 - record the change
|
||||
$this->aSelectedClasses[$sAlias] = $sNewClass;
|
||||
}
|
||||
|
||||
public function GetSelectedClasses()
|
||||
{
|
||||
return $this->aSelectedClasses;
|
||||
}
|
||||
|
||||
public function IsAny()
|
||||
{
|
||||
$bIsAny = true;
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
if (!$oSearch->IsAny())
|
||||
{
|
||||
$bIsAny = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $bIsAny;
|
||||
}
|
||||
|
||||
public function ResetCondition()
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->ResetCondition();
|
||||
}
|
||||
}
|
||||
|
||||
public function MergeConditionExpression($oExpression)
|
||||
{
|
||||
$aAliases = array_keys($this->aSelectedClasses);
|
||||
foreach ($this->aSearches as $iSearchIndex => $oSearch)
|
||||
{
|
||||
$oClonedExpression = $oExpression->DeepClone();
|
||||
if ($iSearchIndex != 0)
|
||||
{
|
||||
foreach (array_keys($oSearch->GetSelectedClasses()) as $iColumn => $sSearchAlias)
|
||||
{
|
||||
$oClonedExpression->RenameAlias($aAliases[$iColumn], $sSearchAlias);
|
||||
}
|
||||
}
|
||||
$oSearch->MergeConditionExpression($oClonedExpression);
|
||||
}
|
||||
}
|
||||
|
||||
public function AddConditionExpression($oExpression)
|
||||
{
|
||||
$aAliases = array_keys($this->aSelectedClasses);
|
||||
foreach ($this->aSearches as $iSearchIndex => $oSearch)
|
||||
{
|
||||
$oClonedExpression = $oExpression->DeepClone();
|
||||
if ($iSearchIndex != 0)
|
||||
{
|
||||
foreach (array_keys($oSearch->GetSelectedClasses()) as $iColumn => $sSearchAlias)
|
||||
{
|
||||
$oClonedExpression->RenameAlias($aAliases[$iColumn], $sSearchAlias);
|
||||
}
|
||||
}
|
||||
$oSearch->AddConditionExpression($oClonedExpression);
|
||||
}
|
||||
}
|
||||
|
||||
public function AddNameCondition($sName)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->AddNameCondition($sName);
|
||||
}
|
||||
}
|
||||
|
||||
public function AddCondition($sFilterCode, $value, $sOpCode = null)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->AddCondition($sFilterCode, $value, $sOpCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a condition on external keys or link sets
|
||||
* @param sAttSpec Can be either an attribute code or extkey->[sAttSpec] or linkset->[sAttSpec] and so on, recursively
|
||||
* Example: infra_list->ci_id->location_id->country
|
||||
* @param value The value to match (can be an array => IN(val1, val2...)
|
||||
* @return void
|
||||
*/
|
||||
public function AddConditionAdvanced($sAttSpec, $value)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->AddConditionAdvanced($sAttSpec, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function AddCondition_FullText($sFullText)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->AddCondition_FullText($sFullText);
|
||||
}
|
||||
}
|
||||
|
||||
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode);
|
||||
}
|
||||
}
|
||||
|
||||
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->AddCondition_ReferencedBy($oFilter, $sForeignExtKeyAttCode);
|
||||
}
|
||||
}
|
||||
|
||||
public function Intersect(DBSearch $oFilter)
|
||||
{
|
||||
$aSearches = array();
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$aSearches[] = $oSearch->Intersect($oFilter);
|
||||
}
|
||||
return new DBUnionSearch($aSearches);
|
||||
}
|
||||
|
||||
public function SetInternalParams($aParams)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->SetInternalParams($aParams);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetInternalParams()
|
||||
{
|
||||
$aParams = array();
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$aParams = array_merge($oSearch->GetInternalParams(), $aParams);
|
||||
}
|
||||
return $aParams;
|
||||
}
|
||||
|
||||
public function GetQueryParams()
|
||||
{
|
||||
$aParams = array();
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$aParams = array_merge($oSearch->GetQueryParams(), $aParams);
|
||||
}
|
||||
return $aParams;
|
||||
}
|
||||
|
||||
public function ListConstantFields()
|
||||
{
|
||||
// Somewhat complex to implement for unions, for a poor benefit
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the parameters (:xxx) into scalar values in order to easily
|
||||
* serialize a search
|
||||
*/
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->ApplyParameters($aArgs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overloads for query building
|
||||
*/
|
||||
public function ToOQL($bDevelopParams = false, $aContextParams = null)
|
||||
{
|
||||
$aSubQueries = array();
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$aSubQueries[] = $oSearch->ToOQL($bDevelopParams, $aContextParams);
|
||||
}
|
||||
$sRet = implode(' UNION ', $aSubQueries);
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Construction of the SQL queries
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public function MakeDeleteQuery($aArgs = array())
|
||||
{
|
||||
throw new Exception('MakeDeleteQuery is not implemented for the unions!');
|
||||
}
|
||||
|
||||
public function MakeUpdateQuery($aValues, $aArgs = array())
|
||||
{
|
||||
throw new Exception('MakeUpdateQuery is not implemented for the unions!');
|
||||
}
|
||||
|
||||
protected function MakeSQLQuery($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null)
|
||||
{
|
||||
if (count($this->aSearches) == 1)
|
||||
{
|
||||
return $this->aSearches[0]->MakeSQLQuery($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr);
|
||||
}
|
||||
|
||||
$aSQLQueries = array();
|
||||
$aAliases = array_keys($this->aSelectedClasses);
|
||||
foreach ($this->aSearches as $iSearch => $oSearch)
|
||||
{
|
||||
$aSearchAliases = array_keys($oSearch->GetSelectedClasses());
|
||||
|
||||
// The selected classes from the query build perspective are the lowest common ancestors amongst the various queries
|
||||
// (used when it comes to determine which attributes must be selected)
|
||||
$aSearchSelectedClasses = array();
|
||||
foreach ($aSearchAliases as $iColumn => $sSearchAlias)
|
||||
{
|
||||
$sAlias = $aAliases[$iColumn];
|
||||
$aSearchSelectedClasses[$sSearchAlias] = $this->aSelectedClasses[$sAlias];
|
||||
}
|
||||
|
||||
if (is_null($aAttToLoad))
|
||||
{
|
||||
$aQueryAttToLoad = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// (Eventually) Transform the aliases
|
||||
$aQueryAttToLoad = array();
|
||||
foreach ($aAttToLoad as $sAlias => $aAttributes)
|
||||
{
|
||||
$iColumn = array_search($sAlias, $aAliases);
|
||||
$sQueryAlias = ($iColumn === false) ? $sAlias : $aSearchAliases[$iColumn];
|
||||
$aQueryAttToLoad[$sQueryAlias] = $aAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($aGroupByExpr))
|
||||
{
|
||||
$aQueryGroupByExpr = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clone (and eventually transform) the group by expressions
|
||||
$aQueryGroupByExpr = array();
|
||||
$aTranslationData = array();
|
||||
$aQueryColumns = array_keys($oSearch->GetSelectedClasses());
|
||||
foreach ($aAliases as $iColumn => $sAlias)
|
||||
{
|
||||
$sQueryAlias = $aQueryColumns[$iColumn];
|
||||
$aTranslationData[$sAlias]['*'] = $sQueryAlias;
|
||||
$aQueryGroupByExpr[$sAlias.'id'] = new FieldExpression('id', $sQueryAlias);
|
||||
}
|
||||
foreach ($aGroupByExpr as $sExpressionAlias => $oExpression)
|
||||
{
|
||||
$aQueryGroupByExpr[$sExpressionAlias] = $oExpression->Translate($aTranslationData, false, false);
|
||||
}
|
||||
}
|
||||
$oSubQuery = $oSearch->MakeSQLQuery($aQueryAttToLoad, false, $aModifierProperties, $aQueryGroupByExpr, $aSearchSelectedClasses);
|
||||
$aSQLQueries[] = $oSubQuery;
|
||||
}
|
||||
|
||||
$oSQLQuery = new SQLUnionQuery($aSQLQueries, $aGroupByExpr);
|
||||
//MyHelpers::var_dump_html($oSQLQuery, true);
|
||||
//MyHelpers::var_dump_html($oSQLQuery->RenderSelect(), true);
|
||||
if (self::$m_bDebugQuery) $oSQLQuery->DisplayHtml();
|
||||
return $oSQLQuery;
|
||||
}
|
||||
}
|
||||
@@ -101,6 +101,7 @@ 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;
|
||||
}
|
||||
|
||||
1471
core/displayablegraph.class.inc.php
Normal file
1471
core/displayablegraph.class.inc.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -332,6 +332,42 @@ 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()
|
||||
|
||||
274
core/excelbulkexport.class.inc.php
Normal file
274
core/excelbulkexport.class.inc.php
Normal file
@@ -0,0 +1,274 @@
|
||||
<?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'));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* General definition of an expression tree (could be OQL, SQL or whatever)
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -30,6 +30,14 @@ class MissingQueryArgument extends CoreException
|
||||
|
||||
abstract class Expression
|
||||
{
|
||||
/**
|
||||
* Perform a deep clone (as opposed to "clone" which does copy a reference to the underlying objects)
|
||||
**/
|
||||
public function DeepClone()
|
||||
{
|
||||
return unserialize(serialize($this));
|
||||
}
|
||||
|
||||
// recursive translation of identifiers
|
||||
abstract public function GetUnresolvedFields($sAlias, &$aUnresolved);
|
||||
abstract public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true);
|
||||
@@ -95,11 +103,12 @@ abstract class Expression
|
||||
}
|
||||
|
||||
abstract public function RenameParam($sOldName, $sNewName);
|
||||
abstract public function RenameAlias($sOldName, $sNewName);
|
||||
|
||||
/**
|
||||
* Make the most relevant label, given the value of the expression
|
||||
*
|
||||
* @param DBObjectSearch oFilter The context in which this expression has been used
|
||||
* @param DBSearch oFilter The context in which this expression has been used
|
||||
* @param string sValue The value returned by the query, for this expression
|
||||
* @param string sDefault The default value if no relevant label could be computed
|
||||
* @return The label
|
||||
@@ -161,6 +170,11 @@ class SQLExpression extends Expression
|
||||
{
|
||||
// Do nothing, since there is nothing to rename
|
||||
}
|
||||
|
||||
public function RenameAlias($sOldName, $sNewName)
|
||||
{
|
||||
// Do nothing, since there is nothing to rename
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -313,6 +327,12 @@ class BinaryExpression extends Expression
|
||||
$this->GetLeftExpr()->RenameParam($sOldName, $sNewName);
|
||||
$this->GetRightExpr()->RenameParam($sOldName, $sNewName);
|
||||
}
|
||||
|
||||
public function RenameAlias($sOldName, $sNewName)
|
||||
{
|
||||
$this->GetLeftExpr()->RenameAlias($sOldName, $sNewName);
|
||||
$this->GetRightExpr()->RenameAlias($sOldName, $sNewName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -374,6 +394,11 @@ class UnaryExpression extends Expression
|
||||
// Do nothing
|
||||
// really ? what about :param{$iParamIndex} ??
|
||||
}
|
||||
|
||||
public function RenameAlias($sOldName, $sNewName)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
class ScalarExpression extends UnaryExpression
|
||||
@@ -526,7 +551,7 @@ class FieldExpression extends UnaryExpression
|
||||
/**
|
||||
* Make the most relevant label, given the value of the expression
|
||||
*
|
||||
* @param DBObjectSearch oFilter The context in which this expression has been used
|
||||
* @param DBSearch oFilter The context in which this expression has been used
|
||||
* @param string sValue The value returned by the query, for this expression
|
||||
* @param string sDefault The default value if no relevant label could be computed
|
||||
* @return The label
|
||||
@@ -568,6 +593,14 @@ class FieldExpression extends UnaryExpression
|
||||
}
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
public function RenameAlias($sOldName, $sNewName)
|
||||
{
|
||||
if ($this->m_sParent == $sOldName)
|
||||
{
|
||||
$this->m_sParent = $sNewName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Has been resolved into an SQL expression
|
||||
@@ -792,6 +825,15 @@ class ListExpression extends Expression
|
||||
$this->m_aExpressions[$key] = $oExpr->RenameParam($sOldName, $sNewName);
|
||||
}
|
||||
}
|
||||
|
||||
public function RenameAlias($sOldName, $sNewName)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $key => $oExpr)
|
||||
{
|
||||
$oExpr->RenameAlias($sOldName, $sNewName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -826,7 +868,7 @@ class FunctionExpression extends Expression
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aArgs as $oExpr)
|
||||
foreach ($this->m_aArgs as $iPos => $oExpr)
|
||||
{
|
||||
$aRes[] = $oExpr->Render($aArgs, $bRetrofitParams);
|
||||
}
|
||||
@@ -903,10 +945,18 @@ class FunctionExpression extends Expression
|
||||
}
|
||||
}
|
||||
|
||||
public function RenameAlias($sOldName, $sNewName)
|
||||
{
|
||||
foreach ($this->m_aArgs as $key => $oExpr)
|
||||
{
|
||||
$oExpr->RenameAlias($sOldName, $sNewName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the most relevant label, given the value of the expression
|
||||
*
|
||||
* @param DBObjectSearch oFilter The context in which this expression has been used
|
||||
* @param DBSearch oFilter The context in which this expression has been used
|
||||
* @param string sValue The value returned by the query, for this expression
|
||||
* @param string sDefault The default value if no relevant label could be computed
|
||||
* @return The label
|
||||
@@ -1048,6 +1098,11 @@ class IntervalExpression extends Expression
|
||||
{
|
||||
$this->m_oValue->RenameParam($sOldName, $sNewName);
|
||||
}
|
||||
|
||||
public function RenameAlias($sOldName, $sNewName)
|
||||
{
|
||||
$this->m_oValue->RenameAlias($sOldName, $sNewName);
|
||||
}
|
||||
}
|
||||
|
||||
class CharConcatExpression extends Expression
|
||||
@@ -1152,6 +1207,14 @@ class CharConcatExpression extends Expression
|
||||
$this->m_aExpressions[$key] = $oExpr->RenameParam($sOldName, $sNewName);
|
||||
}
|
||||
}
|
||||
|
||||
public function RenameAlias($sOldName, $sNewName)
|
||||
{
|
||||
foreach ($this->m_aExpressions as $key => $oExpr)
|
||||
{
|
||||
$oExpr->RenameAlias($sOldName, $sNewName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
195
core/htmlbulkexport.class.inc.php
Normal file
195
core/htmlbulkexport.class.inc.php
Normal file
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
// Copyright (C) 2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* Bulk export: HTML export
|
||||
*
|
||||
* @copyright Copyright (C) 2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class HTMLBulkExport extends TabularBulkExport
|
||||
{
|
||||
public function DisplayUsage(Page $oP)
|
||||
{
|
||||
$oP->p(" * html format options:");
|
||||
$oP->p(" *\tfields: (mandatory) the comma separated list of field codes to export (e.g: name,org_id,service_name...).");
|
||||
}
|
||||
|
||||
public function EnumFormParts()
|
||||
{
|
||||
return array_merge(parent::EnumFormParts(), array('interactive_fields_html' => array('interactive_fields_html')));
|
||||
}
|
||||
|
||||
public function DisplayFormPart(WebPage $oP, $sPartId)
|
||||
{
|
||||
switch($sPartId)
|
||||
{
|
||||
case 'interactive_fields_html':
|
||||
$this->GetInteractiveFieldsWidget($oP, 'interactive_fields_html');
|
||||
break;
|
||||
|
||||
default:
|
||||
return parent:: DisplayFormPart($oP, $sPartId);
|
||||
}
|
||||
}
|
||||
|
||||
protected function GetSampleData($oObj, $sAttCode)
|
||||
{
|
||||
return $this->GetValue($oObj, $sAttCode);
|
||||
}
|
||||
|
||||
protected function GetValue($oObj, $sAttCode)
|
||||
{
|
||||
switch($sAttCode)
|
||||
{
|
||||
case 'id':
|
||||
$sRet = $oObj->GetHyperlink();
|
||||
break;
|
||||
|
||||
default:
|
||||
$value = $oObj->Get($sAttCode);
|
||||
if ($value instanceof ormCaseLog)
|
||||
{
|
||||
$sRet = $value->GetAsSimpleHtml();
|
||||
}
|
||||
elseif ($value instanceof ormStopWatch)
|
||||
{
|
||||
$sRet = $value->GetTimeSpent();
|
||||
}
|
||||
else
|
||||
{
|
||||
$sRet = $oObj->GetAsHtml($sAttCode);
|
||||
}
|
||||
}
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public function GetHeader()
|
||||
{
|
||||
$sData = '';
|
||||
|
||||
$oSet = new DBObjectSet($this->oSearch);
|
||||
$this->aStatusInfo['status'] = 'running';
|
||||
$this->aStatusInfo['position'] = 0;
|
||||
$this->aStatusInfo['total'] = $oSet->Count();
|
||||
|
||||
$sData .= "<table class=\"listResults\">\n";
|
||||
$sData .= "<thead>\n";
|
||||
$sData .= "<tr>\n";
|
||||
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
|
||||
{
|
||||
$sData .= "<th>".$aFieldSpec['sColLabel']."</th>\n";
|
||||
}
|
||||
$sData .= "</tr>\n";
|
||||
$sData .= "</thead>\n";
|
||||
$sData .= "<tbody>\n";
|
||||
return $sData;
|
||||
}
|
||||
|
||||
public function GetNextChunk(&$aStatus)
|
||||
{
|
||||
$sRetCode = 'run';
|
||||
$iPercentage = 0;
|
||||
|
||||
$oSet = new DBObjectSet($this->oSearch);
|
||||
$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
|
||||
$this->OptimizeColumnLoad($oSet);
|
||||
|
||||
$sFirstAlias = $this->oSearch->GetClassAlias();
|
||||
|
||||
$iCount = 0;
|
||||
$sData = '';
|
||||
$iPreviousTimeLimit = ini_get('max_execution_time');
|
||||
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
|
||||
while($aRow = $oSet->FetchAssoc())
|
||||
{
|
||||
set_time_limit($iLoopTimeLimit);
|
||||
$oMainObj = $aRow[$sFirstAlias];
|
||||
$sHilightClass = '';
|
||||
if ($oMainObj)
|
||||
{
|
||||
$sHilightClass = $aRow[$sFirstAlias]->GetHilightClass();
|
||||
}
|
||||
if ($sHilightClass != '')
|
||||
{
|
||||
$sData .= "<tr class=\"$sHilightClass\">";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sData .= "<tr>";
|
||||
}
|
||||
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
|
||||
{
|
||||
$sAlias = $aFieldSpec['sAlias'];
|
||||
$sAttCode = $aFieldSpec['sAttCode'];
|
||||
|
||||
$oObj = $aRow[$sAlias];
|
||||
$sField = '';
|
||||
if ($oObj)
|
||||
{
|
||||
$sField = $this->GetValue($oObj, $sAttCode);
|
||||
}
|
||||
$sValue = ($sField === '') ? ' ' : $sField;
|
||||
$sData .= "<td>$sValue</td>";
|
||||
}
|
||||
$sData .= "</tr>";
|
||||
$iCount++;
|
||||
}
|
||||
set_time_limit($iPreviousTimeLimit);
|
||||
$this->aStatusInfo['position'] += $this->iChunkSize;
|
||||
if ($this->aStatusInfo['total'] == 0)
|
||||
{
|
||||
$iPercentage = 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
$iPercentage = floor(min(100.0, 100.0*$this->aStatusInfo['position']/$this->aStatusInfo['total']));
|
||||
}
|
||||
|
||||
if ($iCount < $this->iChunkSize)
|
||||
{
|
||||
$sRetCode = 'done';
|
||||
}
|
||||
|
||||
$aStatus = array('code' => $sRetCode, 'message' => Dict::S('Core:BulkExport:RetrievingData'), 'percentage' => $iPercentage);
|
||||
return $sData;
|
||||
}
|
||||
|
||||
public function GetFooter()
|
||||
{
|
||||
$sData = "</tbody>\n";
|
||||
$sData .= "</table>\n";
|
||||
return $sData;
|
||||
}
|
||||
|
||||
public function GetSupportedFormats()
|
||||
{
|
||||
return array('html' => Dict::S('Core:BulkExport:HTMLFormat'));
|
||||
}
|
||||
|
||||
public function GetMimeType()
|
||||
{
|
||||
return 'text/html';
|
||||
}
|
||||
|
||||
public function GetFileExtension()
|
||||
{
|
||||
return 'html';
|
||||
}
|
||||
}
|
||||
@@ -59,8 +59,11 @@ 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
@@ -69,7 +69,7 @@ abstract class QueryReflection
|
||||
/**
|
||||
* Throws an exception in case of an invalid syntax
|
||||
*/
|
||||
abstract public function __construct($sOQL);
|
||||
abstract public function __construct($sOQL, ModelReflection $oModelReflection);
|
||||
|
||||
abstract public function GetClass();
|
||||
abstract public function GetClassAlias();
|
||||
@@ -222,7 +222,7 @@ class ModelReflectionRuntime extends ModelReflection
|
||||
|
||||
public function GetQuery($sOQL)
|
||||
{
|
||||
return new QueryReflectionRuntime($sOQL);
|
||||
return new QueryReflectionRuntime($sOQL, $this);
|
||||
}
|
||||
|
||||
public function DictString($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
|
||||
@@ -244,7 +244,7 @@ class QueryReflectionRuntime extends QueryReflection
|
||||
/**
|
||||
* throws an exception in case of a wrong syntax
|
||||
*/
|
||||
public function __construct($sOQL)
|
||||
public function __construct($sOQL, ModelReflection $oModelReflection)
|
||||
{
|
||||
$this->oFilter = DBObjectSearch::FromOQL($sOQL);
|
||||
}
|
||||
|
||||
@@ -37,8 +37,19 @@ class iTopMutex
|
||||
public function __construct($sName, $sDBHost = null, $sDBUser = null, $sDBPwd = null)
|
||||
{
|
||||
// Compute the name of a lock for mysql
|
||||
// Note: the name is server-wide!!!
|
||||
// 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();
|
||||
$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))
|
||||
@@ -48,7 +59,6 @@ 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;
|
||||
@@ -121,7 +131,13 @@ class iTopMutex
|
||||
$this->bLocked = true;
|
||||
self::$aAcquiredLocks[$this->sName]++;
|
||||
}
|
||||
return ($res === '1');
|
||||
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');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/bash
|
||||
php /usr/share/php/PHP/LexerGenerator/cli.php oql-lexer.plex
|
||||
php /usr/share/php/PHP/ParserGenerator/cli.php oql-parser.y
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
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
|
||||
937
core/oql/build/PHP/Lempar.php
Normal file
937
core/oql/build/PHP/Lempar.php
Normal file
@@ -0,0 +1,937 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
332
core/oql/build/PHP/LexerGenerator.php
Normal file
332
core/oql/build/PHP/LexerGenerator.php
Normal file
@@ -0,0 +1,332 @@
|
||||
<?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');
|
||||
?>
|
||||
55
core/oql/build/PHP/LexerGenerator/Exception.php
Normal file
55
core/oql/build/PHP/LexerGenerator/Exception.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?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 {}
|
||||
?>
|
||||
533
core/oql/build/PHP/LexerGenerator/Lexer.php
Normal file
533
core/oql/build/PHP/LexerGenerator/Lexer.php
Normal file
@@ -0,0 +1,533 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
?>
|
||||
492
core/oql/build/PHP/LexerGenerator/Parser.out
Normal file
492
core/oql/build/PHP/LexerGenerator/Parser.out
Normal file
@@ -0,0 +1,492 @@
|
||||
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
|
||||
|
||||
1994
core/oql/build/PHP/LexerGenerator/Parser.php
Normal file
1994
core/oql/build/PHP/LexerGenerator/Parser.php
Normal file
File diff suppressed because it is too large
Load Diff
795
core/oql/build/PHP/LexerGenerator/Parser.y
Normal file
795
core/oql/build/PHP/LexerGenerator/Parser.y
Normal file
@@ -0,0 +1,795 @@
|
||||
%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'];
|
||||
}
|
||||
1028
core/oql/build/PHP/LexerGenerator/Regex/Lexer.php
Normal file
1028
core/oql/build/PHP/LexerGenerator/Regex/Lexer.php
Normal file
File diff suppressed because it is too large
Load Diff
285
core/oql/build/PHP/LexerGenerator/Regex/Lexer.plex
Normal file
285
core/oql/build/PHP/LexerGenerator/Regex/Lexer.plex
Normal file
@@ -0,0 +1,285 @@
|
||||
<?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;
|
||||
}
|
||||
*/
|
||||
}
|
||||
2568
core/oql/build/PHP/LexerGenerator/Regex/Parser.out
Normal file
2568
core/oql/build/PHP/LexerGenerator/Regex/Parser.out
Normal file
File diff suppressed because it is too large
Load Diff
1933
core/oql/build/PHP/LexerGenerator/Regex/Parser.php
Normal file
1933
core/oql/build/PHP/LexerGenerator/Regex/Parser.php
Normal file
File diff suppressed because it is too large
Load Diff
477
core/oql/build/PHP/LexerGenerator/Regex/Parser.y
Normal file
477
core/oql/build/PHP/LexerGenerator/Regex/Parser.y
Normal file
@@ -0,0 +1,477 @@
|
||||
%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');
|
||||
}
|
||||
4
core/oql/build/PHP/LexerGenerator/cli.php
Normal file
4
core/oql/build/PHP/LexerGenerator/cli.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
require_once 'PHP/LexerGenerator.php';
|
||||
$a = new PHP_LexerGenerator($_SERVER['argv'][1]);
|
||||
?>
|
||||
806
core/oql/build/PHP/ParserGenerator.php
Normal file
806
core/oql/build/PHP/ParserGenerator.php
Normal file
@@ -0,0 +1,806 @@
|
||||
<?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";
|
||||
}
|
||||
}
|
||||
}
|
||||
257
core/oql/build/PHP/ParserGenerator/Action.php
Normal file
257
core/oql/build/PHP/ParserGenerator/Action.php
Normal file
@@ -0,0 +1,257 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
?>
|
||||
299
core/oql/build/PHP/ParserGenerator/ActionTable.php
Normal file
299
core/oql/build/PHP/ParserGenerator/ActionTable.php
Normal file
@@ -0,0 +1,299 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
?>
|
||||
574
core/oql/build/PHP/ParserGenerator/Config.php
Normal file
574
core/oql/build/PHP/ParserGenerator/Config.php
Normal file
@@ -0,0 +1,574 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
?>
|
||||
1857
core/oql/build/PHP/ParserGenerator/Data.php
Normal file
1857
core/oql/build/PHP/ParserGenerator/Data.php
Normal file
File diff suppressed because it is too large
Load Diff
851
core/oql/build/PHP/ParserGenerator/Parser.php
Normal file
851
core/oql/build/PHP/ParserGenerator/Parser.php
Normal file
@@ -0,0 +1,851 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
126
core/oql/build/PHP/ParserGenerator/PropagationLink.php
Normal file
126
core/oql/build/PHP/ParserGenerator/PropagationLink.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
144
core/oql/build/PHP/ParserGenerator/Rule.php
Normal file
144
core/oql/build/PHP/ParserGenerator/Rule.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?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;
|
||||
}
|
||||
283
core/oql/build/PHP/ParserGenerator/State.php
Normal file
283
core/oql/build/PHP/ParserGenerator/State.php
Normal file
@@ -0,0 +1,283 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
288
core/oql/build/PHP/ParserGenerator/Symbol.php
Normal file
288
core/oql/build/PHP/ParserGenerator/Symbol.php
Normal file
@@ -0,0 +1,288 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
5
core/oql/build/PHP/ParserGenerator/cli.php
Normal file
5
core/oql/build/PHP/ParserGenerator/cli.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
require_once 'PHP/ParserGenerator.php';
|
||||
$me = new PHP_ParserGenerator;
|
||||
$me->main();
|
||||
?>
|
||||
4
core/oql/build/build.bash
Normal file
4
core/oql/build/build.bash
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
php PHP/LexerGenerator/cli.php oql-lexer.plex
|
||||
php PHP/ParserGenerator/cli.php oql-parser.y
|
||||
|
||||
5
core/oql/build/build.cmd
Normal file
5
core/oql/build/build.cmd
Normal file
@@ -0,0 +1,5 @@
|
||||
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
|
||||
pause
|
||||
@@ -2,7 +2,6 @@
|
||||
# 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.cmd
|
||||
build.bash
|
||||
build
|
||||
oql-lexer.plex
|
||||
oql-parser.y
|
||||
oql-parser.y
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 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-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -108,6 +108,7 @@ class OQLLexerRaw
|
||||
do {
|
||||
$rules = array(
|
||||
'/\G[ \t\n\r]+/ ',
|
||||
'/\GUNION/ ',
|
||||
'/\GSELECT/ ',
|
||||
'/\GFROM/ ',
|
||||
'/\GAS/ ',
|
||||
@@ -280,359 +281,364 @@ class OQLLexerRaw
|
||||
function yy_r1_1($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::SELECT;
|
||||
$this->token = OQLParser::UNION;
|
||||
}
|
||||
function yy_r1_2($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::FROM;
|
||||
$this->token = OQLParser::SELECT;
|
||||
}
|
||||
function yy_r1_3($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::AS_ALIAS;
|
||||
$this->token = OQLParser::FROM;
|
||||
}
|
||||
function yy_r1_4($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::WHERE;
|
||||
$this->token = OQLParser::AS_ALIAS;
|
||||
}
|
||||
function yy_r1_5($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::JOIN;
|
||||
$this->token = OQLParser::WHERE;
|
||||
}
|
||||
function yy_r1_6($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::ON;
|
||||
$this->token = OQLParser::JOIN;
|
||||
}
|
||||
function yy_r1_7($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::MATH_DIV;
|
||||
$this->token = OQLParser::ON;
|
||||
}
|
||||
function yy_r1_8($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::MATH_MULT;
|
||||
$this->token = OQLParser::MATH_DIV;
|
||||
}
|
||||
function yy_r1_9($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::MATH_PLUS;
|
||||
$this->token = OQLParser::MATH_MULT;
|
||||
}
|
||||
function yy_r1_10($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::MATH_MINUS;
|
||||
$this->token = OQLParser::MATH_PLUS;
|
||||
}
|
||||
function yy_r1_11($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LOG_AND;
|
||||
$this->token = OQLParser::MATH_MINUS;
|
||||
}
|
||||
function yy_r1_12($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LOG_OR;
|
||||
$this->token = OQLParser::LOG_AND;
|
||||
}
|
||||
function yy_r1_13($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BITWISE_OR;
|
||||
$this->token = OQLParser::LOG_OR;
|
||||
}
|
||||
function yy_r1_14($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BITWISE_AND;
|
||||
$this->token = OQLParser::BITWISE_OR;
|
||||
}
|
||||
function yy_r1_15($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BITWISE_XOR;
|
||||
$this->token = OQLParser::BITWISE_AND;
|
||||
}
|
||||
function yy_r1_16($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BITWISE_LEFT_SHIFT;
|
||||
$this->token = OQLParser::BITWISE_XOR;
|
||||
}
|
||||
function yy_r1_17($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BITWISE_RIGHT_SHIFT;
|
||||
$this->token = OQLParser::BITWISE_LEFT_SHIFT;
|
||||
}
|
||||
function yy_r1_18($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::COMA;
|
||||
$this->token = OQLParser::BITWISE_RIGHT_SHIFT;
|
||||
}
|
||||
function yy_r1_19($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::PAR_OPEN;
|
||||
$this->token = OQLParser::COMA;
|
||||
}
|
||||
function yy_r1_20($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::PAR_CLOSE;
|
||||
$this->token = OQLParser::PAR_OPEN;
|
||||
}
|
||||
function yy_r1_21($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::REGEXP;
|
||||
$this->token = OQLParser::PAR_CLOSE;
|
||||
}
|
||||
function yy_r1_22($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::EQ;
|
||||
$this->token = OQLParser::REGEXP;
|
||||
}
|
||||
function yy_r1_23($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_EQ;
|
||||
$this->token = OQLParser::EQ;
|
||||
}
|
||||
function yy_r1_24($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::GT;
|
||||
$this->token = OQLParser::NOT_EQ;
|
||||
}
|
||||
function yy_r1_25($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LT;
|
||||
$this->token = OQLParser::GT;
|
||||
}
|
||||
function yy_r1_26($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::GE;
|
||||
$this->token = OQLParser::LT;
|
||||
}
|
||||
function yy_r1_27($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LE;
|
||||
$this->token = OQLParser::GE;
|
||||
}
|
||||
function yy_r1_28($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LIKE;
|
||||
$this->token = OQLParser::LE;
|
||||
}
|
||||
function yy_r1_29($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_LIKE;
|
||||
$this->token = OQLParser::LIKE;
|
||||
}
|
||||
function yy_r1_30($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::IN;
|
||||
$this->token = OQLParser::NOT_LIKE;
|
||||
}
|
||||
function yy_r1_31($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_IN;
|
||||
$this->token = OQLParser::IN;
|
||||
}
|
||||
function yy_r1_32($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::INTERVAL;
|
||||
$this->token = OQLParser::NOT_IN;
|
||||
}
|
||||
function yy_r1_33($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_IF;
|
||||
$this->token = OQLParser::INTERVAL;
|
||||
}
|
||||
function yy_r1_34($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_ELT;
|
||||
$this->token = OQLParser::F_IF;
|
||||
}
|
||||
function yy_r1_35($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_COALESCE;
|
||||
$this->token = OQLParser::F_ELT;
|
||||
}
|
||||
function yy_r1_36($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_ISNULL;
|
||||
$this->token = OQLParser::F_COALESCE;
|
||||
}
|
||||
function yy_r1_37($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_CONCAT;
|
||||
$this->token = OQLParser::F_ISNULL;
|
||||
}
|
||||
function yy_r1_38($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_SUBSTR;
|
||||
$this->token = OQLParser::F_CONCAT;
|
||||
}
|
||||
function yy_r1_39($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_TRIM;
|
||||
$this->token = OQLParser::F_SUBSTR;
|
||||
}
|
||||
function yy_r1_40($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE;
|
||||
$this->token = OQLParser::F_TRIM;
|
||||
}
|
||||
function yy_r1_41($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE_FORMAT;
|
||||
$this->token = OQLParser::F_DATE;
|
||||
}
|
||||
function yy_r1_42($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_CURRENT_DATE;
|
||||
$this->token = OQLParser::F_DATE_FORMAT;
|
||||
}
|
||||
function yy_r1_43($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_NOW;
|
||||
$this->token = OQLParser::F_CURRENT_DATE;
|
||||
}
|
||||
function yy_r1_44($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_TIME;
|
||||
$this->token = OQLParser::F_NOW;
|
||||
}
|
||||
function yy_r1_45($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_TO_DAYS;
|
||||
$this->token = OQLParser::F_TIME;
|
||||
}
|
||||
function yy_r1_46($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_FROM_DAYS;
|
||||
$this->token = OQLParser::F_TO_DAYS;
|
||||
}
|
||||
function yy_r1_47($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_YEAR;
|
||||
$this->token = OQLParser::F_FROM_DAYS;
|
||||
}
|
||||
function yy_r1_48($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_MONTH;
|
||||
$this->token = OQLParser::F_YEAR;
|
||||
}
|
||||
function yy_r1_49($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DAY;
|
||||
$this->token = OQLParser::F_MONTH;
|
||||
}
|
||||
function yy_r1_50($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_HOUR;
|
||||
$this->token = OQLParser::F_DAY;
|
||||
}
|
||||
function yy_r1_51($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_MINUTE;
|
||||
$this->token = OQLParser::F_HOUR;
|
||||
}
|
||||
function yy_r1_52($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_SECOND;
|
||||
$this->token = OQLParser::F_MINUTE;
|
||||
}
|
||||
function yy_r1_53($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE_ADD;
|
||||
$this->token = OQLParser::F_SECOND;
|
||||
}
|
||||
function yy_r1_54($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE_SUB;
|
||||
$this->token = OQLParser::F_DATE_ADD;
|
||||
}
|
||||
function yy_r1_55($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_ROUND;
|
||||
$this->token = OQLParser::F_DATE_SUB;
|
||||
}
|
||||
function yy_r1_56($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_FLOOR;
|
||||
$this->token = OQLParser::F_ROUND;
|
||||
}
|
||||
function yy_r1_57($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_INET_ATON;
|
||||
$this->token = OQLParser::F_FLOOR;
|
||||
}
|
||||
function yy_r1_58($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_INET_NTOA;
|
||||
$this->token = OQLParser::F_INET_ATON;
|
||||
}
|
||||
function yy_r1_59($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BELOW;
|
||||
$this->token = OQLParser::F_INET_NTOA;
|
||||
}
|
||||
function yy_r1_60($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BELOW_STRICT;
|
||||
$this->token = OQLParser::BELOW;
|
||||
}
|
||||
function yy_r1_61($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_BELOW;
|
||||
$this->token = OQLParser::BELOW_STRICT;
|
||||
}
|
||||
function yy_r1_62($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_BELOW_STRICT;
|
||||
$this->token = OQLParser::NOT_BELOW;
|
||||
}
|
||||
function yy_r1_63($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::ABOVE;
|
||||
$this->token = OQLParser::NOT_BELOW_STRICT;
|
||||
}
|
||||
function yy_r1_64($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::ABOVE_STRICT;
|
||||
$this->token = OQLParser::ABOVE;
|
||||
}
|
||||
function yy_r1_65($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_ABOVE;
|
||||
$this->token = OQLParser::ABOVE_STRICT;
|
||||
}
|
||||
function yy_r1_66($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_ABOVE_STRICT;
|
||||
$this->token = OQLParser::NOT_ABOVE;
|
||||
}
|
||||
function yy_r1_67($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::HEXVAL;
|
||||
$this->token = OQLParser::NOT_ABOVE_STRICT;
|
||||
}
|
||||
function yy_r1_68($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NUMVAL;
|
||||
$this->token = OQLParser::HEXVAL;
|
||||
}
|
||||
function yy_r1_69($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::STRVAL;
|
||||
$this->token = OQLParser::NUMVAL;
|
||||
}
|
||||
function yy_r1_70($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NAME;
|
||||
$this->token = OQLParser::STRVAL;
|
||||
}
|
||||
function yy_r1_71($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::VARNAME;
|
||||
$this->token = OQLParser::NAME;
|
||||
}
|
||||
function yy_r1_72($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::VARNAME;
|
||||
}
|
||||
function yy_r1_73($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::DOT;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 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-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -80,6 +80,7 @@ class OQLLexerRaw
|
||||
%line $this->line
|
||||
%matchlongest 1
|
||||
whitespace = /[ \t\n\r]+/
|
||||
union = "UNION"
|
||||
select = "SELECT"
|
||||
from = "FROM"
|
||||
as_alias = "AS"
|
||||
@@ -176,6 +177,9 @@ dot = "."
|
||||
whitespace {
|
||||
return false;
|
||||
}
|
||||
union {
|
||||
$this->token = OQLParser::UNION;
|
||||
}
|
||||
select {
|
||||
$this->token = OQLParser::SELECT;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,9 +26,17 @@ 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));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 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-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -83,10 +83,10 @@ class OqlInterpreter
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function ParseObjectQuery()
|
||||
public function ParseQuery()
|
||||
{
|
||||
$oRes = $this->Parse();
|
||||
if (!$oRes instanceof OqlObjectQuery)
|
||||
if (!$oRes instanceof OqlQuery)
|
||||
{
|
||||
throw new OQLException('Expecting an OQL query', $this->m_sQuery, 0, 0, get_class($oRes));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Classes defined for lexical analyze (see oql-parser.y)
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -266,23 +266,18 @@ class IntervalOqlExpression extends IntervalExpression implements CheckableExpre
|
||||
|
||||
abstract class OqlQuery
|
||||
{
|
||||
protected $m_aJoins; // array of OqlJoinSpec
|
||||
protected $m_oCondition; // condition tree (expressions)
|
||||
|
||||
public function __construct($oCondition = null, $aJoins = null)
|
||||
public function __construct()
|
||||
{
|
||||
$this->m_aJoins = $aJoins;
|
||||
$this->m_oCondition = $oCondition;
|
||||
}
|
||||
|
||||
public function GetJoins()
|
||||
{
|
||||
return $this->m_aJoins;
|
||||
}
|
||||
public function GetCondition()
|
||||
{
|
||||
return $this->m_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);
|
||||
}
|
||||
|
||||
class OqlObjectQuery extends OqlQuery
|
||||
@@ -290,13 +285,18 @@ 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;
|
||||
parent::__construct($oCondition, $aJoins);
|
||||
$this->m_aJoins = $aJoins;
|
||||
$this->m_oCondition = $oCondition;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function GetSelectedClasses()
|
||||
@@ -321,6 +321,15 @@ 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
|
||||
@@ -388,17 +397,22 @@ 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');
|
||||
if (!array_key_exists($sExtKeyAttCode, $aExtKeys))
|
||||
$aObjKeys = $oModelReflection->ListAttributes($aAliases[$sFromClass], 'AttributeObjectKey');
|
||||
$aAllKeys = array_merge($aExtKeys, $aObjKeys);
|
||||
if (!array_key_exists($sExtKeyAttCode, $aAllKeys))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown external key in join condition (left expression)', $sSourceQuery, $oLeftField->GetNameDetails(), array_keys($aExtKeys));
|
||||
throw new OqlNormalizeException('Unknown key in join condition (left expression)', $sSourceQuery, $oLeftField->GetNameDetails(), array_keys($aAllKeys));
|
||||
}
|
||||
|
||||
if ($sFromClass == $sJoinClassAlias)
|
||||
{
|
||||
$sTargetClass = $oModelReflection->GetAttributeProperty($aAliases[$sFromClass], $sExtKeyAttCode, 'targetclass');
|
||||
if(!$oModelReflection->IsSameFamilyBranch($aAliases[$sToClass], $sTargetClass))
|
||||
if (array_key_exists($sExtKeyAttCode, $aExtKeys)) // Skip that check for object keys
|
||||
{
|
||||
throw new OqlNormalizeException("The joined class ($aAliases[$sFromClass]) is not compatible with the external key, which is pointing to $sTargetClass", $sSourceQuery, $oLeftField->GetNameDetails());
|
||||
$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());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -434,10 +448,13 @@ class OqlObjectQuery extends OqlQuery
|
||||
$iOperatorCode = TREE_OPERATOR_NOT_ABOVE_STRICT;
|
||||
break;
|
||||
}
|
||||
$sTargetClass = $oModelReflection->GetAttributeProperty($aAliases[$sFromClass], $sExtKeyAttCode, 'targetclass');
|
||||
if(!$oModelReflection->IsSameFamilyBranch($aAliases[$sToClass], $sTargetClass))
|
||||
if (array_key_exists($sExtKeyAttCode, $aExtKeys)) // Skip that check for object keys
|
||||
{
|
||||
throw new OqlNormalizeException("The joined class ($aAliases[$sToClass]) is not compatible with the external key, which is pointing to $sTargetClass", $sSourceQuery, $oLeftField->GetNameDetails());
|
||||
$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());
|
||||
}
|
||||
}
|
||||
$aAttList = $oModelReflection->ListAttributes($aAliases[$sFromClass]);
|
||||
$sAttType = $aAttList[$sExtKeyAttCode];
|
||||
@@ -451,7 +468,6 @@ class OqlObjectQuery extends OqlQuery
|
||||
|
||||
// Check the select information
|
||||
//
|
||||
$aSelected = array();
|
||||
foreach ($this->GetSelectedClasses() as $oClassDetails)
|
||||
{
|
||||
$sClassToSelect = $oClassDetails->GetValue();
|
||||
@@ -459,7 +475,6 @@ class OqlObjectQuery extends OqlQuery
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class [alias]', $sSourceQuery, $oClassDetails, array_keys($aAliases));
|
||||
}
|
||||
$aSelected[$sClassToSelect] = $aAliases[$sClassToSelect];
|
||||
}
|
||||
|
||||
// Check the condition tree
|
||||
@@ -469,6 +484,128 @@ class OqlObjectQuery extends OqlQuery
|
||||
$this->m_oCondition->Check($oModelReflection, $aAliases, $sSourceQuery);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the relevant DBSearch instance (FromOQL)
|
||||
*/
|
||||
public function ToDBSearch($sQuery)
|
||||
{
|
||||
$sClass = $this->GetClass();
|
||||
$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());
|
||||
$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 = MetaModel::GetRootClass($aData['class']);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MetaModel::GetRootClass($aData['class']) != $sRootClass)
|
||||
{
|
||||
$aSubclasses = MetaModel::EnumChildClasses($sRootClass, ENUM_CHILD_CLASSES_ALL);
|
||||
throw new OqlNormalizeException('Incompatible classes: could not find a common ancestor', $sSourceQuery, $aData['class_name'], $aSubclasses);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the relevant DBSearch instance (FromOQL)
|
||||
*/
|
||||
public function ToDBSearch($sQuery)
|
||||
{
|
||||
$aSearches = array();
|
||||
foreach ($this->aQueries as $oQuery)
|
||||
{
|
||||
$aSearches[] = $oQuery->ToDBSearch($sQuery);
|
||||
}
|
||||
|
||||
$oSearch = new DBUnionSearch($aSearches);
|
||||
return $oSearch;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -23,7 +23,7 @@ define('CASELOG_SEPARATOR', "\n".'========== %1$s : %2$s (%3$d) ============'."\
|
||||
/**
|
||||
* Class to store a "case log" in a structured way, keeping track of its successive entries
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
class ormCaseLog {
|
||||
@@ -212,11 +212,88 @@ class ormCaseLog {
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces an HTML representation, aimed at being used to produce a PDF with TCPDF (no table)
|
||||
*/
|
||||
public function GetAsSimpleHtml()
|
||||
{
|
||||
$sStyleCaseLogHeader = '';
|
||||
$sStyleCaseLogEntry = '';
|
||||
|
||||
$sHtml = '<ul class="case_log_simple_html">';
|
||||
$iPos = 0;
|
||||
$aIndex = $this->m_aIndex;
|
||||
for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
|
||||
{
|
||||
$iPos += $aIndex[$index]['separator_length'];
|
||||
$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
$iPos += $aIndex[$index]['text_length'];
|
||||
|
||||
$sEntry = '<li>';
|
||||
// Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
|
||||
// therefore we have changed the format. To preserve the compatibility with existing
|
||||
// installations of iTop, both format are allowed:
|
||||
// the 'date' item is either a DateTime object, or a unix timestamp
|
||||
if (is_int($aIndex[$index]['date']))
|
||||
{
|
||||
// Unix timestamp
|
||||
$sDate = date(Dict::S('UI:CaseLog:DateFormat'),$aIndex[$index]['date']);
|
||||
}
|
||||
elseif (is_object($aIndex[$index]['date']))
|
||||
{
|
||||
if (version_compare(phpversion(), '5.3.0', '>='))
|
||||
{
|
||||
// DateTime
|
||||
$sDate = $aIndex[$index]['date']->format(Dict::S('UI:CaseLog:DateFormat'));
|
||||
}
|
||||
else
|
||||
{
|
||||
// No Warning... but the date is unknown
|
||||
$sDate = '';
|
||||
}
|
||||
}
|
||||
$sEntry .= sprintf(Dict::S('UI:CaseLog:Header_Date_UserName'), '<span class="caselog_header_date">'.$sDate.'</span>', '<span class="caselog_header_user">'.$aIndex[$index]['user_name'].'</span>');
|
||||
$sEntry .= '<div class="case_log_simple_html_entry" style="'.$sStyleCaseLogEntry.'">';
|
||||
$sEntry .= $sTextEntry;
|
||||
$sEntry .= '</div>';
|
||||
$sEntry .= '</li>';
|
||||
$sHtml = $sHtml.$sEntry;
|
||||
}
|
||||
|
||||
// Process the case of an eventual remainder (quick migration of AttributeText fields)
|
||||
if ($iPos < (strlen($this->m_sLog) - 1))
|
||||
{
|
||||
$sTextEntry = substr($this->m_sLog, $iPos);
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
|
||||
if (count($this->m_aIndex) == 0)
|
||||
{
|
||||
$sHtml .= '<li>';
|
||||
$sHtml .= $sTextEntry;
|
||||
$sHtml .= '</li>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml .= '<li>';
|
||||
$sHtml .= Dict::S('UI:CaseLog:InitialValue');
|
||||
$sHtml .= '<div class="case_log_simple_html_entry" style="'.$sStyleCaseLogEntry.'">';
|
||||
$sHtml .= $sTextEntry;
|
||||
$sHtml .= '</div>';
|
||||
$sHtml .= '</li>';
|
||||
}
|
||||
}
|
||||
$sHtml .= '</ul>';
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces an HTML representation, aimed at being used within the iTop framework
|
||||
*/
|
||||
public function GetAsHTML(WebPage $oP = null, $bEditMode = false, $aTransfoHandler = null)
|
||||
{
|
||||
$bPrintableVersion = (utils::ReadParam('printable', '0') == '1');
|
||||
|
||||
$sHtml = '<table style="width:100%;table-layout:fixed"><tr><td>'; // Use table-layout:fixed to force the with to be independent from the actual content
|
||||
$iPos = 0;
|
||||
$aIndex = $this->m_aIndex;
|
||||
@@ -228,7 +305,7 @@ class ormCaseLog {
|
||||
}
|
||||
for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
|
||||
{
|
||||
if ($index < count($aIndex) - CASELOG_VISIBLE_ITEMS)
|
||||
if (!$bPrintableVersion && ($index < count($aIndex) - CASELOG_VISIBLE_ITEMS))
|
||||
{
|
||||
$sOpen = '';
|
||||
$sDisplay = 'style="display:none;"';
|
||||
@@ -296,7 +373,7 @@ class ormCaseLog {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (count($this->m_aIndex) - CASELOG_VISIBLE_ITEMS > 0)
|
||||
if (!$bPrintableVersion && (count($this->m_aIndex) - CASELOG_VISIBLE_ITEMS > 0))
|
||||
{
|
||||
$sOpen = '';
|
||||
$sDisplay = 'style="display:none;"';
|
||||
@@ -384,7 +461,7 @@ class ormCaseLog {
|
||||
}
|
||||
|
||||
|
||||
public function AddLogEntryFromJSON($oJson)
|
||||
public function AddLogEntryFromJSON($oJson, $bCheckUserId = true)
|
||||
{
|
||||
$sText = isset($oJson->message) ? $oJson->message : '';
|
||||
|
||||
@@ -394,16 +471,24 @@ class ormCaseLog {
|
||||
{
|
||||
throw new Exception("Only administrators can set the user id", RestResult::UNAUTHORIZED);
|
||||
}
|
||||
try
|
||||
if ($bCheckUserId && ($oJson->user_id != 0))
|
||||
{
|
||||
$oUser = RestUtils::FindObjectFromKey('User', $oJson->user_id);
|
||||
try
|
||||
{
|
||||
$oUser = RestUtils::FindObjectFromKey('User', $oJson->user_id);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
throw new Exception('user_id: '.$e->getMessage(), $e->getCode());
|
||||
}
|
||||
$iUserId = $oUser->GetKey();
|
||||
$sOnBehalfOf = $oUser->GetFriendlyName();
|
||||
}
|
||||
catch(Exception $e)
|
||||
else
|
||||
{
|
||||
throw new Exception('user_id: '.$e->getMessage(), $e->getCode());
|
||||
$iUserId = $oJson->user_id;
|
||||
$sOnBehalfOf = $oJson->user_login;
|
||||
}
|
||||
$iUserId = $oUser->GetKey();
|
||||
$sOnBehalfOf = $oUser->GetFriendlyName();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -469,5 +554,26 @@ class ormCaseLog {
|
||||
$iLast = end($aKeys); // Strict standards: the parameter passed to 'end' must be a variable since it is passed by reference
|
||||
return $iLast;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text string corresponding to the given entry in the log (zero based index, older entries first)
|
||||
* @param integer $iIndex
|
||||
* @return string The text of the entry
|
||||
*/
|
||||
public function GetEntryAt($iIndex)
|
||||
{
|
||||
$iPos = 0;
|
||||
$index = count($this->m_aIndex) - 1;
|
||||
$aIndex = $this->m_aIndex;
|
||||
while($index > $iIndex)
|
||||
{
|
||||
$iPos += $this->m_aIndex[$index]['separator_length'];
|
||||
$iPos += $this->m_aIndex[$index]['text_length'];
|
||||
$index--;
|
||||
}
|
||||
$iPos += $this->m_aIndex[$index]['separator_length'];
|
||||
$sText = substr($this->m_sLog, $iPos, $this->m_aIndex[$index]['text_length']);
|
||||
return $sText;
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -82,6 +82,26 @@ class ormStopWatch
|
||||
return $this->iTimeSpent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the working elapsed time since the start of the stop watch
|
||||
* even if it is currently running
|
||||
* @param oAttDef AttributeDefinition Attribute hosting the stop watch
|
||||
* @param oObject Hosting object (used for query parameters)
|
||||
*/
|
||||
public function GetElapsedTime($oAttDef, $oObject)
|
||||
{
|
||||
if (is_null($this->iLastStart))
|
||||
{
|
||||
return $this->GetTimeSpent();
|
||||
}
|
||||
else
|
||||
{
|
||||
$iElapsed = $this->ComputeDuration($oObject, $oAttDef, $this->iLastStart, time());
|
||||
return $this->iTimeSpent + $iElapsed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function GetStartDate()
|
||||
{
|
||||
return $this->iStarted;
|
||||
@@ -311,7 +331,7 @@ class ormStopWatch
|
||||
* It is the responsibility of the caller to compute the deadlines
|
||||
* (to avoid computing twice for the same result)
|
||||
*/
|
||||
public function Start($oObject, $oAttDef)
|
||||
public function Start($oObject, $oAttDef, $iNow = null)
|
||||
{
|
||||
if (!is_null($this->iLastStart))
|
||||
{
|
||||
@@ -319,11 +339,16 @@ class ormStopWatch
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_null($iNow))
|
||||
{
|
||||
$iNow = time();
|
||||
}
|
||||
|
||||
if (is_null($this->iStarted))
|
||||
{
|
||||
$this->iStarted = time();
|
||||
$this->iStarted = $iNow;
|
||||
}
|
||||
$this->iLastStart = time();
|
||||
$this->iLastStart = $iNow;
|
||||
$this->iStopped = null;
|
||||
|
||||
return true;
|
||||
@@ -339,8 +364,9 @@ class ormStopWatch
|
||||
// Currently stopped - do nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$iDurationGoal = $this->ComputeGoal($oObject, $oAttDef);
|
||||
$iComputationRefTime = time();
|
||||
foreach ($this->aThresholds as $iPercent => &$aThresholdData)
|
||||
{
|
||||
if (is_null($iDurationGoal))
|
||||
@@ -351,9 +377,20 @@ class ormStopWatch
|
||||
else
|
||||
{
|
||||
$iThresholdDuration = round($iPercent * $iDurationGoal / 100);
|
||||
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
$sClass = get_class($oObject);
|
||||
$sAttCode = $oAttDef->GetCode();
|
||||
WorkingTimeRecorder::Start($oObject, $iComputationRefTime, "ormStopWatch-Deadline-$iPercent-$sAttCode", 'Core:ExplainWTC:StopWatch-Deadline', array("Class:$sClass/Attribute:$sAttCode", $iPercent));
|
||||
}
|
||||
$aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $this->iLastStart, $iThresholdDuration - $this->iTimeSpent);
|
||||
// OR $aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $this->iStarted, $iThresholdDuration);
|
||||
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
WorkingTimeRecorder::End();
|
||||
}
|
||||
}
|
||||
if (is_null($aThresholdData['deadline']) || ($aThresholdData['deadline'] > time()))
|
||||
{
|
||||
@@ -375,7 +412,7 @@ class ormStopWatch
|
||||
/**
|
||||
* Stop counting if not already done
|
||||
*/
|
||||
public function Stop($oObject, $oAttDef)
|
||||
public function Stop($oObject, $oAttDef, $iNow = null)
|
||||
{
|
||||
if (is_null($this->iLastStart))
|
||||
{
|
||||
@@ -383,12 +420,27 @@ class ormStopWatch
|
||||
return false;
|
||||
}
|
||||
|
||||
$iElapsed = $this->ComputeDuration($oObject, $oAttDef, $this->iLastStart, time());
|
||||
if (is_null($iNow))
|
||||
{
|
||||
$iNow = time();
|
||||
}
|
||||
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
$sClass = get_class($oObject);
|
||||
$sAttCode = $oAttDef->GetCode();
|
||||
WorkingTimeRecorder::Start($oObject, $iNow, "ormStopWatch-TimeSpent-$sAttCode", 'Core:ExplainWTC:StopWatch-TimeSpent', array("Class:$sClass/Attribute:$sAttCode"), true /*cumulative*/);
|
||||
}
|
||||
$iElapsed = $this->ComputeDuration($oObject, $oAttDef, $this->iLastStart, $iNow);
|
||||
$this->iTimeSpent = $this->iTimeSpent + $iElapsed;
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
WorkingTimeRecorder::End();
|
||||
}
|
||||
|
||||
foreach ($this->aThresholds as $iPercent => &$aThresholdData)
|
||||
{
|
||||
if (!is_null($aThresholdData['deadline']) && (time() > $aThresholdData['deadline']))
|
||||
if (!is_null($aThresholdData['deadline']) && ($iNow > $aThresholdData['deadline']))
|
||||
{
|
||||
if ($aThresholdData['overrun'] > 0)
|
||||
{
|
||||
@@ -398,7 +450,7 @@ class ormStopWatch
|
||||
else
|
||||
{
|
||||
// First stop after the deadline has been passed
|
||||
$iOverrun = $this->ComputeDuration($oObject, $oAttDef, $aThresholdData['deadline'], time());
|
||||
$iOverrun = $this->ComputeDuration($oObject, $oAttDef, $aThresholdData['deadline'], $iNow);
|
||||
$aThresholdData['overrun'] = $iOverrun;
|
||||
}
|
||||
}
|
||||
@@ -406,7 +458,7 @@ class ormStopWatch
|
||||
}
|
||||
|
||||
$this->iLastStart = null;
|
||||
$this->iStopped = time();
|
||||
$this->iStopped = $iNow;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -459,7 +511,7 @@ class CheckStopWatchThresholds implements iBackgroundProcess
|
||||
{
|
||||
if (is_string($def))
|
||||
{
|
||||
// Old method (pre-2.0.4) non typed parameters
|
||||
// Old method (pre-2.1.0) non typed parameters
|
||||
$aValues[] = $def;
|
||||
}
|
||||
else // if(is_array($def))
|
||||
|
||||
352
core/ownershiplock.class.inc.php
Normal file
352
core/ownershiplock.class.inc.php
Normal file
@@ -0,0 +1,352 @@
|
||||
<?php
|
||||
// Copyright (C) 2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* Mechanism to obtain an exclusive lock while editing an object
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
|
||||
/**
|
||||
* Persistent storage (in the database) for remembering that an object is locked
|
||||
*/
|
||||
class iTopOwnershipToken extends DBObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
'category' => 'application',
|
||||
'key_type' => 'autoincrement',
|
||||
'name_attcode' => array('obj_class', 'obj_key'),
|
||||
'state_attcode' => '',
|
||||
'reconc_keys' => array(''),
|
||||
'db_table' => 'priv_ownership_token',
|
||||
'db_key_field' => 'id',
|
||||
'db_finalclass_field' => '',
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("acquired", array("allowed_values"=>null, "sql"=>'acquired', "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("last_seen", array("allowed_values"=>null, "sql"=>'last_seen', "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("obj_class", array("allowed_values"=>null, "sql"=>'obj_class', "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("obj_key", array("allowed_values"=>null, "sql"=>'obj_key', "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("token", array("allowed_values"=>null, "sql"=>'token', "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "jointype"=> '', "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_SetZListItems('details', array ('obj_class', 'obj_key', 'last_seen', 'token'));
|
||||
MetaModel::Init_SetZListItems('standard_search', array ('obj_class', 'obj_key', 'last_seen', 'token'));
|
||||
MetaModel::Init_SetZListItems('list', array ('obj_class', 'obj_key', 'last_seen', 'token'));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class to acquire/extend/release/kill an exclusive lock on a given persistent object,
|
||||
* for example to prevent concurrent edition of the same object.
|
||||
* Each lock has an expiration delay of 120 seconds (tunable via the configuration parameter 'concurrent_lock_expiration_delay')
|
||||
* A watchdog (called twice during this delay) is in charge of keeping the lock "alive" while an object is being edited.
|
||||
*/
|
||||
class iTopOwnershipLock
|
||||
{
|
||||
protected $sObjClass;
|
||||
protected $iObjKey;
|
||||
protected $oToken;
|
||||
|
||||
/**
|
||||
* Acquires an exclusive lock on the specified DBObject. Once acquired, the lock is identified
|
||||
* by a unique "token" string.
|
||||
* @param string $sObjClass The class of the object for which to acquire the lock
|
||||
* @param integer $iObjKey The identifier of the object for which to acquire the lock
|
||||
* @return multitype:boolean iTopOwnershipLock Ambigous <boolean, string, DBObjectSet>
|
||||
*/
|
||||
public static function AcquireLock($sObjClass, $iObjKey)
|
||||
{
|
||||
$oMutex = new iTopMutex('lock_'.$sObjClass.'::'.$iObjKey);
|
||||
|
||||
$oMutex->Lock();
|
||||
$oOwnershipLock = new iTopOwnershipLock($sObjClass, $iObjKey);
|
||||
$token = $oOwnershipLock->Acquire();
|
||||
$oMutex->Unlock();
|
||||
|
||||
return array('success' => $token !== false, 'token' => $token, 'lock' => $oOwnershipLock, 'acquired' => $oOwnershipLock->oToken->Get('acquired'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the ownership lock or acquires it if none exists
|
||||
* Returns a hash array with 3 elements:
|
||||
* 'status': either true or false, tells if the lock is still owned
|
||||
* 'owner': is status is false, the User object currently owning the lock
|
||||
* 'operation': whether the lock was 'renewed' (i.e. the lock was valid, its duration has been extended) or 'acquired' (there was no valid lock for this object and a new one was created)
|
||||
* @param string $sToken
|
||||
* @return multitype:boolean string User
|
||||
*/
|
||||
public static function ExtendLock($sObjClass, $iObjKey, $sToken)
|
||||
{
|
||||
$oMutex = new iTopMutex('lock_'.$sObjClass.'::'.$iObjKey);
|
||||
|
||||
$oMutex->Lock();
|
||||
$oOwnershipLock = new iTopOwnershipLock($sObjClass, $iObjKey);
|
||||
$aResult = $oOwnershipLock->Extend($sToken);
|
||||
$oMutex->Unlock();
|
||||
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the given lock for the specified object
|
||||
*
|
||||
* @param string $sObjClass The class of the object
|
||||
* @param int $iObjKey The identifier of the object
|
||||
* @param string $sToken The string identifying the lock
|
||||
* @return boolean
|
||||
*/
|
||||
public static function ReleaseLock($sObjClass, $iObjKey, $sToken)
|
||||
{
|
||||
$oMutex = new iTopMutex('lock_'.$sObjClass.'::'.$iObjKey);
|
||||
|
||||
$oMutex->Lock();
|
||||
$oOwnershipLock = new iTopOwnershipLock($sObjClass, $iObjKey);
|
||||
$bResult = $oOwnershipLock->Release($sToken);
|
||||
self::DeleteExpiredLocks(); // Cleanup orphan locks
|
||||
$oMutex->Unlock();
|
||||
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kills the lock for the specified object
|
||||
*
|
||||
* @param string $sObjClass The class of the object
|
||||
* @param int $iObjKey The identifier of the object
|
||||
* @return boolean
|
||||
*/
|
||||
public static function KillLock($sObjClass, $iObjKey)
|
||||
{
|
||||
$oMutex = new iTopMutex('lock_'.$sObjClass.'::'.$iObjKey);
|
||||
|
||||
$oMutex->Lock();
|
||||
$sOQL = "SELECT iTopOwnershipToken WHERE obj_class = :obj_class AND obj_key = :obj_key";
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL, array('obj_class' => $sObjClass, 'obj_key' => $iObjKey)));
|
||||
while($oLock = $oSet->Fetch())
|
||||
{
|
||||
$oLock->DBDelete();
|
||||
}
|
||||
$oMutex->Unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an exclusive lock exists on the specified DBObject.
|
||||
* @param string $sObjClass The class of the object for which to acquire the lock
|
||||
* @param integer $iObjKey The identifier of the object for which to acquire the lock
|
||||
* @return multitype:boolean iTopOwnershipLock Ambigous <boolean, string, DBObjectSet>
|
||||
*/
|
||||
public static function IsLocked($sObjClass, $iObjKey)
|
||||
{
|
||||
$bLocked = false;
|
||||
$oMutex = new iTopMutex('lock_'.$sObjClass.'::'.$iObjKey);
|
||||
|
||||
$oMutex->Lock();
|
||||
$oOwnershipLock = new iTopOwnershipLock($sObjClass, $iObjKey);
|
||||
if ($oOwnershipLock->IsOwned())
|
||||
{
|
||||
$bLocked = true;
|
||||
}
|
||||
$oMutex->Unlock();
|
||||
|
||||
return array('locked' =>$bLocked, 'owner' => $oOwnershipLock->GetOwner());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current owner of the lock
|
||||
* @return User
|
||||
*/
|
||||
public function GetOwner()
|
||||
{
|
||||
if ($this->IsTokenValid())
|
||||
{
|
||||
return MetaModel::GetObject('User', $this->oToken->Get('user_id'), false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The constructor is protected. Use the static methods AcquireLock / ExtendLock / ReleaseLock / KillLock
|
||||
* which are protected against concurrent access by a Mutex.
|
||||
* @param string $sObjClass The class of the object for which to create a lock
|
||||
* @param integer $iObjKey The identifier of the object for which to create a lock
|
||||
*/
|
||||
protected function __construct($sObjClass, $iObjKey)
|
||||
{
|
||||
$sOQL = "SELECT iTopOwnershipToken WHERE obj_class = :obj_class AND obj_key = :obj_key";
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL, array('obj_class' => $sObjClass, 'obj_key' => $iObjKey)));
|
||||
$this->oToken = $oSet->Fetch();
|
||||
$this->sObjClass = $sObjClass;
|
||||
$this->iObjKey = $iObjKey;
|
||||
// IssueLog::Info("iTopOwnershipLock::__construct($sObjClass, $iObjKey) oToken::".($this->oToken ? $this->oToken->GetKey() : 'null'));
|
||||
}
|
||||
|
||||
protected function IsOwned()
|
||||
{
|
||||
return $this->IsTokenValid();
|
||||
}
|
||||
|
||||
protected function Acquire($sToken = null)
|
||||
{
|
||||
if ($this->IsTokenValid())
|
||||
{
|
||||
// IssueLog::Info("Acquire($sToken) returns false");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sToken = $this->TakeOwnership($sToken);
|
||||
// IssueLog::Info("Acquire($sToken) returns $sToken");
|
||||
return $sToken;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the ownership lock or acquires it if none exists
|
||||
* Returns a hash array with 3 elements:
|
||||
* 'status': either true or false, tells if the lock is still owned
|
||||
* 'owner': is status is false, the User object currently owning the lock
|
||||
* 'operation': whether the lock was 'renewed' (i.e. the lock was valid, its duration was extended) or 'expired' (there was no valid lock for this object) or 'lost' (someone else grabbed it)
|
||||
* 'acquired': date at which the lock was initially acquired
|
||||
* @param string $sToken
|
||||
* @return multitype:boolean string User
|
||||
*/
|
||||
protected function Extend($sToken)
|
||||
{
|
||||
$aResult = array('status' => true, 'owner' => '', 'operation' => 'renewed');
|
||||
|
||||
if ($this->IsTokenValid())
|
||||
{
|
||||
if ($sToken === $this->oToken->Get('token'))
|
||||
{
|
||||
$this->oToken->Set('last_seen', date('Y-m-d H:i:s'));
|
||||
$this->oToken->DBUpdate();
|
||||
$aResult['acquired'] = $this->oToken->Get('acquired');
|
||||
}
|
||||
else
|
||||
{
|
||||
// IssueLog::Info("Extend($sToken) returns false");
|
||||
$aResult['status'] = false;
|
||||
$aResult['operation'] = 'lost';
|
||||
$aResult['owner'] = $this->GetOwner();
|
||||
$aResult['acquired'] = $this->oToken->Get('acquired');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aResult['status'] = false;
|
||||
$aResult['operation'] = 'expired';
|
||||
}
|
||||
// IssueLog::Info("Extend($sToken) returns true");
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
protected function HasOwnership($sToken)
|
||||
{
|
||||
$bRet = false;
|
||||
if ($this->IsTokenValid())
|
||||
{
|
||||
if ($sToken === $this->oToken->Get('token'))
|
||||
{
|
||||
$bRet = true;
|
||||
}
|
||||
}
|
||||
// IssueLog::Info("HasOwnership($sToken) return $bRet");
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
protected function Release($sToken)
|
||||
{
|
||||
$bRet = false;
|
||||
// IssueLog::Info("Release... begin [$sToken]");
|
||||
if (($this->oToken) && ($sToken === $this->oToken->Get('token')))
|
||||
{
|
||||
// IssueLog::Info("oToken::".$this->oToken->GetKey().' ('.$sToken.') to be deleted');
|
||||
$this->oToken->DBDelete();
|
||||
// IssueLog::Info("oToken deleted");
|
||||
$this->oToken = null;
|
||||
$bRet = true;
|
||||
}
|
||||
else if ($this->oToken == null)
|
||||
{
|
||||
// IssueLog::Info("Release FAILED oToken == null !!!");
|
||||
}
|
||||
else
|
||||
{
|
||||
// IssueLog::Info("Release FAILED inconsistent tokens: sToken=\"".$sToken.'", oToken->Get(\'token\')="'.$this->oToken->Get('token').'"');
|
||||
}
|
||||
// IssueLog::Info("Release... end");
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
protected function IsTokenValid()
|
||||
{
|
||||
$bRet = false;
|
||||
if ($this->oToken != null)
|
||||
{
|
||||
$sToken = $this->oToken->Get('token');
|
||||
$sDate = $this->oToken->Get('last_seen');
|
||||
if (($sDate != '') && ($sToken != ''))
|
||||
{
|
||||
$oLastSeenTime = new DateTime($sDate);
|
||||
$iNow = date('U');
|
||||
if (($iNow - $oLastSeenTime->format('U')) < MetaModel::GetConfig()->Get('concurrent_lock_expiration_delay'))
|
||||
{
|
||||
$bRet = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
protected function TakeOwnership($sToken = null)
|
||||
{
|
||||
if ($this->oToken == null)
|
||||
{
|
||||
$this->oToken = new iTopOwnershipToken();
|
||||
$this->oToken->Set('obj_class', $this->sObjClass);
|
||||
$this->oToken->Set('obj_key', $this->iObjKey);
|
||||
}
|
||||
$this->oToken->Set('acquired', date('Y-m-d H:i:s'));
|
||||
$this->oToken->Set('user_id', UserRights::GetUserId());
|
||||
$this->oToken->Set('last_seen', date('Y-m-d H:i:s'));
|
||||
if ($sToken === null)
|
||||
{
|
||||
$sToken = sprintf('%X', microtime(true));
|
||||
}
|
||||
$this->oToken->Set('token', $sToken);
|
||||
$this->oToken->DBWrite();
|
||||
return $this->oToken->Get('token');
|
||||
}
|
||||
|
||||
protected static function DeleteExpiredLocks()
|
||||
{
|
||||
$sOQL = "SELECT iTopOwnershipToken WHERE last_seen < :last_seen_limit";
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL, array('last_seen_limit' => date('Y-m-d H:i:s', time() - MetaModel::GetConfig()->Get('concurrent_lock_expiration_delay')))));
|
||||
while($oToken = $oSet->Fetch())
|
||||
{
|
||||
$oToken->DBDelete();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
152
core/pdfbulkexport.class.inc.php
Normal file
152
core/pdfbulkexport.class.inc.php
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
// Copyright (C) 2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* Bulk export: PDF export, based on the HTML export converted to PDF
|
||||
*
|
||||
* @copyright Copyright (C) 2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'application/pdfpage.class.inc.php');
|
||||
|
||||
class PDFBulkExport extends HTMLBulkExport
|
||||
{
|
||||
public function DisplayUsage(Page $oP)
|
||||
{
|
||||
$oP->p(" * pdf format options:");
|
||||
$oP->p(" *\tfields: (mandatory) the comma separated list of field codes to export (e.g: name,org_id,service_name...).");
|
||||
$oP->p(" *\tpage_size: (optional) size of the page. One of A4, A3, Letter (default is 'A4').");
|
||||
$oP->p(" *\tpage_orientation: (optional) the orientation of the page. Either Portrait or Landscape (default is 'Portrait').");
|
||||
}
|
||||
|
||||
public function EnumFormParts()
|
||||
{
|
||||
return array_merge(array('pdf_options' => array('pdf_options')), parent::EnumFormParts());
|
||||
}
|
||||
|
||||
public function DisplayFormPart(WebPage $oP, $sPartId)
|
||||
{
|
||||
switch($sPartId)
|
||||
{
|
||||
case 'pdf_options':
|
||||
$oP->add('<fieldset><legend>'.Dict::S('Core:BulkExport:PDFOptions').'</legend>');
|
||||
$oP->add('<table>');
|
||||
$oP->add('<tr>');
|
||||
$oP->add('<td>'.Dict::S('Core:BulkExport:PDFPageSize').'</td>');
|
||||
$oP->add('<td>'.$this->GetSelectCtrl('page_size', array('A3', 'A4', 'Letter'), 'Core:BulkExport:PageSize-', 'A4').'</td>');
|
||||
$oP->add('</tr>');
|
||||
$oP->add('<td>'.Dict::S('Core:BulkExport:PDFPageOrientation').'</td>');
|
||||
$oP->add('<td>'.$this->GetSelectCtrl('page_orientation', array('P', 'L'), 'Core:BulkExport:PageOrientation-', 'L').'</td>');
|
||||
$oP->add('</tr>');
|
||||
$oP->add('</table>');
|
||||
|
||||
$oP->add('</fieldset>');
|
||||
break;
|
||||
|
||||
default:
|
||||
return parent:: DisplayFormPart($oP, $sPartId);
|
||||
}
|
||||
}
|
||||
|
||||
protected function GetSelectCtrl($sName, $aValues, $sDictPrefix, $sDefaultValue)
|
||||
{
|
||||
$sCurrentValue = utils::ReadParam($sName, $sDefaultValue, false, 'raw_data');
|
||||
$aLabels = array();
|
||||
foreach($aValues as $sVal)
|
||||
{
|
||||
$aLabels[$sVal] = Dict::S($sDictPrefix.$sVal);
|
||||
}
|
||||
asort($aLabels);
|
||||
|
||||
$sHtml = '<select name="'.$sName.'">';
|
||||
foreach($aLabels as $sVal => $sLabel)
|
||||
{
|
||||
$sSelected = ($sVal == $sCurrentValue) ? 'selected' : '';
|
||||
$sHtml .= '<option value="'.$sVal.'" '.$sSelected.'>'.htmlentities($sLabel, ENT_QUOTES, 'UTF-8').'</option>';
|
||||
}
|
||||
$sHtml .= '</select>';
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
|
||||
public function ReadParameters()
|
||||
{
|
||||
parent::ReadParameters();
|
||||
$this->aStatusInfo['page_size'] = utils::ReadParam('page_size', 'A4', true, 'raw_data');
|
||||
$this->aStatusInfo['page_orientation'] = utils::ReadParam('page_orientation', 'L', true);
|
||||
}
|
||||
|
||||
public function GetHeader()
|
||||
{
|
||||
$this->aStatusInfo['tmp_file'] = $this->MakeTmpFile('data');
|
||||
$sData = parent::GetHeader();
|
||||
$hFile = @fopen($this->aStatusInfo['tmp_file'], 'ab');
|
||||
if ($hFile === false)
|
||||
{
|
||||
throw new Exception('PDFBulkExport: Failed to open temporary data file: "'.$this->aStatusInfo['tmp_file'].'" for writing.');
|
||||
}
|
||||
fwrite($hFile, $sData."\n");
|
||||
fclose($hFile);
|
||||
return '';
|
||||
}
|
||||
|
||||
public function GetNextChunk(&$aStatus)
|
||||
{
|
||||
$sData = parent::GetNextChunk($aStatus);
|
||||
$hFile = @fopen($this->aStatusInfo['tmp_file'], 'ab');
|
||||
if ($hFile === false)
|
||||
{
|
||||
throw new Exception('PDFBulkExport: Failed to open temporary data file: "'.$this->aStatusInfo['tmp_file'].'" for writing.');
|
||||
}
|
||||
fwrite($hFile, $sData."\n");
|
||||
fclose($hFile);
|
||||
return '';
|
||||
}
|
||||
|
||||
public function GetFooter()
|
||||
{
|
||||
$sData = parent::GetFooter();
|
||||
|
||||
$oPage = new PDFPage(Dict::Format('Core:BulkExportOf_Class', MetaModel::GetName($this->oSearch->GetClass())), $this->aStatusInfo['page_size'], $this->aStatusInfo['page_orientation']);
|
||||
$oPDF = $oPage->get_tcpdf();
|
||||
$oPDF->SetFont('dejavusans', '', 8, '', true);
|
||||
|
||||
$oPage->add(file_get_contents($this->aStatusInfo['tmp_file']));
|
||||
$oPage->add($sData);
|
||||
|
||||
$sPDF = $oPage->get_pdf();
|
||||
|
||||
return $sPDF;
|
||||
}
|
||||
|
||||
public function GetSupportedFormats()
|
||||
{
|
||||
return array('pdf' => Dict::S('Core:BulkExport:PDFFormat'));
|
||||
}
|
||||
|
||||
public function GetMimeType()
|
||||
{
|
||||
return 'application/x-pdf';
|
||||
}
|
||||
|
||||
public function GetFileExtension()
|
||||
{
|
||||
return 'pdf';
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
/**
|
||||
* Associated with the metamodel -> MakeQuery/MakeQuerySingleTable
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -29,10 +29,11 @@ class QueryBuilderContext
|
||||
protected $m_aClassAliases;
|
||||
protected $m_aTableAliases;
|
||||
protected $m_aModifierProperties;
|
||||
protected $m_aSelectedClasses;
|
||||
|
||||
public $m_oQBExpressions;
|
||||
|
||||
public function __construct($oFilter, $aModifierProperties, $aGroupByExpr = null)
|
||||
public function __construct($oFilter, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null)
|
||||
{
|
||||
$this->m_oRootFilter = $oFilter;
|
||||
$this->m_oQBExpressions = new QueryBuilderExpressions($oFilter, $aGroupByExpr);
|
||||
@@ -41,6 +42,15 @@ class QueryBuilderContext
|
||||
$this->m_aTableAliases = array();
|
||||
|
||||
$this->m_aModifierProperties = $aModifierProperties;
|
||||
if (is_null($aSelectedClasses))
|
||||
{
|
||||
$this->m_aSelectedClasses = $oFilter->GetSelectedClasses();
|
||||
}
|
||||
else
|
||||
{
|
||||
// For the unions, the selected classes can be upper in the hierarchy (lowest common ancestor)
|
||||
$this->m_aSelectedClasses = $aSelectedClasses;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetRootFilter()
|
||||
@@ -69,6 +79,9 @@ class QueryBuilderContext
|
||||
return array();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
public function GetSelectedClass($sAlias)
|
||||
{
|
||||
return $this->m_aSelectedClasses[$sAlias];
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user