mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-21 19:34:12 +01:00
Compare commits
152 Commits
feature/b1
...
2.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eadb11ce39 | ||
|
|
261bc83811 | ||
|
|
2dbaf4dfa1 | ||
|
|
5df9f38391 | ||
|
|
93763c5932 | ||
|
|
242caff990 | ||
|
|
e97e9907c7 | ||
|
|
ac330b6665 | ||
|
|
a4a70a1287 | ||
|
|
dda8651ba2 | ||
|
|
83e2d48f4d | ||
|
|
a903711a7a | ||
|
|
9e17a611d2 | ||
|
|
1e11ed3041 | ||
|
|
0449470cdf | ||
|
|
21a5a2d4ef | ||
|
|
a848cb28f1 | ||
|
|
b7ae6b143e | ||
|
|
ba0c18eec1 | ||
|
|
61366b347d | ||
|
|
edab6643f6 | ||
|
|
ce36ef3aad | ||
|
|
fdb439f054 | ||
|
|
2229f3f015 | ||
|
|
18a5df1ce7 | ||
|
|
f5cb29fadd | ||
|
|
d929732fb6 | ||
|
|
6c36f3bc7c | ||
|
|
c6a59a5309 | ||
|
|
b02c30a525 | ||
|
|
46a647ae66 | ||
|
|
8943a67f85 | ||
|
|
f132d751f5 | ||
|
|
4ba8c9ff9e | ||
|
|
955ae6c392 | ||
|
|
ed33b327fb | ||
|
|
f8f7486be2 | ||
|
|
3215875e5f | ||
|
|
62da90418a | ||
|
|
7c620fae78 | ||
|
|
daef0c3a8f | ||
|
|
52b6d399a0 | ||
|
|
f210f63ec4 | ||
|
|
41694050ea | ||
|
|
3f612cfc90 | ||
|
|
272acdd8d3 | ||
|
|
5a4375cb71 | ||
|
|
5b3d7e2354 | ||
|
|
189cefac1b | ||
|
|
87d36914c4 | ||
|
|
72e14805b1 | ||
|
|
87e2f76793 | ||
|
|
8fbd53d72d | ||
|
|
586cc1f003 | ||
|
|
a77753cfef | ||
|
|
5e464ef3a2 | ||
|
|
0727c9774b | ||
|
|
ee43a365dc | ||
|
|
d5ba0d9ed5 | ||
|
|
7031a52a43 | ||
|
|
12af164dcc | ||
|
|
362cd72e87 | ||
|
|
cb19520b6b | ||
|
|
c74972488d | ||
|
|
b78e40153f | ||
|
|
f7212662b9 | ||
|
|
97c8e1f7a9 | ||
|
|
2afc6d1c62 | ||
|
|
708858da39 | ||
|
|
41dccb468e | ||
|
|
36cfe9e5c2 | ||
|
|
1c7fd57f2e | ||
|
|
f920851420 | ||
|
|
04b8fe3326 | ||
|
|
9fb4374efa | ||
|
|
885cabd6ef | ||
|
|
e00f9c2a83 | ||
|
|
fe24eda4b4 | ||
|
|
483d80b576 | ||
|
|
70a0a3c52e | ||
|
|
5b2f32c08a | ||
|
|
5bad1e1c88 | ||
|
|
2706ebf638 | ||
|
|
9fe3261424 | ||
|
|
9df087984f | ||
|
|
cbb9bcd93d | ||
|
|
ad8f7576e0 | ||
|
|
e205d85728 | ||
|
|
a01d5c2760 | ||
|
|
b57423386c | ||
|
|
22e525452a | ||
|
|
6c84074b02 | ||
|
|
6cc4a6e1be | ||
|
|
04e41ab676 | ||
|
|
3ad64d9823 | ||
|
|
8de7ff5470 | ||
|
|
0a44f34c2c | ||
|
|
4f900e36c1 | ||
|
|
571d90618e | ||
|
|
fe8436f2ad | ||
|
|
a4459901e8 | ||
|
|
bef8fd566f | ||
|
|
0f9994ac74 | ||
|
|
8691ccc013 | ||
|
|
bac7b50090 | ||
|
|
39ff1e318c | ||
|
|
1f8110573c | ||
|
|
7c128e0f6e | ||
|
|
11b50b4917 | ||
|
|
9d771be8b2 | ||
|
|
02315b8aa1 | ||
|
|
7fb3d133e3 | ||
|
|
65fb29a1d4 | ||
|
|
703a432f7b | ||
|
|
4d37942717 | ||
|
|
137067ea43 | ||
|
|
67e12dcc74 | ||
|
|
911c0d2c1b | ||
|
|
4b9648affa | ||
|
|
804afa65f2 | ||
|
|
c56dc6cade | ||
|
|
c4f7055e1a | ||
|
|
44b6dfab1d | ||
|
|
50e79b8c97 | ||
|
|
185825f83c | ||
|
|
c6be331f14 | ||
|
|
c0dd418992 | ||
|
|
c03d5167f6 | ||
|
|
56d625b7b9 | ||
|
|
e74b23f305 | ||
|
|
6e7d2abc9a | ||
|
|
3bebb9bf0f | ||
|
|
1063697e85 | ||
|
|
7bdad90564 | ||
|
|
1dccc54814 | ||
|
|
1c255213e1 | ||
|
|
cb2c172483 | ||
|
|
b43063a6d2 | ||
|
|
3974406f1b | ||
|
|
21aed2d2e1 | ||
|
|
820c257e96 | ||
|
|
56e5616080 | ||
|
|
6f7351ecc3 | ||
|
|
8f56032d49 | ||
|
|
1b6ca2ed14 | ||
|
|
d956682b9f | ||
|
|
76759f1847 | ||
|
|
d441595ee6 | ||
|
|
2be0250aee | ||
|
|
b2a3b10065 | ||
|
|
22b181a8f7 | ||
|
|
71d07be646 |
@@ -75,8 +75,8 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'user_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('description')); // 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
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name','description')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('default_search', array ('name','description'));
|
||||
}
|
||||
|
||||
protected static $m_aCacheProfiles = null;
|
||||
@@ -200,6 +200,12 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
// preserve DB integrity by deleting links to users
|
||||
protected function OnDelete()
|
||||
{
|
||||
// Don't remove admin profile
|
||||
if ($this->Get('name') === ADMIN_PROFILE_NAME)
|
||||
{
|
||||
throw new SecurityException(Dict::Format('UI:Login:Error:AccessAdmin'));
|
||||
}
|
||||
|
||||
// Note: this may break the rule that says: "a user must have at least ONE profile" !
|
||||
$oLnkSet = $this->Get('user_list');
|
||||
while($oLnk = $oLnkSet->Fetch())
|
||||
@@ -297,13 +303,38 @@ class URP_UserProfile extends UserRightsBaseClassGUI
|
||||
$this->CheckIfProfileIsAllowed(UR_ACTION_DELETE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $iActionCode
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \SecurityException
|
||||
*/
|
||||
protected function CheckIfProfileIsAllowed($iActionCode)
|
||||
{
|
||||
// When initializing or admin, we need to let everything pass trough
|
||||
if (!UserRights::IsLoggedIn() || UserRights::IsAdministrator()) { return; }
|
||||
|
||||
// Only administrators can manage administrators
|
||||
$iOrigUserId = $this->GetOriginal('userid');
|
||||
if (!empty($iOrigUserId))
|
||||
{
|
||||
$oUser = MetaModel::GetObject('User', $iOrigUserId, true, true);
|
||||
if (UserRights::IsAdministrator($oUser) && !UserRights::IsAdministrator())
|
||||
{
|
||||
throw new SecurityException(Dict::Format('UI:Login:Error:AccessRestricted'));
|
||||
}
|
||||
}
|
||||
$oUser = MetaModel::GetObject('User', $this->Get('userid'), true, true);
|
||||
if (UserRights::IsAdministrator($oUser) && !UserRights::IsAdministrator())
|
||||
{
|
||||
throw new SecurityException(Dict::Format('UI:Login:Error:AccessRestricted'));
|
||||
}
|
||||
if (!UserRights::IsActionAllowed(get_class($this), $iActionCode, DBObjectSet::FromObject($this)))
|
||||
{
|
||||
throw new SecurityException(Dict::Format('UI:Error:ObjectCannotBeUpdated'));
|
||||
}
|
||||
if (UserRights::IsLoggedIn() && !UserRights::IsAdministrator() && ($this->Get('profile') === ADMIN_PROFILE_NAME))
|
||||
if (!UserRights::IsAdministrator() && ($this->Get('profile') === ADMIN_PROFILE_NAME))
|
||||
{
|
||||
throw new SecurityException(Dict::Format('UI:Login:Error:AccessAdmin'));
|
||||
}
|
||||
@@ -349,6 +380,42 @@ class URP_UserOrg extends UserRightsBaseClassGUI
|
||||
{
|
||||
return Dict::Format('UI:UserManagement:LinkBetween_User_And_Org', $this->Get('userlogin'), $this->Get('allowed_org_name'));
|
||||
}
|
||||
|
||||
|
||||
protected function OnInsert()
|
||||
{
|
||||
$this->CheckIfOrgIsAllowed();
|
||||
}
|
||||
|
||||
protected function OnUpdate()
|
||||
{
|
||||
$this->CheckIfOrgIsAllowed();
|
||||
}
|
||||
|
||||
protected function OnDelete()
|
||||
{
|
||||
$this->CheckIfOrgIsAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \CoreException
|
||||
*/
|
||||
protected function CheckIfOrgIsAllowed()
|
||||
{
|
||||
if (UserRights::IsAdministrator()) { return; }
|
||||
|
||||
$oUser = UserRights::GetUserObject();
|
||||
$oAddon = UserRights::GetModuleInstance();
|
||||
$aOrgs = $oAddon->GetUserOrgs($oUser, '');
|
||||
if (count($aOrgs) > 0)
|
||||
{
|
||||
$iOrigOrgId = $this->GetOriginal('allowed_org_id');
|
||||
if ((!empty($iOrigOrgId) && !in_array($iOrigOrgId, $aOrgs)) || !in_array($this->Get('allowed_org_id'), $aOrgs))
|
||||
{
|
||||
throw new SecurityException(Dict::Format('Class:User/Error:OrganizationNotAllowed'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -97,8 +97,8 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'user_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('description')); // 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
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name', 'description')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('default_search', array ('name', 'description'));
|
||||
}
|
||||
|
||||
protected $m_bCheckReservedNames = true;
|
||||
|
||||
@@ -78,8 +78,8 @@ class URP_Profiles extends UserRightsBaseClass
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'user_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('description')); // 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
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name', 'description')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('default_search', array ('name', 'description'));
|
||||
}
|
||||
|
||||
function GetGrantAsHtml($oUserRights, $sClass, $sAction)
|
||||
|
||||
@@ -244,9 +244,18 @@ EOF
|
||||
//echo $this->s_deferred_content;
|
||||
if (count($this->a_scripts) > 0)
|
||||
{
|
||||
echo "<script type=\"text/javascript\">\n";
|
||||
echo implode("\n", $this->a_scripts);
|
||||
echo "\n</script>\n";
|
||||
echo "<script type=\"text/javascript\">\n";
|
||||
echo implode("\n", $this->a_scripts);
|
||||
echo "\n</script>\n";
|
||||
}
|
||||
if (count($this->a_linked_scripts) > 0)
|
||||
{
|
||||
echo "<script type=\"text/javascript\">\n";
|
||||
foreach($this->a_linked_scripts as $sScriptUrl)
|
||||
{
|
||||
echo '$.getScript('.json_encode($sScriptUrl).");\n";
|
||||
}
|
||||
echo "\n</script>\n";
|
||||
}
|
||||
if (!empty($this->s_deferred_content))
|
||||
{
|
||||
|
||||
@@ -118,7 +118,7 @@ class ApplicationContext
|
||||
$oSearchFilter = new DBObjectSearch('Organization');
|
||||
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
|
||||
$oSet = new CMDBObjectSet($oSearchFilter);
|
||||
$iCount = $oSet->Count();
|
||||
$iCount = $oSet->CountWithLimit(2);
|
||||
if ($iCount == 1)
|
||||
{
|
||||
// Only one possible value for org_id, set it in the context
|
||||
|
||||
@@ -53,8 +53,8 @@ class AuditCategory extends cmdbAbstractObject
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'definition_set', 'rules_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('description', )); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name', 'description')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('name', 'description', 'definition_set')); // Criteria of the advanced search form
|
||||
MetaModel::Init_SetZListItems('standard_search', array('description', 'definition_set')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('default_search', array('name', 'description')); // Criteria of the default search form
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -1768,11 +1768,11 @@ EOF
|
||||
case 'LinkedSet':
|
||||
if ($oAttDef->IsIndirect())
|
||||
{
|
||||
$oWidget = new UILinksWidget($sClass, $sAttCode, $iId, $sNameSuffix, $oAttDef->DuplicatesAllowed(), $aArgs);
|
||||
$oWidget = new UILinksWidget($sClass, $sAttCode, $iId, $sNameSuffix, $oAttDef->DuplicatesAllowed());
|
||||
}
|
||||
else
|
||||
{
|
||||
$oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iId, $sNameSuffix, $aArgs);
|
||||
$oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iId, $sNameSuffix);
|
||||
}
|
||||
$aEventsList[] ='validate';
|
||||
$aEventsList[] ='change';
|
||||
@@ -2045,7 +2045,7 @@ EOF
|
||||
$LockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled');
|
||||
if ($LockEnabled)
|
||||
{
|
||||
$sOwnershipToken = utils::ReadPostedParam('ownership_token', null, false, 'raw_data');
|
||||
$sOwnershipToken = utils::ReadPostedParam('ownership_token', null, '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
|
||||
@@ -2357,8 +2357,9 @@ EOF
|
||||
{
|
||||
if ($oAttDef->IsExternalKey())
|
||||
{
|
||||
/** @var DBObjectSet $oAllowedValues */
|
||||
$oAllowedValues = MetaModel::GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs);
|
||||
if ($oAllowedValues->Count() == 1)
|
||||
if ($oAllowedValues->CountWithLimit(2) == 1)
|
||||
{
|
||||
$oRemoteObj = $oAllowedValues->Fetch();
|
||||
$oObj->Set($sAttCode, $oRemoteObj->GetKey());
|
||||
@@ -2367,7 +2368,7 @@ EOF
|
||||
else
|
||||
{
|
||||
$aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, $aArgs);
|
||||
if (count($aAllowedValues) == 1)
|
||||
if (is_array($aAllowedValues) && (count($aAllowedValues) == 1))
|
||||
{
|
||||
$aValues = array_keys($aAllowedValues);
|
||||
$oObj->Set($sAttCode, $aValues[0]);
|
||||
@@ -2395,7 +2396,7 @@ EOF
|
||||
$sOwnershipToken = null;
|
||||
if ($LockEnabled)
|
||||
{
|
||||
$sOwnershipToken = utils::ReadPostedParam('ownership_token', null, false, 'raw_data');
|
||||
$sOwnershipToken = utils::ReadPostedParam('ownership_token', null, 'raw_data');
|
||||
$aLockInfo = iTopOwnershipLock::AcquireLock($sClass, $iKey);
|
||||
if ($aLockInfo['success'])
|
||||
{
|
||||
@@ -2430,7 +2431,7 @@ EOF
|
||||
$oPage->add('</div>');
|
||||
}
|
||||
$oPage->add("<div class=\"wizContainer\">\n");
|
||||
$oPage->add("<form id=\"apply_stimulus\" method=\"post\" onSubmit=\"return OnSubmit('apply_stimulus');\">\n");
|
||||
$oPage->add("<form id=\"apply_stimulus\" method=\"post\" enctype=\"multipart/form-data\" onSubmit=\"return OnSubmit('apply_stimulus');\">\n");
|
||||
$aDetails = array();
|
||||
$iFieldIndex = 0;
|
||||
$aFieldsMap = array();
|
||||
@@ -2476,8 +2477,9 @@ EOF
|
||||
{
|
||||
if ($oAttDef->IsExternalKey())
|
||||
{
|
||||
/** @var DBObjectSet $oAllowedValues */
|
||||
$oAllowedValues = MetaModel::GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs, '', $this->Get($sAttCode));
|
||||
if ($oAllowedValues->Count() == 1)
|
||||
if ($oAllowedValues->CountWithLimit(2) == 1)
|
||||
{
|
||||
$oRemoteObj = $oAllowedValues->Fetch();
|
||||
$this->Set($sAttCode, $oRemoteObj->GetKey());
|
||||
@@ -3461,7 +3463,7 @@ EOF
|
||||
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$aNewIssues = $oExtensionInstance->OnCheckToWrite($this);
|
||||
if (count($aNewIssues) > 0)
|
||||
if (is_array($aNewIssues) && (count($aNewIssues) > 0)) // Some extensions return null instead of an empty array
|
||||
{
|
||||
$this->m_aCheckIssues = array_merge($this->m_aCheckIssues, $aNewIssues);
|
||||
}
|
||||
@@ -3883,7 +3885,7 @@ EOF
|
||||
$bResult = (count($aErrors) == 0);
|
||||
if ($bResult)
|
||||
{
|
||||
list($bResult, $aErrors) = $oObj->CheckToWrite(true /* Enforce Read-only fields */);
|
||||
list($bResult, $aErrors) = $oObj->CheckToWrite();
|
||||
}
|
||||
if ($bPreview)
|
||||
{
|
||||
|
||||
@@ -148,14 +148,12 @@ abstract class Dashboard
|
||||
protected function InitDashletFromDOMNode($oDomNode)
|
||||
{
|
||||
$sId = $oDomNode->getAttribute('id');
|
||||
$sClass = $oDomNode->getAttribute('xsi:type');
|
||||
$sDashletType = $oDomNode->getAttribute('xsi:type');
|
||||
|
||||
// Test if dashlet can be instanciated, otherwise (uninstalled, broken, ...) we display a placeholder
|
||||
if(!class_exists($sClass))
|
||||
{
|
||||
$sClass = 'DashletUnknown';
|
||||
}
|
||||
$sClass = static::GetDashletClassFromType($sDashletType);
|
||||
$oNewDashlet = new $sClass($this->oMetaModel, $sId);
|
||||
$oNewDashlet->SetDashletType($sDashletType);
|
||||
$oNewDashlet->FromDOMNode($oDomNode);
|
||||
|
||||
return $oNewDashlet;
|
||||
@@ -234,7 +232,7 @@ abstract class Dashboard
|
||||
$oNode = $oDoc->createElement('dashlet');
|
||||
$oDashletsNode->appendChild($oNode);
|
||||
$oNode->setAttribute('id', $oDashlet->GetID());
|
||||
$oNode->setAttribute('xsi:type', get_class($oDashlet));
|
||||
$oNode->setAttribute('xsi:type', $oDashlet->GetDashletType());
|
||||
$oDashletRank = $oDoc->createElement('rank', $iDashletRank);
|
||||
$oNode->appendChild($oDashletRank);
|
||||
$iDashletRank++;
|
||||
@@ -259,7 +257,10 @@ abstract class Dashboard
|
||||
$sDashletClass = $aDashletParams['dashlet_class'];
|
||||
$sId = $aDashletParams['dashlet_id'];
|
||||
$oNewDashlet = new $sDashletClass($this->oMetaModel, $sId);
|
||||
|
||||
if (isset($aDashletParams['dashlet_type']))
|
||||
{
|
||||
$oNewDashlet->SetDashletType($aDashletParams['dashlet_type']);
|
||||
}
|
||||
$oForm = $oNewDashlet->GetForm();
|
||||
$oForm->SetParamsContainer($sId);
|
||||
$oForm->SetPrefix('');
|
||||
@@ -513,6 +514,15 @@ EOF
|
||||
}
|
||||
|
||||
abstract protected function SetFormParams($oForm);
|
||||
|
||||
public static function GetDashletClassFromType($sType, $oFactory = null)
|
||||
{
|
||||
if (is_subclass_of($sType, 'Dashlet'))
|
||||
{
|
||||
return $sType;
|
||||
}
|
||||
return 'DashletUnknown';
|
||||
}
|
||||
}
|
||||
|
||||
class RuntimeDashboard extends Dashboard
|
||||
|
||||
@@ -32,6 +32,7 @@ abstract class Dashlet
|
||||
protected $bFormRedrawNeeded;
|
||||
protected $aProperties; // array of {property => value}
|
||||
protected $aCSSClasses;
|
||||
protected $sDashletType;
|
||||
|
||||
public function __construct(ModelReflection $oModelReflection, $sId)
|
||||
{
|
||||
@@ -41,6 +42,7 @@ abstract class Dashlet
|
||||
$this->bFormRedrawNeeded = false; // By default: no need to redraw the form (independent fields)
|
||||
$this->aProperties = array(); // By default: there is no property
|
||||
$this->aCSSClasses = array('dashlet');
|
||||
$this->sDashletType = get_class($this);
|
||||
}
|
||||
|
||||
// Assuming that a property has the type of its default value, set in the constructor
|
||||
@@ -132,7 +134,14 @@ abstract class Dashlet
|
||||
public function FromXml($sXml)
|
||||
{
|
||||
$oDomDoc = new DOMDocument('1.0', 'UTF-8');
|
||||
libxml_clear_errors();
|
||||
$oDomDoc->loadXml($sXml);
|
||||
$aErrors = libxml_get_errors();
|
||||
if (count($aErrors) > 0)
|
||||
{
|
||||
throw new DOMException("Malformed XML");
|
||||
}
|
||||
|
||||
$this->FromDOMNode($oDomDoc->firstChild);
|
||||
}
|
||||
|
||||
@@ -214,9 +223,10 @@ abstract class Dashlet
|
||||
if ($bEditMode)
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
$sType = $this->sDashletType;
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('#dashlet_$sId').dashlet({dashlet_id: '$sId', dashlet_class: '$sClass'});
|
||||
$('#dashlet_$sId').dashlet({dashlet_id: '$sId', dashlet_class: '$sClass', 'dashlet_type': '$sType'});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
@@ -280,7 +290,7 @@ EOF
|
||||
);
|
||||
}
|
||||
|
||||
public function GetForm()
|
||||
public function GetForm($aInfo = array())
|
||||
{
|
||||
$oForm = new DesignerForm();
|
||||
$sPrefix = "dashlet_".$this->GetID();
|
||||
@@ -293,6 +303,9 @@ EOF
|
||||
$oDashletClassField = new DesignerHiddenField('dashlet_class', '', get_class($this));
|
||||
$oForm->AddField($oDashletClassField);
|
||||
|
||||
$oDashletTypeField = new DesignerHiddenField('dashlet_type', '', $this->sDashletType);
|
||||
$oForm->AddField($oDashletTypeField);
|
||||
|
||||
$oDashletIdField = new DesignerHiddenField('dashlet_id', '', $this->GetID());
|
||||
$oForm->AddField($oDashletIdField);
|
||||
|
||||
@@ -313,6 +326,53 @@ EOF
|
||||
{
|
||||
// Default: do nothing since it's not supported
|
||||
}
|
||||
|
||||
|
||||
protected function GetGroupByOptions($sOql)
|
||||
{
|
||||
$oQuery = $this->oModelReflection->GetQuery($sOql);
|
||||
$sClass = $oQuery->GetClass();
|
||||
$aGroupBy = array();
|
||||
foreach($this->oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType)
|
||||
{
|
||||
if ($sAttType == 'AttributeLinkedSet') continue;
|
||||
if (is_subclass_of($sAttType, 'AttributeLinkedSet')) continue;
|
||||
if ($sAttType == 'AttributeFriendlyName') continue;
|
||||
if (is_subclass_of($sAttType, 'AttributeFriendlyName')) continue;
|
||||
if ($sAttType == 'AttributeExternalField') continue;
|
||||
if (is_subclass_of($sAttType, 'AttributeExternalField')) continue;
|
||||
if ($sAttType == 'AttributeOneWayPassword') continue;
|
||||
|
||||
$sLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);
|
||||
$aGroupBy[$sAttCode] = $sLabel;
|
||||
|
||||
if (is_subclass_of($sAttType, 'AttributeDateTime') || $sAttType == 'AttributeDateTime')
|
||||
{
|
||||
$aGroupBy[$sAttCode.':hour'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-Hour', $sLabel);
|
||||
$aGroupBy[$sAttCode.':month'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-Month', $sLabel);
|
||||
$aGroupBy[$sAttCode.':day_of_week'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-DayOfWeek', $sLabel);
|
||||
$aGroupBy[$sAttCode.':day_of_month'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-DayOfMonth', $sLabel);
|
||||
}
|
||||
}
|
||||
asort($aGroupBy);
|
||||
return $aGroupBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GetDashletType()
|
||||
{
|
||||
return $this->sDashletType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sDashletType
|
||||
*/
|
||||
public function SetDashletType($sDashletType)
|
||||
{
|
||||
$this->sDashletType = $sDashletType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,39 +386,20 @@ class DashletUnknown extends Dashlet
|
||||
{
|
||||
static protected $aClassList = null;
|
||||
|
||||
protected $sOriginalDashletClass;
|
||||
protected $sOriginalDashletXML;
|
||||
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->sOriginalDashletClass = 'Unknown';
|
||||
$this->sOriginalDashletXML = '';
|
||||
$this->aCSSClasses[] = 'dashlet-unknown';
|
||||
}
|
||||
|
||||
public function GetOriginalDashletClass()
|
||||
{
|
||||
return $this->sOriginalDashletClass;
|
||||
}
|
||||
|
||||
public function SetOriginalDashletClass($sOriginalDashletClass)
|
||||
{
|
||||
$this->sOriginalDashletClass = $sOriginalDashletClass;
|
||||
}
|
||||
|
||||
public function FromDOMNode($oDOMNode)
|
||||
{
|
||||
// Parent won't do anything as there is no property declared
|
||||
parent::FromDOMNode($oDOMNode);
|
||||
|
||||
// Original dashlet
|
||||
// - Class
|
||||
if($oDOMNode->hasAttribute('xsi:type'))
|
||||
{
|
||||
$this->sOriginalDashletClass = $oDOMNode->getAttribute('xsi:type');
|
||||
}
|
||||
|
||||
// Build properties from XML
|
||||
$this->sOriginalDashletXML = "";
|
||||
foreach($oDOMNode->childNodes as $oDOMChildNode)
|
||||
@@ -383,13 +424,35 @@ class DashletUnknown extends Dashlet
|
||||
$this->OnUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $oDOMNode
|
||||
*
|
||||
* @throws \DOMFormatException
|
||||
*/
|
||||
public function ToDOMNode($oDOMNode)
|
||||
{
|
||||
$oDoc = new DOMDocument();
|
||||
libxml_clear_errors();
|
||||
$oDoc->loadXML('<root>'.$this->sOriginalDashletXML.'</root>');
|
||||
$aErrors = libxml_get_errors();
|
||||
if (count($aErrors) > 0)
|
||||
{
|
||||
throw new DOMFormatException('Dashlet definition not correctly formatted!');
|
||||
}
|
||||
foreach($oDoc->documentElement->childNodes as $oDOMChildNode)
|
||||
{
|
||||
$oPropNode = $oDOMNode->ownerDocument->importNode($oDOMChildNode, true);
|
||||
$oDOMNode->appendChild($oPropNode);
|
||||
}
|
||||
}
|
||||
|
||||
public function FromParams($aParams)
|
||||
{
|
||||
// For unknown dashlet, parameters are not parsed but passed as a raw xml
|
||||
if(array_key_exists('xml', $aParams))
|
||||
{
|
||||
// A namspace must be present for the "xsi:type" attribute, otherwise a warning will be thrown.
|
||||
$sXML = '<dashlet id="'.$aParams['dashlet_id'].'" xsi:type="'.$aParams['dashlet_class'].'" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'.$aParams['xml'].'</dashlet>';
|
||||
// A namespace must be present for the "xsi:type" attribute, otherwise a warning will be thrown.
|
||||
$sXML = '<dashlet id="'.$aParams['dashlet_id'].'" xsi:type="'.$aParams['dashlet_type'].'" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'.$aParams['xml'].'</dashlet>';
|
||||
$this->FromXml($sXML);
|
||||
}
|
||||
$this->OnUpdate();
|
||||
@@ -400,7 +463,7 @@ class DashletUnknown extends Dashlet
|
||||
$aInfos = static::GetInfo();
|
||||
|
||||
$sIconUrl = utils::GetAbsoluteUrlAppRoot().$aInfos['icon'];
|
||||
$sExplainText = ($bEditMode) ? Dict::Format('UI:DashletUnknown:RenderText:Edit', $this->sOriginalDashletClass) : Dict::S('UI:DashletUnknown:RenderText:View');
|
||||
$sExplainText = ($bEditMode) ? Dict::Format('UI:DashletUnknown:RenderText:Edit', $this->GetDashletType()) : Dict::S('UI:DashletUnknown:RenderText:View');
|
||||
|
||||
$oPage->add('<div class="dashlet-content">');
|
||||
|
||||
@@ -415,7 +478,7 @@ class DashletUnknown extends Dashlet
|
||||
$aInfos = static::GetInfo();
|
||||
|
||||
$sIconUrl = utils::GetAbsoluteUrlAppRoot().$aInfos['icon'];
|
||||
$sExplainText = Dict::Format('UI:DashletUnknown:RenderNoDataText:Edit', $this->sOriginalDashletClass);
|
||||
$sExplainText = Dict::Format('UI:DashletUnknown:RenderNoDataText:Edit', $this->GetDashletType());
|
||||
|
||||
$oPage->add('<div class="dashlet-content">');
|
||||
|
||||
@@ -425,6 +488,15 @@ class DashletUnknown extends Dashlet
|
||||
$oPage->add('</div>');
|
||||
}
|
||||
|
||||
public function GetForm($aInfo = array())
|
||||
{
|
||||
if (isset($aInfo['configuration']) && empty($this->sOriginalDashletXML))
|
||||
{
|
||||
$this->sOriginalDashletXML = $aInfo['configuration'];
|
||||
}
|
||||
return parent::GetForm($aInfo);
|
||||
}
|
||||
|
||||
public function GetPropertiesFields(DesignerForm $oForm)
|
||||
{
|
||||
$oField = new DesignerLongTextField('xml', Dict::S('UI:DashletUnknown:Prop-XMLConfiguration'), $this->sOriginalDashletXML);
|
||||
@@ -446,7 +518,7 @@ class DashletUnknown extends Dashlet
|
||||
if($bHasSubProperties)
|
||||
{
|
||||
$sTmp = $oDOMNode->ownerDocument->saveXML($oDOMNode, LIBXML_NOENT);
|
||||
$sTmp = trim(preg_replace("/(<".$oDOMNode->tagName.".*>|<\/".$oDOMNode->tagName.">)/", "", $sTmp));
|
||||
$sTmp = trim(preg_replace("/(<".$oDOMNode->tagName."[^>]*>|<\/".$oDOMNode->tagName.">)/", "", $sTmp));
|
||||
return $sTmp;
|
||||
}
|
||||
else
|
||||
@@ -490,9 +562,6 @@ class DashletUnknown extends Dashlet
|
||||
|
||||
class DashletProxy extends DashletUnknown
|
||||
{
|
||||
protected $sOriginalDashletClass;
|
||||
protected $sOriginalDashletXML;
|
||||
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
@@ -503,21 +572,30 @@ class DashletProxy extends DashletUnknown
|
||||
unset($this->aCSSClasses[$key]);
|
||||
}
|
||||
|
||||
$this->sOriginalDashletClass = 'Proxy';
|
||||
$this->sOriginalDashletXML = '';
|
||||
$this->aCSSClasses[] = 'dashlet-proxy';
|
||||
}
|
||||
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
|
||||
{
|
||||
// This should never be called.
|
||||
$oPage->add('<div class="dashlet-content">');
|
||||
$oPage->add('<div>This dashlet is not supposed to be rendered as it is just a proxy for third-party widgets.</div>');
|
||||
$oPage->add('</div>');
|
||||
}
|
||||
|
||||
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = array())
|
||||
{
|
||||
// TODO
|
||||
$oPage->add('<div>RENDER NO DATA TO DO! (PREVIEW OR SO)</div>');
|
||||
$aInfos = static::GetInfo();
|
||||
|
||||
$sIconUrl = utils::GetAbsoluteUrlAppRoot().$aInfos['icon'];
|
||||
$sExplainText = Dict::Format('UI:DashletProxy:RenderNoDataText:Edit', $this->GetDashletType());
|
||||
|
||||
$oPage->add('<div class="dashlet-content">');
|
||||
|
||||
$oPage->add('<div class="dashlet-pxy-image"><img src="'.$sIconUrl.'" /></div>');
|
||||
$oPage->add('<div class="dashlet-pxy-text">'.$sExplainText.'</div>');
|
||||
|
||||
$oPage->add('</div>');
|
||||
}
|
||||
|
||||
static public function GetInfo()
|
||||
@@ -702,19 +780,21 @@ abstract class DashletGroupBy extends Dashlet
|
||||
$this->aProperties['query'] = 'SELECT Contact';
|
||||
$this->aProperties['group_by'] = 'status';
|
||||
$this->aProperties['style'] = 'table';
|
||||
$this->aProperties['agregation_function'] = 'count';
|
||||
$this->aProperties['agregation_attribute'] = '';
|
||||
$this->aProperties['aggregation_function'] = 'count';
|
||||
$this->aProperties['aggregation_attribute'] = '';
|
||||
$this->aProperties['limit'] = '';
|
||||
$this->aProperties['order_direction'] = 'desc';
|
||||
$this->aProperties['order_by'] = '';
|
||||
$this->aProperties['order_direction'] = '';
|
||||
}
|
||||
|
||||
protected $sGroupByLabel = null;
|
||||
protected $sGroupByExpr = null;
|
||||
protected $sGroupByAttCode = null;
|
||||
protected $sFunction = null;
|
||||
protected $sAgregationFunction = null;
|
||||
protected $sAgregationAttribute = null;
|
||||
protected $sAggregationFunction = null;
|
||||
protected $sAggregationAttribute = null;
|
||||
protected $sLimit = null;
|
||||
protected $sOrderBy = null;
|
||||
protected $sOrderDirection = null;
|
||||
protected $sClass = null;
|
||||
|
||||
@@ -732,10 +812,22 @@ abstract class DashletGroupBy extends Dashlet
|
||||
$sQuery = $this->aProperties['query'];
|
||||
$sGroupBy = $this->aProperties['group_by'];
|
||||
|
||||
$this->sAgregationFunction = $this->aProperties['agregation_function'];
|
||||
$this->sAgregationAttribute = $this->aProperties['agregation_attribute'];
|
||||
$this->sAggregationFunction = $this->aProperties['aggregation_function'];
|
||||
$this->sAggregationAttribute = $this->aProperties['aggregation_attribute'];
|
||||
|
||||
$this->sLimit = $this->aProperties['limit'];
|
||||
$this->sOrderDirection = $this->aProperties['order_direction'];
|
||||
$this->sOrderBy = $this->aProperties['order_by'];
|
||||
if (empty($this->sOrderBy))
|
||||
{
|
||||
if ($this->aProperties['style'] == 'pie')
|
||||
{
|
||||
$this->sOrderBy = 'function';
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->sOrderBy = 'attribute';
|
||||
}
|
||||
}
|
||||
|
||||
// First perform the query - if the OQL is not ok, it will generate an exception : no need to go further
|
||||
try
|
||||
@@ -750,6 +842,7 @@ abstract class DashletGroupBy extends Dashlet
|
||||
$this->sClass = null;
|
||||
$sClassAlias = '';
|
||||
}
|
||||
|
||||
// Check groupby... it can be wrong at this stage
|
||||
if (preg_match('/^(.*):(.*)$/', $sGroupBy, $aMatches))
|
||||
{
|
||||
@@ -761,6 +854,28 @@ abstract class DashletGroupBy extends Dashlet
|
||||
$this->sGroupByAttCode = $sGroupBy;
|
||||
$this->sFunction = null;
|
||||
}
|
||||
|
||||
if (empty($this->aProperties['order_direction']))
|
||||
{
|
||||
$aAttributeTypes = $this->oModelReflection->ListAttributes($this->sClass);
|
||||
if (isset($aAttributeTypes[$this->sGroupByAttCode]))
|
||||
{
|
||||
$sAttributeType = $aAttributeTypes[$this->sGroupByAttCode];
|
||||
if (is_subclass_of($sAttributeType, 'AttributeDateTime') || $sAttributeType == 'AttributeDateTime')
|
||||
{
|
||||
$this->sOrderDirection = 'asc';
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->sOrderDirection = 'desc';
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->sOrderDirection = $this->aProperties['order_direction'];
|
||||
}
|
||||
|
||||
if ((!is_null($this->sClass)) && $this->oModelReflection->IsValidAttCode($this->sClass, $this->sGroupByAttCode))
|
||||
{
|
||||
$sAttLabel = $this->oModelReflection->GetLabel($this->sClass, $this->sGroupByAttCode);
|
||||
@@ -832,10 +947,11 @@ abstract class DashletGroupBy extends Dashlet
|
||||
'chart_title' => $sTitle,
|
||||
'group_by' => $this->sGroupByExpr,
|
||||
'group_by_label' => $this->sGroupByLabel,
|
||||
'agregation_function' => $this->sAgregationFunction,
|
||||
'agregation_attribute' => $this->sAgregationAttribute,
|
||||
'aggregation_function' => $this->sAggregationFunction,
|
||||
'aggregation_attribute' => $this->sAggregationAttribute,
|
||||
'limit' => $this->sLimit,
|
||||
'order_direction' => $this->sOrderDirection,
|
||||
'order_by' => $this->sOrderBy,
|
||||
);
|
||||
$sHtmlTitle = ''; // done in the itop block
|
||||
break;
|
||||
@@ -847,10 +963,11 @@ abstract class DashletGroupBy extends Dashlet
|
||||
'chart_title' => $sTitle,
|
||||
'group_by' => $this->sGroupByExpr,
|
||||
'group_by_label' => $this->sGroupByLabel,
|
||||
'agregation_function' => $this->sAgregationFunction,
|
||||
'agregation_attribute' => $this->sAgregationAttribute,
|
||||
'aggregation_function' => $this->sAggregationFunction,
|
||||
'aggregation_attribute' => $this->sAggregationAttribute,
|
||||
'limit' => $this->sLimit,
|
||||
'order_direction' => $this->sOrderDirection,
|
||||
'order_by' => $this->sOrderBy,
|
||||
);
|
||||
$sHtmlTitle = ''; // done in the itop block
|
||||
break;
|
||||
@@ -862,10 +979,11 @@ abstract class DashletGroupBy extends Dashlet
|
||||
$aExtraParams = array(
|
||||
'group_by' => $this->sGroupByExpr,
|
||||
'group_by_label' => $this->sGroupByLabel,
|
||||
'agregation_function' => $this->sAgregationFunction,
|
||||
'agregation_attribute' => $this->sAgregationAttribute,
|
||||
'aggregation_function' => $this->sAggregationFunction,
|
||||
'aggregation_attribute' => $this->sAggregationAttribute,
|
||||
'limit' => $this->sLimit,
|
||||
'order_direction' => $this->sOrderDirection,
|
||||
'order_by' => $this->sOrderBy,
|
||||
);
|
||||
break;
|
||||
}
|
||||
@@ -885,7 +1003,6 @@ abstract class DashletGroupBy extends Dashlet
|
||||
protected function MakeSimulatedData()
|
||||
{
|
||||
$sQuery = $this->aProperties['query'];
|
||||
$sGroupBy = $this->aProperties['group_by'];
|
||||
|
||||
$oQuery = $this->oModelReflection->GetQuery($sQuery);
|
||||
$sClass = $oQuery->GetClass();
|
||||
@@ -962,36 +1079,6 @@ abstract class DashletGroupBy extends Dashlet
|
||||
$oPage->add('</div>');
|
||||
}
|
||||
|
||||
protected function GetGroupByOptions($sOql)
|
||||
{
|
||||
$oQuery = $this->oModelReflection->GetQuery($sOql);
|
||||
$sClass = $oQuery->GetClass();
|
||||
$aGroupBy = array();
|
||||
foreach($this->oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType)
|
||||
{
|
||||
if ($sAttType == 'AttributeLinkedSet') continue;
|
||||
if (is_subclass_of($sAttType, 'AttributeLinkedSet')) continue;
|
||||
if ($sAttType == 'AttributeFriendlyName') continue;
|
||||
if (is_subclass_of($sAttType, 'AttributeFriendlyName')) continue;
|
||||
if ($sAttType == 'AttributeExternalField') continue;
|
||||
if (is_subclass_of($sAttType, 'AttributeExternalField')) continue;
|
||||
if ($sAttType == 'AttributeOneWayPassword') continue;
|
||||
|
||||
$sLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);
|
||||
$aGroupBy[$sAttCode] = $sLabel;
|
||||
|
||||
if (is_subclass_of($sAttType, 'AttributeDateTime') || $sAttType == 'AttributeDateTime')
|
||||
{
|
||||
$aGroupBy[$sAttCode.':hour'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-Hour', $sLabel);
|
||||
$aGroupBy[$sAttCode.':month'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-Month', $sLabel);
|
||||
$aGroupBy[$sAttCode.':day_of_week'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-DayOfWeek', $sLabel);
|
||||
$aGroupBy[$sAttCode.':day_of_month'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-DayOfMonth', $sLabel);
|
||||
}
|
||||
}
|
||||
asort($aGroupBy);
|
||||
return $aGroupBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DesignerForm $oForm
|
||||
* @throws DictExceptionMissingString
|
||||
@@ -1018,6 +1105,7 @@ abstract class DashletGroupBy extends Dashlet
|
||||
{
|
||||
$oField = new DesignerTextField('group_by', Dict::S('UI:DashletGroupBy:Prop-GroupBy'), $this->aProperties['group_by']);
|
||||
$oField->SetReadOnly();
|
||||
$aGroupBy = array();
|
||||
}
|
||||
$oForm->AddField($oField);
|
||||
|
||||
@@ -1034,7 +1122,7 @@ abstract class DashletGroupBy extends Dashlet
|
||||
|
||||
$aFunctionAttributes = $this->GetNumericAttributes($this->aProperties['query']);
|
||||
$aFunctions = $this->GetAllowedFunctions($aFunctionAttributes);
|
||||
$oSelectorField = new DesignerFormSelectorField('agregation_function', Dict::S('UI:DashletGroupBy:Prop-Function'), $this->aProperties['agregation_function']);
|
||||
$oSelectorField = new DesignerFormSelectorField('aggregation_function', Dict::S('UI:DashletGroupBy:Prop-Function'), $this->aProperties['aggregation_function']);
|
||||
$oForm->AddField($oSelectorField);
|
||||
$oSelectorField->SetMandatory();
|
||||
// Count sub-menu
|
||||
@@ -1043,25 +1131,52 @@ abstract class DashletGroupBy extends Dashlet
|
||||
foreach($aFunctions as $sFct => $sLabel)
|
||||
{
|
||||
$oSubForm = new DesignerForm();
|
||||
$oField = new DesignerComboField('agregation_attribute', Dict::S('UI:DashletGroupBy:Prop-FunctionAttribute'), $this->aProperties['agregation_attribute']);
|
||||
$oField = new DesignerComboField('aggregation_attribute', Dict::S('UI:DashletGroupBy:Prop-FunctionAttribute'), $this->aProperties['aggregation_attribute']);
|
||||
$oField->SetMandatory();
|
||||
$oField->SetAllowedValues($aFunctionAttributes);
|
||||
$oSubForm->AddField($oField);
|
||||
$oSelectorField->AddSubForm($oSubForm, $sLabel, $sFct);
|
||||
}
|
||||
|
||||
$aOrderField = array();
|
||||
|
||||
if (isset($this->aProperties['group_by']) && isset($aGroupBy[$this->aProperties['group_by']]))
|
||||
{
|
||||
$aOrderField['attribute'] = $aGroupBy[$this->aProperties['group_by']];
|
||||
}
|
||||
|
||||
if ($this->aProperties['aggregation_function'] == 'count')
|
||||
{
|
||||
$aOrderField['function'] = Dict::S('UI:GroupBy:count');
|
||||
}
|
||||
else
|
||||
{
|
||||
$aOrderField['function'] = $aFunctions[$this->aProperties['aggregation_function']];
|
||||
}
|
||||
$oSelectorField = new DesignerFormSelectorField('order_by', Dict::S('UI:DashletGroupBy:Prop-OrderField'), $this->aProperties['order_by']);
|
||||
$oForm->AddField($oSelectorField);
|
||||
$oSelectorField->SetMandatory();
|
||||
foreach($aOrderField as $sField => $sLabel)
|
||||
{
|
||||
$oSubForm = new DesignerForm();
|
||||
if ($sField == 'function')
|
||||
{
|
||||
$oField = new DesignerIntegerField('limit', Dict::S('UI:DashletGroupBy:Prop-Limit'), $this->aProperties['limit']);
|
||||
$oSubForm->AddField($oField);
|
||||
}
|
||||
$oSelectorField->AddSubForm($oSubForm, $sLabel, $sField);
|
||||
}
|
||||
|
||||
$aOrderDirections = array(
|
||||
'asc' => Dict::S('UI:DashletGroupBy:Order:asc'),
|
||||
'desc' => Dict::S('UI:DashletGroupBy:Order:desc'),
|
||||
);
|
||||
$oField = new DesignerComboField('order_direction', Dict::S('UI:DashletGroupBy:Prop-OrderDirection'), $this->aProperties['order_direction']);
|
||||
$sOrderDirection = empty($this->aProperties['order_direction']) ? $this->sOrderDirection : $this->aProperties['order_direction'];
|
||||
$oField = new DesignerComboField('order_direction', Dict::S('UI:DashletGroupBy:Prop-OrderDirection'), $sOrderDirection);
|
||||
$oField->SetMandatory();
|
||||
$oField->SetAllowedValues($aOrderDirections);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerIntegerField('limit', Dict::S('UI:DashletGroupBy:Prop-Limit'), $this->aProperties['limit']);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1076,7 +1191,7 @@ abstract class DashletGroupBy extends Dashlet
|
||||
}
|
||||
return array(
|
||||
$this->aProperties['group_by'] => $this->oModelReflection->GetLabel($this->sClass, $this->aProperties['group_by']),
|
||||
'_itop_'.$this->aProperties['agregation_function'].'_' => Dict::S('UI:GroupBy:'.$this->aProperties['agregation_function']));
|
||||
'_itop_'.$this->aProperties['aggregation_function'].'_' => Dict::S('UI:GroupBy:'.$this->aProperties['aggregation_function']));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1118,6 +1233,7 @@ abstract class DashletGroupBy extends Dashlet
|
||||
case 'AttributeDuration':
|
||||
case 'AttributeInteger':
|
||||
case 'AttributePercentage':
|
||||
case 'AttributeSubItem': // TODO: Known limitation: no unit displayed (values in sec)
|
||||
$sLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);
|
||||
$aFunctionAttributes[$sAttCode] = $sLabel;
|
||||
break;
|
||||
@@ -1176,11 +1292,11 @@ abstract class DashletGroupBy extends Dashlet
|
||||
$oDashlet->bRedrawNeeded = true;
|
||||
$oDashlet->bFormRedrawNeeded = true;
|
||||
}
|
||||
if (in_array('group_by', $aUpdatedFields) || in_array('agregation_attribute', $aUpdatedFields) || in_array('order_direction', $aUpdatedFields) || in_array('limit', $aUpdatedFields))
|
||||
if (in_array('aggregation_attribute', $aUpdatedFields) || in_array('order_direction', $aUpdatedFields) || in_array('order_by', $aUpdatedFields) || in_array('limit', $aUpdatedFields))
|
||||
{
|
||||
$oDashlet->bRedrawNeeded = true;
|
||||
}
|
||||
if (in_array('agregation_function', $aUpdatedFields))
|
||||
if (in_array('group_by', $aUpdatedFields) || in_array('aggregation_function', $aUpdatedFields))
|
||||
{
|
||||
$oDashlet->bRedrawNeeded = true;
|
||||
$oDashlet->bFormRedrawNeeded = true;
|
||||
@@ -1332,7 +1448,6 @@ class DashletGroupByBars extends DashletGroupBy
|
||||
$sJSNames = json_encode($aNames);
|
||||
|
||||
$sJson = json_encode($aDisplayValues);
|
||||
$sJSCount = json_encode(Dict::S('UI:GroupBy:Count'));
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
window.setTimeout(function() {
|
||||
@@ -1404,7 +1519,6 @@ class DashletGroupByTable extends DashletGroupBy
|
||||
|
||||
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = array())
|
||||
{
|
||||
$sTitle = $this->aProperties['title'];
|
||||
|
||||
$aDisplayValues = $this->MakeSimulatedData();
|
||||
$iTotal = 0;
|
||||
@@ -1614,7 +1728,6 @@ class DashletHeaderDynamic extends Dashlet
|
||||
$sSubtitle = $this->aProperties['subtitle'];
|
||||
$sQuery = $this->aProperties['query'];
|
||||
$sGroupBy = $this->aProperties['group_by'];
|
||||
$aValues = $this->aProperties['values'];
|
||||
|
||||
$oQuery = $this->oModelReflection->GetQuery($sQuery);
|
||||
$sClass = $oQuery->GetClass();
|
||||
@@ -1631,14 +1744,6 @@ class DashletHeaderDynamic extends Dashlet
|
||||
|
||||
$iTotal = 0;
|
||||
$aValues = $this->GetValues();
|
||||
if (count($aValues) > 0)
|
||||
{
|
||||
// Stats grouped by <group_by>
|
||||
}
|
||||
else
|
||||
{
|
||||
// Simple stats
|
||||
}
|
||||
|
||||
$oPage->add('<div class="display_block" id="'.$sBlockId.'">');
|
||||
$oPage->add('<div class="summary-details">');
|
||||
@@ -1663,7 +1768,6 @@ class DashletHeaderDynamic extends Dashlet
|
||||
|
||||
$sTitle = $this->oModelReflection->DictString($sTitle);
|
||||
$sSubtitle = $this->oModelReflection->DictFormat($sSubtitle, $iTotal);
|
||||
// $sSubtitle = "original: $sSubtitle, S:".$this->oModelReflection->DictString($sSubtitle).", Format: '".$this->oModelReflection->DictFormat($sSubtitle, $iTotal)."'";
|
||||
|
||||
$oPage->add('<h1>'.$sTitle.'</h1>');
|
||||
$oPage->add('<a class="summary">'.$sSubtitle.'</a>');
|
||||
@@ -1693,16 +1797,7 @@ class DashletHeaderDynamic extends Dashlet
|
||||
// Group by field: build the list of possible values (attribute codes + ...)
|
||||
$oQuery = $this->oModelReflection->GetQuery($this->aProperties['query']);
|
||||
$sClass = $oQuery->GetClass();
|
||||
$aGroupBy = array();
|
||||
foreach($this->oModelReflection->ListAttributes($sClass, 'AttributeEnum,AttributeFinalClass') as $sAttCode => $sAttType)
|
||||
{
|
||||
if (is_subclass_of($sAttType, 'AttributeFinalClass') || ($sAttType == 'AttributeFinalClass'))
|
||||
{
|
||||
if (!$this->oModelReflection->HasChildrenClasses($sClass)) continue;
|
||||
}
|
||||
$sLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);
|
||||
$aGroupBy[$sAttCode] = $sLabel;
|
||||
}
|
||||
$aGroupBy = $this->GetGroupByOptions($this->aProperties['query']);
|
||||
$oField = new DesignerComboField('group_by', Dict::S('UI:DashletHeaderDynamic:Prop-GroupBy'), $this->aProperties['group_by']);
|
||||
$oField->SetMandatory();
|
||||
$oField->SetAllowedValues($aGroupBy);
|
||||
@@ -1821,7 +1916,7 @@ class DashletBadge extends Dashlet
|
||||
$aExtraParams = array(
|
||||
'context_filter' => 1,
|
||||
);
|
||||
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
|
||||
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM)
|
||||
$oBlock->Display($oPage, $sBlockId, $aExtraParams);
|
||||
|
||||
$oPage->add('</div>');
|
||||
@@ -1870,7 +1965,6 @@ class DashletBadge extends Dashlet
|
||||
foreach($aClasses as $sClass => $sLabel)
|
||||
{
|
||||
$sIconUrl = $this->oModelReflection->GetClassIcon($sClass, false);
|
||||
$sIconFilePath = str_replace(utils::GetAbsoluteUrlAppRoot(), APPROOT, $sIconUrl);
|
||||
if ($sIconUrl == '')
|
||||
{
|
||||
// The icon does not exist, let's use a transparent one of the same size.
|
||||
|
||||
@@ -108,11 +108,15 @@ class DisplayBlock
|
||||
$oBlock = new DisplayBlock($oDummyFilter, $sStyle, false, $aParams); // DisplayBlocks built this way are synchronous
|
||||
return $oBlock;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a DisplayBlock object from an XML template
|
||||
*
|
||||
* @param $sTemplate string The XML template
|
||||
*
|
||||
* @return DisplayBlock The DisplayBlock object, or null if the template is invalid
|
||||
* @throws \ApplicationException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public static function FromTemplate($sTemplate)
|
||||
{
|
||||
@@ -122,7 +126,6 @@ class DisplayBlock
|
||||
$aParams = array();
|
||||
|
||||
if (($iStartPos === false) || ($iEndPos === false)) return null; // invalid template
|
||||
$sITopBlock = substr($sTemplate,$iStartPos, $iEndPos-$iStartPos+strlen('</'.self::TAG_BLOCK.'>'));
|
||||
$sITopData = substr($sTemplate, 1+$iEndTag, $iEndPos - $iEndTag - 1);
|
||||
$sITopTag = substr($sTemplate, $iStartPos + strlen('<'.self::TAG_BLOCK), $iEndTag - $iStartPos - strlen('<'.self::TAG_BLOCK));
|
||||
|
||||
@@ -143,10 +146,6 @@ class DisplayBlock
|
||||
{
|
||||
$sBlockClass = $aMatches[1];
|
||||
}
|
||||
if (preg_match('/ objectclass="(.*)"/U',$sITopTag, $aMatches))
|
||||
{
|
||||
$sObjectClass = $aMatches[1];
|
||||
}
|
||||
if (preg_match('/ encoding="(.*)"/U',$sITopTag, $aMatches))
|
||||
{
|
||||
$sEncoding = strtolower($aMatches[1]);
|
||||
@@ -195,6 +194,7 @@ class DisplayBlock
|
||||
}
|
||||
|
||||
}
|
||||
$oFilter = null;
|
||||
switch($sEncoding)
|
||||
{
|
||||
case 'text/serialize':
|
||||
@@ -428,71 +428,7 @@ class DisplayBlock
|
||||
case 'count':
|
||||
if (isset($aExtraParams['group_by']))
|
||||
{
|
||||
$sAlias = $this->m_oFilter->GetClassAlias();
|
||||
if (isset($aExtraParams['group_by_label']))
|
||||
{
|
||||
$oGroupByExp = Expression::FromOQL($aExtraParams['group_by']);
|
||||
$sGroupByLabel = $aExtraParams['group_by_label'];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Backward compatibility: group_by is simply a field id
|
||||
$oGroupByExp = new FieldExpression($aExtraParams['group_by'], $sAlias);
|
||||
$sGroupByLabel = MetaModel::GetLabel($this->m_oFilter->GetClass(), $aExtraParams['group_by']);
|
||||
}
|
||||
|
||||
// Security filtering
|
||||
$aFields = $oGroupByExp->ListRequiredFields();
|
||||
foreach($aFields as $sFieldAlias)
|
||||
{
|
||||
if (preg_match('/^([^.]+)\\.([^.]+)$/', $sFieldAlias, $aMatches))
|
||||
{
|
||||
$sFieldClass = $this->m_oFilter->GetClassName($aMatches[1]);
|
||||
$oAttDef = MetaModel::GetAttributeDef($sFieldClass, $aMatches[2]);
|
||||
if ($oAttDef instanceof AttributeOneWayPassword)
|
||||
{
|
||||
throw new Exception('Grouping on password fields is not supported.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$aGroupBy = array();
|
||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||
$aQueryParams = array();
|
||||
if (isset($aExtraParams['query_params']))
|
||||
{
|
||||
$aQueryParams = $aExtraParams['query_params'];
|
||||
}
|
||||
$aFunctions = array();
|
||||
$aOrderBy = array();
|
||||
$sAgregationFunction = 'count';
|
||||
$sFctVar = '_itop_count_';
|
||||
$sAgregationAttr = '';
|
||||
if (isset($aExtraParams['agregation_function']) && !empty($aExtraParams['agregation_attribute']))
|
||||
{
|
||||
$sAgregationFunction = $aExtraParams['agregation_function'];
|
||||
$sAgregationAttr = $aExtraParams['agregation_attribute'];
|
||||
$oAttrExpr = Expression::FromOQL('`'.$sAlias.'`.`'.$sAgregationAttr.'`');
|
||||
$oFctExpr = new FunctionExpression(strtoupper($sAgregationFunction), array($oAttrExpr));
|
||||
$sFctVar = '_itop_'.$sAgregationFunction.'_';
|
||||
$aFunctions = array($sFctVar => $oFctExpr);
|
||||
}
|
||||
if (!empty($sAgregationAttr))
|
||||
{
|
||||
$sClass = $this->m_oFilter->GetClass();
|
||||
$sAgregationAttr = MetaModel::GetLabel($sClass, $sAgregationAttr);
|
||||
}
|
||||
$iLimit = 0;
|
||||
if (isset($aExtraParams['limit']))
|
||||
{
|
||||
$iLimit = intval($aExtraParams['limit']);
|
||||
}
|
||||
if (isset($aExtraParams['order_direction']))
|
||||
{
|
||||
$aOrderBy = array($sFctVar => ($aExtraParams['order_direction'] === 'asc'));
|
||||
}
|
||||
|
||||
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true, $aFunctions, $aOrderBy, $iLimit);
|
||||
$this->MakeGroupByQuery($aExtraParams, $oGroupByExp, $sGroupByLabel, $aGroupBy, $sAggregationFunction, $sFctVar, $sAggregationAttr, $sSql);
|
||||
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
|
||||
@@ -527,7 +463,7 @@ class DisplayBlock
|
||||
}
|
||||
$aAttribs =array(
|
||||
'group' => array('label' => $sGroupByLabel, 'description' => ''),
|
||||
'value' => array('label'=> Dict::S('UI:GroupBy:'.$sAgregationFunction), 'description' => Dict::Format('UI:GroupBy:'.$sAgregationFunction.'+', $sAgregationAttr))
|
||||
'value' => array('label'=> Dict::S('UI:GroupBy:'.$sAggregationFunction), 'description' => Dict::Format('UI:GroupBy:'.$sAggregationFunction.'+', $sAggregationAttr))
|
||||
);
|
||||
$sFormat = isset($aExtraParams['format']) ? $aExtraParams['format'] : 'UI:Pagination:HeaderNoSelection';
|
||||
$sHtml .= $oPage->GetP(Dict::Format($sFormat, $iTotalCount));
|
||||
@@ -658,7 +594,7 @@ class DisplayBlock
|
||||
}
|
||||
if (count($aAuthorizedClasses) > 0)
|
||||
{
|
||||
if($this->m_oSet->Count() > 0)
|
||||
if($this->m_oSet->CountWithLimit(1) > 0)
|
||||
{
|
||||
$sHtml .= cmdbAbstractObject::GetDisplayExtendedSet($oPage, $this->m_oSet, $aExtraParams);
|
||||
}
|
||||
@@ -677,7 +613,7 @@ class DisplayBlock
|
||||
else
|
||||
{
|
||||
// The list is made of only 1 class of objects, actions on the list are possible
|
||||
if ( ($this->m_oSet->Count()> 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) )
|
||||
if ( ($this->m_oSet->CountWithLimit(1)> 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) )
|
||||
{
|
||||
$sHtml .= cmdbAbstractObject::GetDisplaySet($oPage, $this->m_oSet, $aExtraParams);
|
||||
}
|
||||
@@ -732,7 +668,7 @@ class DisplayBlock
|
||||
case 'links':
|
||||
//$bDashboardMode = isset($aExtraParams['dashboard']) ? ($aExtraParams['dashboard'] == 'true') : false;
|
||||
//$bSelectMode = isset($aExtraParams['select']) ? ($aExtraParams['select'] == 'true') : false;
|
||||
if ( ($this->m_oSet->Count()> 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) )
|
||||
if ( ($this->m_oSet->CountWithLimit(1) > 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) )
|
||||
{
|
||||
//$sLinkage = isset($aExtraParams['linkage']) ? $aExtraParams['linkage'] : '';
|
||||
$sHtml .= cmdbAbstractObject::GetDisplaySet($oPage, $this->m_oSet, $aExtraParams);
|
||||
@@ -748,8 +684,6 @@ class DisplayBlock
|
||||
{
|
||||
if ((UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sParams = $oAppContext->GetForLink();
|
||||
$sDefaults = '';
|
||||
if (isset($this->m_aParams['default']))
|
||||
{
|
||||
@@ -777,7 +711,6 @@ class DisplayBlock
|
||||
$bContextFilter = isset($aExtraParams['context_filter']) ? isset($aExtraParams['context_filter']) != 0 : false;
|
||||
if ($bContextFilter)
|
||||
{
|
||||
$aFilterCodes = array_keys(MetaModel::GetClassFilterDefs($this->m_oFilter->GetClass()));
|
||||
foreach($oAppContext->GetNames() as $sFilterCode)
|
||||
{
|
||||
$sContextParamValue = $oAppContext->GetCurrentValue($sFilterCode, null);
|
||||
@@ -821,7 +754,6 @@ class DisplayBlock
|
||||
$bContextFilter = isset($aExtraParams['context_filter']) ? isset($aExtraParams['context_filter']) != 0 : false;
|
||||
if ($bContextFilter)
|
||||
{
|
||||
$aFilterCodes = array_keys(MetaModel::GetClassFilterDefs($this->m_oFilter->GetClass()));
|
||||
foreach($oAppContext->GetNames() as $sFilterCode)
|
||||
{
|
||||
$sContextParamValue = $oAppContext->GetCurrentValue($sFilterCode, null);
|
||||
@@ -847,9 +779,15 @@ class DisplayBlock
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sStateAttrCode);
|
||||
|
||||
// Generate one count + group by query [#1330]
|
||||
$oGroupByExpr = Expression::FromOQL($sClass.'.'.$sStateAttrCode);
|
||||
$sClassAlias = $this->m_oFilter->GetClassAlias();
|
||||
$oGroupByExpr = Expression::FromOQL($sClassAlias.'.'.$sStateAttrCode);
|
||||
$aGroupBy = array('group1' => $oGroupByExpr);
|
||||
$sCountGroupByQuery = $this->m_oFilter->MakeGroupByQuery(array(), $aGroupBy, false);
|
||||
$oGroupBySearch = $this->m_oFilter->DeepClone();
|
||||
if (isset($this->m_bShowObsoleteData))
|
||||
{
|
||||
$oGroupBySearch->SetShowObsoleteData($this->m_bShowObsoleteData);
|
||||
}
|
||||
$sCountGroupByQuery = $oGroupBySearch->MakeGroupByQuery(array(), $aGroupBy, false);
|
||||
$aCountGroupByResults = CMDBSource::QueryToArray($sCountGroupByQuery);
|
||||
$aCountsQueryResults = array();
|
||||
foreach ($aCountGroupByResults as $aCountGroupBySingleResult)
|
||||
@@ -873,6 +811,10 @@ class DisplayBlock
|
||||
{
|
||||
$oSingleGroupByValueFilter = $this->m_oFilter->DeepClone();
|
||||
$oSingleGroupByValueFilter->AddCondition($sStateAttrCode, $sStateValue, '=');
|
||||
if (isset($this->m_bShowObsoleteData))
|
||||
{
|
||||
$oSingleGroupByValueFilter->SetShowObsoleteData($this->m_bShowObsoleteData);
|
||||
}
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot()
|
||||
.'pages/UI.php?operation=search&'.$oAppContext->GetForLink()
|
||||
.'&filter='.urlencode($oSingleGroupByValueFilter->serialize());
|
||||
@@ -916,36 +858,6 @@ class DisplayBlock
|
||||
}
|
||||
$sAjaxLink = utils::GetAbsoluteUrlAppRoot().'webservices/export.php';
|
||||
|
||||
/*
|
||||
$sCSVData = cmdbAbstractObject::GetSetAsCSV($this->m_oSet, array('fields_advanced' => $bAdvancedMode));
|
||||
$sCharset = MetaModel::GetConfig()->Get('csv_file_default_charset');
|
||||
if ($sCharset == 'UTF-8')
|
||||
{
|
||||
$bLostChars = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sConverted = @iconv('UTF-8', $sCharset, $sCSVData);
|
||||
$sRestored = @iconv($sCharset, 'UTF-8', $sConverted);
|
||||
$bLostChars = ($sRestored != $sCSVData);
|
||||
}
|
||||
|
||||
if ($bLostChars)
|
||||
{
|
||||
$sCharsetNotice = " <span id=\"csv_charset_issue\">";
|
||||
$sCharsetNotice .= '<img src="../images/error.png" style="vertical-align:middle"/>';
|
||||
$sCharsetNotice .= "</span>";
|
||||
|
||||
$sTip = "<p>".htmlentities(Dict::S('UI:CSVExport:LostChars'), ENT_QUOTES, 'UTF-8')."</p>";
|
||||
$sTip .= "<p>".htmlentities(Dict::Format('UI:CSVExport:LostChars+', $sCharset), ENT_QUOTES, 'UTF-8')."</p>";
|
||||
$oPage->add_ready_script("$('#csv_charset_issue').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
|
||||
}
|
||||
else
|
||||
{
|
||||
$sCharsetNotice = '';
|
||||
}
|
||||
|
||||
*/
|
||||
$sCharsetNotice = false;
|
||||
$sHtml .= "<div>";
|
||||
$sHtml .= '<table style="width:100%" class="transparent">';
|
||||
@@ -1001,19 +913,19 @@ class DisplayBlock
|
||||
$sFilter = $this->m_oFilter->serialize();
|
||||
$oContext = new ApplicationContext();
|
||||
$sContextParam = $oContext->GetForLink();
|
||||
$sAgregationFunction = isset($aExtraParams['agregation_function']) ? $aExtraParams['agregation_function'] : '';
|
||||
$sAgregationAttr = isset($aExtraParams['agregation_attribute']) ? $aExtraParams['agregation_attribute'] : '';
|
||||
$sAggregationFunction = isset($aExtraParams['aggregation_function']) ? $aExtraParams['aggregation_function'] : '';
|
||||
$sAggregationAttr = isset($aExtraParams['aggregation_attribute']) ? $aExtraParams['aggregation_attribute'] : '';
|
||||
$sLimit = isset($aExtraParams['limit']) ? $aExtraParams['limit'] : '';
|
||||
$sOrderBy = isset($aExtraParams['order_by']) ? $aExtraParams['order_by'] : '';
|
||||
$sOrderDirection = isset($aExtraParams['order_direction']) ? $aExtraParams['order_direction'] : '';
|
||||
|
||||
if (isset($aExtraParams['group_by_label']))
|
||||
{
|
||||
$sUrl = json_encode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$sGroupBy{$sGroupByExpr}¶ms[group_by_label]={$aExtraParams['group_by_label']}¶ms[chart_type]=$sChartType¶ms[currentId]=$sId{$iChartCounter}¶ms[order_direction]=$sOrderDirection¶ms[order_by]=$sOrderBy¶ms[limit]=$sLimit¶ms[agregation_function]=$sAgregationFunction¶ms[agregation_attribute]=$sAgregationAttr&id=$sId{$iChartCounter}&filter=".urlencode($sFilter).'&'.$sContextParam);
|
||||
$sUrl = json_encode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$sGroupBy{$sGroupByExpr}¶ms[group_by_label]={$aExtraParams['group_by_label']}¶ms[chart_type]=$sChartType¶ms[currentId]=$sId{$iChartCounter}¶ms[order_direction]=$sOrderDirection¶ms[order_by]=$sOrderBy¶ms[limit]=$sLimit¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sId{$iChartCounter}&filter=".urlencode($sFilter).'&'.$sContextParam);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sUrl = json_encode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$sGroupBy{$sGroupByExpr}¶ms[chart_type]=$sChartType¶ms[currentId]=$sId{$iChartCounter}¶ms[order_direction]=$sOrderDirection¶ms[order_by]=$sOrderBy¶ms[limit]=$sLimit¶ms[agregation_function]=$sAgregationFunction¶ms[agregation_attribute]=$sAgregationAttr&id=$sId{$iChartCounter}&filter=".urlencode($sFilter).'&'.$sContextParam);
|
||||
$sUrl = json_encode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$sGroupBy{$sGroupByExpr}¶ms[chart_type]=$sChartType¶ms[currentId]=$sId{$iChartCounter}¶ms[order_direction]=$sOrderDirection¶ms[order_by]=$sOrderBy¶ms[limit]=$sLimit¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sId{$iChartCounter}&filter=".urlencode($sFilter).'&'.$sContextParam);
|
||||
}
|
||||
|
||||
$oPage->add_ready_script(
|
||||
@@ -1032,47 +944,7 @@ EOF
|
||||
|
||||
if (isset($aExtraParams['group_by']))
|
||||
{
|
||||
$sAlias = $this->m_oFilter->GetClassAlias();
|
||||
if (isset($aExtraParams['group_by_label']))
|
||||
{
|
||||
$oGroupByExp = Expression::FromOQL($aExtraParams['group_by']);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Backward compatibility: group_by is simply a field id
|
||||
$oGroupByExp = new FieldExpression($aExtraParams['group_by'], $sAlias);
|
||||
}
|
||||
|
||||
$aGroupBy = array();
|
||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||
$aQueryParams = array();
|
||||
if (isset($aExtraParams['query_params']))
|
||||
{
|
||||
$aQueryParams = $aExtraParams['query_params'];
|
||||
}
|
||||
$aFunctions = array();
|
||||
$aOrderBy = array();
|
||||
$sFctVar = '_itop_count_';
|
||||
if (isset($aExtraParams['agregation_function']) && !empty($aExtraParams['agregation_attribute']))
|
||||
{
|
||||
$sAgregationFunction = $aExtraParams['agregation_function'];
|
||||
$sAgregationAttr = $aExtraParams['agregation_attribute'];
|
||||
$oAttrExpr = Expression::FromOQL('`'.$sAlias.'`.`'.$sAgregationAttr.'`');
|
||||
$oFctExpr = new FunctionExpression(strtoupper($sAgregationFunction), array($oAttrExpr));
|
||||
$sFctVar = '_itop_'.$sAgregationFunction.'_';
|
||||
$aFunctions = array($sFctVar => $oFctExpr);
|
||||
}
|
||||
$iLimit = 0;
|
||||
if (isset($aExtraParams['limit']))
|
||||
{
|
||||
$iLimit = intval($aExtraParams['limit']);
|
||||
}
|
||||
if (isset($aExtraParams['order_direction']))
|
||||
{
|
||||
$aOrderBy = array($sFctVar => ($aExtraParams['order_direction'] === 'asc'));
|
||||
}
|
||||
|
||||
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true, $aFunctions, $aOrderBy, $iLimit);
|
||||
$this->MakeGroupByQuery($aExtraParams, $oGroupByExp, $sGroupByLabel, $aGroupBy, $sAggregationFunction, $sFctVar, $sAggregationAttr, $sSql);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
$oContext = new ApplicationContext();
|
||||
$sContextParam = $oContext->GetForLink();
|
||||
@@ -1109,7 +981,6 @@ EOF
|
||||
$sJSNames = json_encode($aNames);
|
||||
|
||||
$sJson = json_encode($aValues);
|
||||
$sJSCount = json_encode(Dict::S('UI:GroupBy:Count'));
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
var chart = c3.generate({
|
||||
@@ -1185,6 +1056,7 @@ var chart = c3.generate({
|
||||
var aURLs = $sJSURLs;
|
||||
window.location.href= aURLs[d.index];
|
||||
},
|
||||
order: null,
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
@@ -1327,7 +1199,7 @@ EOF
|
||||
// In all other cases, just add the condition directly
|
||||
if (!$bConditionAdded)
|
||||
{
|
||||
$this->m_oFilter->AddCondition($sFilterCode, $condition, null, $bParseSearchString); // Use the default 'loose' operator
|
||||
$this->m_oFilter->AddCondition($sFilterCode, $condition, null); // Use the default 'loose' operator
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1348,6 +1220,97 @@ EOF
|
||||
{
|
||||
return $this->m_oSet->Count();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $aExtraParams
|
||||
* @param $oGroupByExp
|
||||
* @param $sGroupByLabel
|
||||
* @param $aGroupBy
|
||||
* @param $sAggregationFunction
|
||||
* @param $sFctVar
|
||||
* @param $sAggregationAttr
|
||||
* @param $sSql
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function MakeGroupByQuery(&$aExtraParams, &$oGroupByExp, &$sGroupByLabel, &$aGroupBy, &$sAggregationFunction, &$sFctVar, &$sAggregationAttr, &$sSql)
|
||||
{
|
||||
$sAlias = $this->m_oFilter->GetClassAlias();
|
||||
if (isset($aExtraParams['group_by_label']))
|
||||
{
|
||||
$oGroupByExp = Expression::FromOQL($aExtraParams['group_by']);
|
||||
$sGroupByLabel = $aExtraParams['group_by_label'];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Backward compatibility: group_by is simply a field id
|
||||
$oGroupByExp = new FieldExpression($aExtraParams['group_by'], $sAlias);
|
||||
$sGroupByLabel = MetaModel::GetLabel($this->m_oFilter->GetClass(), $aExtraParams['group_by']);
|
||||
}
|
||||
|
||||
// Security filtering
|
||||
$aFields = $oGroupByExp->ListRequiredFields();
|
||||
foreach($aFields as $sFieldAlias)
|
||||
{
|
||||
$aMatches = array();
|
||||
if (preg_match('/^([^.]+)\\.([^.]+)$/', $sFieldAlias, $aMatches))
|
||||
{
|
||||
$sFieldClass = $this->m_oFilter->GetClassName($aMatches[1]);
|
||||
$oAttDef = MetaModel::GetAttributeDef($sFieldClass, $aMatches[2]);
|
||||
if ($oAttDef instanceof AttributeOneWayPassword)
|
||||
{
|
||||
throw new Exception('Grouping on password fields is not supported.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$aGroupBy = array();
|
||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||
$aQueryParams = array();
|
||||
if (isset($aExtraParams['query_params']))
|
||||
{
|
||||
$aQueryParams = $aExtraParams['query_params'];
|
||||
}
|
||||
$aFunctions = array();
|
||||
$sAggregationFunction = 'count';
|
||||
$sFctVar = '_itop_count_';
|
||||
$sAggregationAttr = '';
|
||||
if (isset($aExtraParams['aggregation_function']) && !empty($aExtraParams['aggregation_attribute']))
|
||||
{
|
||||
$sAggregationFunction = $aExtraParams['aggregation_function'];
|
||||
$sAggregationAttr = $aExtraParams['aggregation_attribute'];
|
||||
$oAttrExpr = Expression::FromOQL('`'.$sAlias.'`.`'.$sAggregationAttr.'`');
|
||||
$oFctExpr = new FunctionExpression(strtoupper($sAggregationFunction), array($oAttrExpr));
|
||||
$sFctVar = '_itop_'.$sAggregationFunction.'_';
|
||||
$aFunctions = array($sFctVar => $oFctExpr);
|
||||
}
|
||||
|
||||
if (!empty($sAggregationAttr))
|
||||
{
|
||||
$sClass = $this->m_oFilter->GetClass();
|
||||
$sAggregationAttr = MetaModel::GetLabel($sClass, $sAggregationAttr);
|
||||
}
|
||||
$iLimit = 0;
|
||||
if (isset($aExtraParams['limit']))
|
||||
{
|
||||
$iLimit = intval($aExtraParams['limit']);
|
||||
}
|
||||
$aOrderBy = array();
|
||||
if (isset($aExtraParams['order_direction']) && isset($aExtraParams['order_by']))
|
||||
{
|
||||
switch ($aExtraParams['order_by'])
|
||||
{
|
||||
case 'attribute':
|
||||
$aOrderBy = array('grouped_by_1' => ($aExtraParams['order_direction'] === 'asc'));
|
||||
break;
|
||||
case 'function':
|
||||
$aOrderBy = array($sFctVar => ($aExtraParams['order_direction'] === 'asc'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true, $aFunctions, $aOrderBy, $iLimit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1536,7 +1499,6 @@ class MenuBlock extends DisplayBlock
|
||||
$oReflectionClass = new ReflectionClass($sClass);
|
||||
$oSet = new CMDBObjectSet($this->m_oFilter);
|
||||
$sFilter = $this->m_oFilter->serialize();
|
||||
$sFilterDesc = $this->m_oFilter->ToOql(true);
|
||||
$aActions = array();
|
||||
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($sClass);
|
||||
$sRootUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
@@ -1655,6 +1617,7 @@ class MenuBlock extends DisplayBlock
|
||||
if ($bLocked && $bRawModifiedAllowed)
|
||||
{
|
||||
// Add a special menu to kill the lock, but only to allowed users who can also modify this object
|
||||
/** @var array $aAllowedProfiles */
|
||||
$aAllowedProfiles = MetaModel::GetConfig()->Get('concurrent_lock_override_profiles');
|
||||
$bCanKill = false;
|
||||
|
||||
@@ -1723,7 +1686,6 @@ class MenuBlock extends DisplayBlock
|
||||
$sTargetAttr = $aExtraParams['target_attr'];
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sTargetAttr);
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$bIsDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, $oSet);
|
||||
if ($bIsModifyAllowed) { $aActions['UI:Menu:Add'] = array ('label' => Dict::S('UI:Menu:Add'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&addObjects=true{$sContext}") + $aActionParams; }
|
||||
if ($bIsBulkModifyAllowed) { $aActions['UI:Menu:Manage'] = array ('label' => Dict::S('UI:Menu:Manage'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id{$sContext}") + $aActionParams; }
|
||||
//if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Remove All...', 'url' => "#") + $aActionParams; }
|
||||
@@ -1740,7 +1702,7 @@ class MenuBlock extends DisplayBlock
|
||||
// Do not perform time consuming computations if there are too may objects in the list
|
||||
$iLimit = MetaModel::GetConfig()->Get('complex_actions_limit');
|
||||
|
||||
if ((count($aStates) > 0) && (($iLimit == 0) || ($oSet->Count() < $iLimit)))
|
||||
if ((count($aStates) > 0) && (($iLimit == 0) || ($oSet->CountWithLimit($iLimit + 1) < $iLimit)))
|
||||
{
|
||||
// Life cycle actions may be available... if all objects are in the same state
|
||||
//
|
||||
@@ -1814,7 +1776,8 @@ class MenuBlock extends DisplayBlock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$param = null;
|
||||
$iMenuId = null;
|
||||
// New extensions based on iPopupMenuItem interface
|
||||
switch($this->m_sStyle)
|
||||
{
|
||||
@@ -1847,11 +1810,7 @@ class MenuBlock extends DisplayBlock
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aShortcutActions = array();
|
||||
}
|
||||
|
||||
|
||||
if (!$oPage->IsPrintableVersion())
|
||||
{
|
||||
if (count($aFavoriteActions) > 0)
|
||||
@@ -1867,7 +1826,7 @@ class MenuBlock extends DisplayBlock
|
||||
|
||||
if ($this->m_sStyle == 'details')
|
||||
{
|
||||
$sSearchAction = "window.location=\"{$sRootUrl}pages/UI.php?operation=search_form&class=$sClass{$sContext}\"";
|
||||
$sSearchAction = "window.location=\"{$sRootUrl}pages/UI.php?operation=search_form&do_search=0&class=$sClass{$sContext}\"";
|
||||
$sHtml .= "<div class=\"actions_button icon_actions_button\" title=\"".htmlentities(Dict::Format('UI:SearchFor_Class', MetaModel::GetName($sClass)), ENT_QUOTES, 'UTF-8')."\"><span class=\"search-button fa fa-search\" onclick='$sSearchAction'></span></div>";
|
||||
}
|
||||
|
||||
@@ -1897,7 +1856,7 @@ class MenuBlock extends DisplayBlock
|
||||
|
||||
/**
|
||||
* Appends a menu separator to the current list of actions
|
||||
* @param Hash $aActions The current actions list
|
||||
* @param array $aActions The current actions list
|
||||
* @return void
|
||||
*/
|
||||
protected function AddMenuSeparator(&$aActions)
|
||||
|
||||
@@ -942,10 +942,7 @@ EOF
|
||||
// Render the text of the global search form
|
||||
$sText = htmlentities(utils::ReadParam('text', '', false, 'raw_data'), ENT_QUOTES, 'UTF-8');
|
||||
$sOnClick = " onclick=\"if ($('#global-search-input').val() != '') { $('#global-search form').submit(); } \"";
|
||||
$sDefaultPlaceHolder = '';
|
||||
if (empty($sText)) {
|
||||
$sDefaultPlaceHolder = Dict::S("UI:YourSearch");
|
||||
}
|
||||
$sDefaultPlaceHolder = Dict::S("UI:YourSearch");
|
||||
|
||||
if ($this->IsPrintableVersion()) {
|
||||
$sHtml .= ' <!-- Beginning of page content -->';
|
||||
|
||||
@@ -195,7 +195,7 @@ class LoginWebPage extends NiceWebPage
|
||||
*/
|
||||
public function ForgotPwdLink()
|
||||
{
|
||||
$sUrl = '?loginop=forgot_pwd';
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot() . 'pages/UI.php?loginop=forgot_pwd';
|
||||
$sHtml = "<a href=\"$sUrl\" target=\"_blank\">".Dict::S('UI:Login:ForgotPwd')."</a>";
|
||||
return $sHtml;
|
||||
}
|
||||
@@ -259,10 +259,6 @@ class LoginWebPage extends NiceWebPage
|
||||
$oEmail = new Email();
|
||||
$oEmail->SetRecipientTO($sTo);
|
||||
$sFrom = MetaModel::GetConfig()->Get('forgot_password_from');
|
||||
if ($sFrom == '')
|
||||
{
|
||||
$sFrom = $sTo;
|
||||
}
|
||||
$oEmail->SetRecipientFrom($sFrom);
|
||||
$oEmail->SetSubject(Dict::S('UI:ResetPwd-EmailSubject'));
|
||||
$sResetUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?loginop=reset_pwd&auth_user='.urlencode($oUser->Get('login')).'&token='.urlencode($sToken);
|
||||
@@ -704,26 +700,36 @@ EOF
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the user is already authentified, if yes, then performs some additional validations:
|
||||
* - if $bMustBeAdmin is true, then the user must be an administrator, otherwise an error is displayed
|
||||
* - if $bIsAllowedToPortalUsers is false and the user has only access to the portal, then the user is redirected to the portal
|
||||
* - if $bIsAllowedToPortalUsers is false and the user has only access to the portal, then the user is redirected
|
||||
* to the portal
|
||||
*
|
||||
* @param bool $bMustBeAdmin Whether or not the user must be an admin to access the current page
|
||||
* @param bool $bIsAllowedToPortalUsers Whether or not the current page is considered as part of the portal
|
||||
* @param int iOnExit What action to take if the user is not logged on (one of the class constants EXIT_...)
|
||||
*
|
||||
* @return int|mixed|string
|
||||
* @throws \Exception
|
||||
*/
|
||||
static function DoLogin($bMustBeAdmin = false, $bIsAllowedToPortalUsers = false, $iOnExit = self::EXIT_PROMPT)
|
||||
{
|
||||
$sRequestedPortalId = $bIsAllowedToPortalUsers ? 'legacy_portal' : 'backoffice';
|
||||
return self::DoLoginEx($sRequestedPortalId, $bMustBeAdmin, $iOnExit);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the user is already authentified, if yes, then performs some additional validations to redirect towards the desired "portal"
|
||||
* 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_...)
|
||||
*
|
||||
* @return int|mixed|string
|
||||
* @throws \Exception
|
||||
*/
|
||||
static function DoLoginEx($sRequestedPortalId = null, $bMustBeAdmin = false, $iOnExit = self::EXIT_PROMPT)
|
||||
{
|
||||
|
||||
@@ -636,7 +636,10 @@ abstract class MenuNode
|
||||
}
|
||||
if ($this->m_aEnableActions[$index] != null)
|
||||
{
|
||||
// Menus access rights ignore the archive mode
|
||||
utils::PushArchiveMode(false);
|
||||
$iResult = UserRights::IsActionAllowed($sClass, $this->m_aEnableActions[$index]);
|
||||
utils::PopArchiveMode();
|
||||
if (!($iResult & $this->m_aEnableActionResults[$index]))
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -54,7 +54,8 @@ abstract class Query extends cmdbAbstractObject
|
||||
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'fields')); // Criteria of the std search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
MetaModel::Init_SetZListItems('default_search', array('name', 'description')); // Criteria of the default search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +84,6 @@ class QueryOQL extends Query
|
||||
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'fields', 'oql')); // Criteria of the std search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
|
||||
|
||||
@@ -101,7 +101,7 @@ class UIExtKeyWidget
|
||||
/**
|
||||
* Get the HTML fragment corresponding to the ext key editing widget
|
||||
* @param WebPage $oP The web page used for all the output
|
||||
* @param Hash $aArgs Extra context arguments
|
||||
* @param array $aArgs Extra context arguments
|
||||
* @return string The HTML fragment to be inserted into the page
|
||||
*/
|
||||
public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, DBObjectset $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = null, $sDisplayStyle = 'select', $bSearchMultiple = true)
|
||||
@@ -145,7 +145,12 @@ class UIExtKeyWidget
|
||||
throw new Exception('Implementation: null value for allowed values definition');
|
||||
}
|
||||
$oAllowedValues->SetShowObsoleteData(utils::ShowObsoleteData());
|
||||
if ($oAllowedValues->Count() < $iMaxComboLength)
|
||||
// Don't automatically launch the search if the table is huge
|
||||
$bDoSearch = !utils::IsHighCardinality($this->sTargetClass);
|
||||
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
|
||||
|
||||
// We just need to compare the number of entries with MaxComboLength, so no need to get the real count.
|
||||
if (!$oAllowedValues->CountExceeds($iMaxComboLength))
|
||||
{
|
||||
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
|
||||
switch($sDisplayStyle)
|
||||
@@ -170,7 +175,6 @@ class UIExtKeyWidget
|
||||
case 'select':
|
||||
case 'list':
|
||||
default:
|
||||
$sSelectMode = 'true';
|
||||
|
||||
$sHelpText = ''; //$this->oAttDef->GetHelpOnEdition();
|
||||
$sHTMLValue .= "<div class=\"field_select_wrapper\">\n";
|
||||
@@ -203,13 +207,13 @@ class UIExtKeyWidget
|
||||
if (($oAllowedValues->Count() == 1) && ($bMandatory == 'true') )
|
||||
{
|
||||
// When there is only once choice, select it by default
|
||||
$sSelected = ' selected';
|
||||
$sSelected = 'selected';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSelected = (is_array($value) && in_array($key, $value)) || ($value == $key) ? ' selected' : '';
|
||||
$sSelected = (is_array($value) && in_array($key, $value)) || ($value == $key) ? 'selected' : '';
|
||||
}
|
||||
$sHTMLValue .= "<option value=\"$key\"$sSelected>$display_value</option>\n";
|
||||
$sHTMLValue .= "<option value=\"$key\" $sSelected>$display_value</option>\n";
|
||||
}
|
||||
$sHTMLValue .= "</select>\n";
|
||||
$sHTMLValue .= "</div>\n";
|
||||
@@ -229,7 +233,7 @@ class UIExtKeyWidget
|
||||
}
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode);
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
|
||||
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
|
||||
$('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } );
|
||||
$('#$this->iId').bind('change', function() { $(this).trigger('extkeychange') } );
|
||||
@@ -241,8 +245,6 @@ EOF
|
||||
else
|
||||
{
|
||||
// Too many choices, use an autocomplete
|
||||
$sSelectMode = 'false';
|
||||
|
||||
// Check that the given value is allowed
|
||||
$oSearch = $oAllowedValues->GetFilter();
|
||||
$oSearch->AddCondition('id', $value);
|
||||
@@ -261,10 +263,9 @@ EOF
|
||||
$sDisplayValue = $this->GetObjectName($value);
|
||||
}
|
||||
$iMinChars = isset($aArgs['iMinChars']) ? $aArgs['iMinChars'] : 3; //@@@ $this->oAttDef->GetMinAutoCompleteChars();
|
||||
$iFieldSize = isset($aArgs['iFieldSize']) ? $aArgs['iFieldSize'] : 20; //@@@ $this->oAttDef->GetMaxSize();
|
||||
|
||||
// the input for the auto-complete
|
||||
$sHTMLValue .= "<input class=\"field_autocomplete\" count=\"".$oAllowedValues->Count()."\" type=\"text\" id=\"label_$this->iId\" value=\"$sDisplayValue\"/>";
|
||||
$sHTMLValue .= "<input class=\"field_autocomplete\" type=\"text\" id=\"label_$this->iId\" value=\"$sDisplayValue\"/>";
|
||||
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif?t=".utils::GetCacheBusterTimestamp()."\" onClick=\"oACWidget_{$this->iId}.Search();\"/></span>";
|
||||
|
||||
// another hidden input to store & pass the object's Id
|
||||
@@ -274,7 +275,7 @@ EOF
|
||||
// Scripts to start the autocomplete and bind some events to it
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode);
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
|
||||
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
|
||||
$('#label_$this->iId').autocomplete(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { scroll:true, minChars:{$iMinChars}, autoFill:false, matchContains:true, mustMatch: true, keyHolder:'#{$this->iId}', extraParams:{operation:'ac_extkey', sTargetClass:'{$this->sTargetClass}',sFilter:'$sFilter',bSearchMode:$JSSearchMode, json: function() { return $sWizHelperJSON; } }});
|
||||
$('#label_$this->iId').keyup(function() { if ($(this).val() == '') { $('#$this->iId').val(''); } } ); // Useful for search forms: empty value in the "label", means no value, immediatly !
|
||||
@@ -376,9 +377,13 @@ EOF
|
||||
|
||||
/**
|
||||
* Search for objects to be selected
|
||||
*
|
||||
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
|
||||
* @param $sFilter
|
||||
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of m_sRemoteClass
|
||||
* @param Array $aAlreadyLinkedIds List of IDs of objects of "remote" class already linked, to be filtered out of the search
|
||||
* @param null $oObj
|
||||
*
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function SearchObjectsToSelect(WebPage $oP, $sFilter, $sRemoteClass = '', $oObj = null)
|
||||
{
|
||||
@@ -401,14 +406,20 @@ EOF
|
||||
$oBlock->Display($oP, $this->iId.'_results', array('this' => $oObj, 'cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single', 'table_id' => 'select_'.$this->sAttCode)); // Don't display the 'Actions' menu on the results
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for objects to be selected
|
||||
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
|
||||
* @param string $sFilter The OQL expression used to define/limit limit the scope of possible values
|
||||
* @param DBObject $oObj The current object for the OQL context
|
||||
* @param string $sContains The text of the autocomplete to filter the results
|
||||
*/
|
||||
public function AutoComplete(WebPage $oP, $sFilter, $oObj = null, $sContains, $sOutputFormat = self::ENUM_OUTPUT_FORMAT_CSV)
|
||||
/**
|
||||
* Search for objects to be selected
|
||||
*
|
||||
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
|
||||
* @param string $sFilter The OQL expression used to define/limit limit the scope of possible values
|
||||
* @param DBObject $oObj The current object for the OQL context
|
||||
* @param string $sContains The text of the autocomplete to filter the results
|
||||
* @param string $sOutputFormat
|
||||
* @param null $sOperation for the values @see ValueSetObjects->LoadValues()
|
||||
*
|
||||
* @throws CoreException
|
||||
* @throws OQLException
|
||||
*/
|
||||
public function AutoComplete(WebPage $oP, $sFilter, $oObj = null, $sContains, $sOutputFormat = self::ENUM_OUTPUT_FORMAT_CSV, $sOperation = null)
|
||||
{
|
||||
if (is_null($sFilter))
|
||||
{
|
||||
@@ -419,15 +430,60 @@ EOF
|
||||
$iCurrentExtKeyId = (is_null($oObj) || $this->sAttCode === '') ? 0 : $oObj->Get($this->sAttCode);
|
||||
|
||||
$oValuesSet = new ValueSetObjects($sFilter, 'friendlyname'); // Bypass GetName() to avoid the encoding by htmlentities
|
||||
$iMax = 150;
|
||||
$oValuesSet->SetLimit($iMax);
|
||||
$oValuesSet->SetSort(false);
|
||||
$oValuesSet->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$aValues = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains);
|
||||
|
||||
if (empty($sOperation) || 'equals_start_with' == $sOperation)
|
||||
{
|
||||
$aValues = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'equals');
|
||||
asort($aValues);
|
||||
|
||||
$iMax -= count($aValues);
|
||||
if ($iMax > 0 ) {
|
||||
$oValuesSet->SetLimit($iMax);
|
||||
$aValuesStartWith = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'start_with');
|
||||
asort($aValuesStartWith);
|
||||
foreach ($aValuesStartWith as $sKey => $sFriendlyName) {
|
||||
if (!isset($aValues[$sKey])) {
|
||||
$aValues[$sKey] = $sFriendlyName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aValues = array();
|
||||
}
|
||||
|
||||
$iMax -= count($aValues);
|
||||
if ($iMax > 0 && (empty($sOperation) || 'contains' == $sOperation))
|
||||
{
|
||||
$oValuesSet->SetLimit($iMax);
|
||||
$aValuesContains = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'contains');
|
||||
asort($aValuesContains);
|
||||
foreach($aValuesContains as $sKey => $sFriendlyName)
|
||||
{
|
||||
if (!isset($aValues[$sKey]))
|
||||
{
|
||||
$aValues[$sKey] = $sFriendlyName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch($sOutputFormat)
|
||||
{
|
||||
case static::ENUM_OUTPUT_FORMAT_JSON:
|
||||
// Array flip to preserve values order on the label, otherwise the JS will re-order regarding the keys.
|
||||
$oP->SetContentType('application/json');
|
||||
$oP->add(json_encode(array_flip($aValues)));
|
||||
|
||||
$aJsonMap = array();
|
||||
foreach ($aValues as $sKey => $sLabel)
|
||||
{
|
||||
$aJsonMap[] = array('value' => $sKey, 'label' => $sLabel);
|
||||
}
|
||||
|
||||
$oP->SetContentType('application/json');
|
||||
$oP->add(json_encode($aJsonMap));
|
||||
break;
|
||||
|
||||
case static::ENUM_OUTPUT_FORMAT_CSV:
|
||||
@@ -461,12 +517,15 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Get the form to select a leaf class from the $this->sTargetClass (that should be abstract)
|
||||
* Note: Inspired from UILinksWidgetDirect::GetObjectCreationDialog()
|
||||
*
|
||||
* @param WebPage $oPage
|
||||
*/
|
||||
* @param WebPage $oPage
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
*/
|
||||
public function GetClassSelectionForm(WebPage $oPage)
|
||||
{
|
||||
// For security reasons: check that the "proposed" class is actually a subclass of the linked class
|
||||
@@ -568,21 +627,11 @@ EOF
|
||||
{
|
||||
throw new Exception('Implementation: null value for allowed values definition');
|
||||
}
|
||||
try
|
||||
{
|
||||
$oFilter = DBObjectSearch::FromOQL($sFilter);
|
||||
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oSet = new DBObjectSet($oFilter, array(), array('this' => $oObj, 'current_extkey_id' => $currValue));
|
||||
}
|
||||
catch(MissingQueryArgument $e)
|
||||
{
|
||||
// When used in a search form the $this parameter may be missing, in this case return all possible values...
|
||||
// TODO check if we can improve this behavior...
|
||||
$sOQL = 'SELECT '.$this->m_sTargetClass;
|
||||
$oFilter = DBObjectSearch::FromOQL($sOQL);
|
||||
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
}
|
||||
|
||||
$oFilter = DBObjectSearch::FromOQL($sFilter);
|
||||
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oSet = new DBObjectSet($oFilter, array(), array('this' => $oObj, 'current_extkey_id' => $currValue));
|
||||
|
||||
$oSet->SetShowObsoleteData(utils::ShowObsoleteData());
|
||||
|
||||
$sHKAttCode = MetaModel::IsHierarchicalClass($this->sTargetClass);
|
||||
|
||||
@@ -275,7 +275,10 @@ class UILinksWidgetDirect
|
||||
$sJSONLabels = json_encode($aLabels);
|
||||
$sJSONButtons = json_encode($aButtons);
|
||||
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
|
||||
$oPage->add_ready_script("$('#{$this->sInputid}').directlinks({class_name: '$this->sClass', att_code: '$this->sAttCode', input_name:'$sInputName', labels: $sJSONLabels, submit_to: '$sSubmitUrl', buttons: $sJSONButtons, oWizardHelper: $sWizHelper });");
|
||||
// Don't automatically launch the search if the table is huge
|
||||
$bDoSearch = !utils::IsHighCardinality($this->sLinkedClass);
|
||||
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
|
||||
$oPage->add_ready_script("$('#{$this->sInputid}').directlinks({class_name: '$this->sClass', att_code: '$this->sAttCode', input_name:'$sInputName', labels: $sJSONLabels, submit_to: '$sSubmitUrl', buttons: $sJSONButtons, oWizardHelper: $sWizHelper, do_search: $sJSDoSearch});");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,6 +340,7 @@ class UILinksWidgetDirect
|
||||
'result_list_outer_selector' => "SearchResultsToAdd_{$this->sInputid}",
|
||||
'table_id' => "add_{$this->sInputid}",
|
||||
'table_inner_id' => "ResultsToAdd_{$this->sInputid}",
|
||||
'selection_mode' => true,
|
||||
'cssCount' => "#count_{$this->sInputid}",
|
||||
'query_params' => $oFilter->GetInternalParams(),
|
||||
'hidden_criteria' => $sHiddenCriteria,
|
||||
|
||||
@@ -365,9 +365,12 @@ EOF
|
||||
}
|
||||
$sHtmlValue .= $this->DisplayFormTable($oPage, $this->m_aTableConfig, $aForm);
|
||||
$sDuplicates = ($this->m_bDuplicatesAllowed) ? 'true' : 'false';
|
||||
// Don't automatically launch the search if the table is huge
|
||||
$bDoSearch = !utils::IsHighCardinality($this->m_sRemoteClass);
|
||||
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
|
||||
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
|
||||
$oPage->add_ready_script(<<<EOF
|
||||
oWidget{$this->m_iInputId} = new LinksWidget('{$this->m_sAttCode}{$this->m_sNameSuffix}', '{$this->m_sClass}', '{$this->m_sAttCode}', '{$this->m_iInputId}', '{$this->m_sNameSuffix}', $sDuplicates, $sWizHelper, '{$this->m_sExtKeyToRemote}');
|
||||
oWidget{$this->m_iInputId} = new LinksWidget('{$this->m_sAttCode}{$this->m_sNameSuffix}', '{$this->m_sClass}', '{$this->m_sAttCode}', '{$this->m_iInputId}', '{$this->m_sNameSuffix}', $sDuplicates, $sWizHelper, '{$this->m_sExtKeyToRemote}', $sJSDoSearch);
|
||||
oWidget{$this->m_iInputId}.Init();
|
||||
EOF
|
||||
);
|
||||
|
||||
@@ -311,7 +311,7 @@ class utils
|
||||
switch($sSanitizationFilter)
|
||||
{
|
||||
case 'parameter':
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options"=>array("regexp"=>'/^[ A-Za-z0-9_=-]*$/'))); // the '=' equal character is used in serialized filters
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options"=>array("regexp"=>'/^([ A-Za-z0-9_=-]|%3D|%2B|%2F)*$/'))); // the '=', '%3D, '%2B', '%2F' characters are used in serialized filters (starting 2.5, only the url encoded versions are presents, but the "=" is kept for BC)
|
||||
break;
|
||||
|
||||
case 'field_name':
|
||||
@@ -658,27 +658,49 @@ class utils
|
||||
return str_replace($aSearch, $aReplacement, $sOldDateTimeFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Config from the current environement, or if not existing from the production env, else new Config made from scratch
|
||||
* @uses \MetaModel::GetConfig() don't forget to add the needed <code>require_once(APPROOT.'core/metamodel.class.php');</code>
|
||||
*/
|
||||
static public function GetConfig()
|
||||
{
|
||||
if (self::$oConfig == null)
|
||||
{
|
||||
self::$oConfig = MetaModel::GetConfig();
|
||||
|
||||
if (self::$oConfig == null)
|
||||
{
|
||||
$sConfigFile = self::GetConfigFilePath();
|
||||
if (file_exists($sConfigFile))
|
||||
if (!file_exists($sConfigFile))
|
||||
{
|
||||
self::$oConfig = new Config($sConfigFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
// When executing the setup, the config file may be still missing
|
||||
self::$oConfig = new Config();
|
||||
$sConfigFile = self::GetConfigFilePath('production');
|
||||
if (!file_exists($sConfigFile))
|
||||
{
|
||||
$sConfigFile = null;
|
||||
}
|
||||
}
|
||||
|
||||
self::$oConfig = new Config($sConfigFile);
|
||||
}
|
||||
}
|
||||
return self::$oConfig;
|
||||
}
|
||||
|
||||
public static function InitTimeZone() {
|
||||
$oConfig = self::GetConfig();
|
||||
$sItopTimeZone = $oConfig->Get('timezone');
|
||||
|
||||
if (!empty($sItopTimeZone))
|
||||
{
|
||||
date_default_timezone_set($sItopTimeZone);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Leave as is... up to the admin to set a value somewhere...
|
||||
// see http://php.net/manual/en/datetime.configuration.php#ini.date.timezone
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute URL to the application root path
|
||||
* @return string The absolute URL to the application root, without the first slash
|
||||
@@ -1902,4 +1924,21 @@ class utils
|
||||
}
|
||||
return COMPILATION_TIMESTAMP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given class if configured as a high cardinality class.
|
||||
*
|
||||
* @param $sClass
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function IsHighCardinality($sClass)
|
||||
{
|
||||
if (utils::GetConfig()->Get('search_manual_submit'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
$aHugeClasses = MetaModel::GetConfig()->Get('high_cardinality_classes');
|
||||
return in_array($sClass, $aHugeClasses);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -86,12 +86,20 @@ class WizardHelper
|
||||
}
|
||||
elseif($sLinkedAttDef instanceof AttributeDateTime)
|
||||
{
|
||||
$sDateClass = get_class($sLinkedAttDef);
|
||||
$sDate = $aLinkedObject[$sLinkedAttCode];
|
||||
if($sDate !== null && $sDate !== '')
|
||||
{
|
||||
$oDateTimeFormat = AttributeDateTime::GetFormat();
|
||||
$oDateTimeFormat = $sDateClass::GetFormat();
|
||||
$oDate = $oDateTimeFormat->Parse($sDate);
|
||||
$sDate = $oDate->format('Y-m-d H:i:s');
|
||||
if ($sDateClass == "AttributeDate")
|
||||
{
|
||||
$sDate = $oDate->format('Y-m-d');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDate = $oDate->format('Y-m-d H:i:s');
|
||||
}
|
||||
}
|
||||
|
||||
$oLinkedObj->Set($sLinkedAttCode, $sDate);
|
||||
|
||||
@@ -60,7 +60,7 @@ abstract class Action extends cmdbAbstractObject
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'trigger_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'description', 'status')); // 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('default_search', array('name', 'description', 'status')); // Criteria of the std search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
@@ -262,22 +262,34 @@ class ActionEmail extends ActionNotification
|
||||
{
|
||||
$sPrefix = '';
|
||||
}
|
||||
|
||||
if ($oLog)
|
||||
{
|
||||
$oLog->Set('message', $sPrefix . $sRes);
|
||||
}
|
||||
$oLog->DBUpdate();
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
if ($oLog)
|
||||
{
|
||||
$oLog->Set('message', 'Error: '.$e->getMessage());
|
||||
|
||||
try
|
||||
{
|
||||
$oLog->DBUpdate();
|
||||
}
|
||||
catch (Exception $eSecondTryUpdate)
|
||||
{
|
||||
IssueLog::Error("Failed to process email ".$oLog->GetKey()." - reason: ".$e->getMessage()."\nTrace:\n".$e->getTraceAsString());
|
||||
|
||||
$oLog->Set('message', 'Error: more details in the log for email "'.$oLog->GetKey().'"');
|
||||
$oLog->DBUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($oLog)
|
||||
{
|
||||
$oLog->DBUpdate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected function _DoExecute($oTrigger, $aContextArgs, &$oLog)
|
||||
@@ -348,7 +360,6 @@ class ActionEmail extends ActionNotification
|
||||
$sTestBody .= "</div>\n";
|
||||
$oEmail->SetBody($sTestBody, 'text/html', $sStyles);
|
||||
$oEmail->SetRecipientTO($this->Get('test_recipient'));
|
||||
$oEmail->SetRecipientFrom($this->Get('test_recipient'));
|
||||
$oEmail->SetReferences($sReference);
|
||||
$oEmail->SetMessageId($sMessageId);
|
||||
}
|
||||
|
||||
@@ -238,7 +238,20 @@ abstract class AsyncTask extends DBObject
|
||||
{
|
||||
$iRetryDelay = $this->GetRetryDelay($iErrorCode);
|
||||
IssueLog::Info('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage.' - remaining retries: '.$iRemaining.' - next retry in '.$iRetryDelay.'s');
|
||||
|
||||
if ($this->Get('event_id') != 0)
|
||||
{
|
||||
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
|
||||
$oEventLog->Set('message', "$sErrorMessage\nFailed to process async task. Remaining retries: '.$iRemaining.'. Next retry in '.$iRetryDelay.'s'");
|
||||
try
|
||||
{
|
||||
$oEventLog->DBUpdate();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$oEventLog->Set('message', "Failed to process async task. Remaining retries: '.$iRemaining.'. Next retry in '.$iRetryDelay.'s', more details in the log");
|
||||
$oEventLog->DBUpdate();
|
||||
}
|
||||
}
|
||||
$this->Set('remaining_retries', $iRemaining - 1);
|
||||
$this->Set('status', 'planned');
|
||||
$this->Set('started', null);
|
||||
@@ -247,7 +260,20 @@ abstract class AsyncTask extends DBObject
|
||||
else
|
||||
{
|
||||
IssueLog::Error('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage);
|
||||
|
||||
if ($this->Get('event_id') != 0)
|
||||
{
|
||||
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
|
||||
$oEventLog->Set('message', "$sErrorMessage\nFailed to process async task.");
|
||||
try
|
||||
{
|
||||
$oEventLog->DBUpdate();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$oEventLog->Set('message', 'Failed to process async task, more details in the log');
|
||||
$oEventLog->DBUpdate();
|
||||
}
|
||||
}
|
||||
$this->Set('status', 'error');
|
||||
$this->Set('started', null);
|
||||
$this->Set('planned', null);
|
||||
|
||||
@@ -740,10 +740,14 @@ abstract class AttributeDefinition
|
||||
|
||||
/**
|
||||
* Get various representations of the value, for insertion into a template (e.g. in Notifications)
|
||||
*
|
||||
* @param $value mixed The current value of the field
|
||||
* @param $sVerb string The verb specifying the representation of the value
|
||||
* @param $oHostObject DBObject The object
|
||||
* @param $bLocalize bool Whether or not to localize the value
|
||||
*
|
||||
* @return mixed|null|string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetForTemplate($value, $sVerb, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
@@ -852,7 +856,7 @@ abstract class AttributeDefinition
|
||||
* does nothing special, and just calls the default (loose) operator
|
||||
* @param string $sSearchText The search string to analyze for smart patterns
|
||||
* @param FieldExpression The FieldExpression representing the atttribute code in this OQL query
|
||||
* @param Hash $aParams Values of the query parameters
|
||||
* @param array $aParams Values of the query parameters
|
||||
* @return Expression The search condition to be added (AND) to the current search
|
||||
*/
|
||||
public function GetSmartConditionExpression($sSearchText, FieldExpression $oField, &$aParams)
|
||||
@@ -888,7 +892,7 @@ abstract class AttributeDefinition
|
||||
|
||||
/**
|
||||
* The part of the current attribute in the object's signature, for the supplied value
|
||||
* @param unknown $value The value of this attribute for the object
|
||||
* @param mixed $value The value of this attribute for the object
|
||||
* @return string The "signature" for this field/attribute
|
||||
*/
|
||||
public function Fingerprint($value)
|
||||
@@ -1008,7 +1012,6 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
$sObjClass = get_class($oObj);
|
||||
$sRes .= "<$sObjClass id=\"".$oObj->GetKey()."\">\n";
|
||||
// Show only relevant information (hide the external key to the current object)
|
||||
$aAttributes = array();
|
||||
foreach(MetaModel::ListAttributeDefs($sObjClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
if ($sAttCode == 'finalclass')
|
||||
@@ -1105,10 +1108,14 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
|
||||
/**
|
||||
* Get various representations of the value, for insertion into a template (e.g. in Notifications)
|
||||
*
|
||||
* @param $value mixed The current value of the field
|
||||
* @param $sVerb string The verb specifying the representation of the value
|
||||
* @param $oHostObject DBObject The object
|
||||
* @param $bLocalize bool Whether or not to localize the value
|
||||
*
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetForTemplate($value, $sVerb, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
@@ -1405,6 +1412,30 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
return $oSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $proposedValue
|
||||
* @param $oHostObj
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj){
|
||||
if($proposedValue === null)
|
||||
{
|
||||
$sLinkedClass = $this->GetLinkedClass();
|
||||
$aLinkedObjectsArray = array();
|
||||
$oSet = DBObjectSet::FromArray($sLinkedClass, $aLinkedObjectsArray);
|
||||
|
||||
return new ormLinkSet(
|
||||
get_class($oHostObj),
|
||||
$this->GetCode(),
|
||||
$oSet
|
||||
);
|
||||
}
|
||||
|
||||
return $proposedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ormLinkSet $val1
|
||||
* @param ormLinkSet $val2
|
||||
@@ -1425,7 +1456,9 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
|
||||
/**
|
||||
* Find the corresponding "link" attribute on the target class, if any
|
||||
*
|
||||
* @return null | AttributeDefinition
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetMirrorLinkAttribute()
|
||||
{
|
||||
@@ -1509,6 +1542,7 @@ class AttributeLinkedSetIndirect extends AttributeLinkedSet
|
||||
/**
|
||||
* Find the corresponding "link" attribute on the target class, if any
|
||||
* @return null | AttributeDefinition
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function GetMirrorLinkAttribute()
|
||||
{
|
||||
@@ -2403,6 +2437,7 @@ class AttributeApplicationLanguage extends AttributeString
|
||||
class AttributeFinalClass extends AttributeString
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
|
||||
public $m_sValue;
|
||||
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
@@ -2754,7 +2789,6 @@ class AttributeText extends AttributeString
|
||||
$sPattern = '/'.str_replace('/', '\/', utils::GetConfig()->Get('url_validation_pattern')).'/i';
|
||||
if (preg_match_all($sPattern, $sText, $aAllMatches, PREG_SET_ORDER /* important !*/ |PREG_OFFSET_CAPTURE /* important ! */))
|
||||
{
|
||||
$aUrls = array();
|
||||
$i = count($aAllMatches);
|
||||
// Replace the URLs by an actual hyperlink <a href="...">...</a>
|
||||
// Let's do it backwards so that the initial positions are not modified by the replacement
|
||||
@@ -3291,10 +3325,14 @@ class AttributeCaseLog extends AttributeLongText
|
||||
|
||||
/**
|
||||
* Get various representations of the value, for insertion into a template (e.g. in Notifications)
|
||||
*
|
||||
* @param $value mixed The current value of the field
|
||||
* @param $sVerb string The verb specifying the representation of the value
|
||||
* @param $oHostObject DBObject The object
|
||||
* @param $bLocalize bool Whether or not to localize the value
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetForTemplate($value, $sVerb, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
@@ -3812,7 +3850,7 @@ class AttributeEnum extends AttributeString
|
||||
$aLocalizedValues = array();
|
||||
foreach ($aRawValues as $sKey => $sValue)
|
||||
{
|
||||
$aLocalizedValues[$sKey] = Str::pure2html($this->GetValueLabel($sKey));
|
||||
$aLocalizedValues[$sKey] = $this->GetValueLabel($sKey);
|
||||
}
|
||||
return $aLocalizedValues;
|
||||
}
|
||||
@@ -4158,7 +4196,6 @@ class AttributeDateTime extends AttributeDBField
|
||||
return parent::GetForTemplate($value, $sVerb, $oHostObject, $bLocalize);
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static public function ListExpectedParams()
|
||||
@@ -4279,7 +4316,7 @@ class AttributeDateTime extends AttributeDBField
|
||||
try
|
||||
{
|
||||
$oFormat = new DateTimeFormat($this->GetInternalFormat());
|
||||
$oTrash = $oFormat->Parse($proposedValue);
|
||||
$oFormat->Parse($proposedValue);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
@@ -4294,17 +4331,10 @@ class AttributeDateTime extends AttributeDBField
|
||||
|
||||
public function ScalarToSQL($value)
|
||||
{
|
||||
if (is_null($value))
|
||||
if (empty($value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
elseif (empty($value))
|
||||
{
|
||||
// Make a valid date for MySQL. TO DO: support NULL as a literal value for fields that can be null.
|
||||
// todo: this is NOT valid in strict mode (default setting for MySQL 5.7)
|
||||
// todo: if to be kept, this should be overloaded for AttributeDate (0000-00-00)
|
||||
return '0000-00-00 00:00:00';
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
@@ -4345,7 +4375,7 @@ class AttributeDateTime extends AttributeDBField
|
||||
* does nothing special, and just calls the default (loose) operator
|
||||
* @param string $sSearchText The search string to analyze for smart patterns
|
||||
* @param FieldExpression The FieldExpression representing the atttribute code in this OQL query
|
||||
* @param Hash $aParams Values of the query parameters
|
||||
* @param array $aParams Values of the query parameters
|
||||
* @return Expression The search condition to be added (AND) to the current search
|
||||
*/
|
||||
public function GetSmartConditionExpression($sSearchText, FieldExpression $oField, &$aParams, $bParseSearchString = false)
|
||||
@@ -4388,7 +4418,6 @@ class AttributeDateTime extends AttributeDBField
|
||||
|
||||
$sParamName2 = $oField->GetParent().'_'.$oField->GetName().'_2';
|
||||
$oRightExpr = new VariableExpression($sParamName2);
|
||||
$sOperator = $this->GetBasicFilterLooseOperator();
|
||||
if ($bParseSearchString)
|
||||
{
|
||||
$aParams[$sParamName2] = $this->ParseSearchString($aMatches[2]);
|
||||
@@ -4422,7 +4451,7 @@ class AttributeDateTime extends AttributeDBField
|
||||
break;
|
||||
|
||||
default:
|
||||
$oNewCondition = parent::GetSmartConditionExpression($sSearchText, $oField, $aParams, $bParseSearchString);
|
||||
$oNewCondition = parent::GetSmartConditionExpression($sSearchText, $oField, $aParams);
|
||||
|
||||
}
|
||||
|
||||
@@ -4478,8 +4507,7 @@ class AttributeDuration extends AttributeInteger
|
||||
public static function FormatDuration($duration)
|
||||
{
|
||||
$aDuration = self::SplitDuration($duration);
|
||||
$sResult = '';
|
||||
|
||||
|
||||
if ($duration < 60)
|
||||
{
|
||||
// Less than 1 min
|
||||
@@ -4641,7 +4669,7 @@ class AttributeDeadline extends AttributeDateTime
|
||||
{
|
||||
$sDifference = Dict::Format('UI:DeadlineMissedBy_duration', self::FormatDuration(-$difference));
|
||||
}
|
||||
$sFormat = MetaModel::GetConfig()->Get('deadline_format', '$difference$');
|
||||
$sFormat = MetaModel::GetConfig()->Get('deadline_format');
|
||||
$sResult = str_replace(array('$date$', '$difference$'), array($sDate, $sDifference), $sFormat);
|
||||
}
|
||||
|
||||
@@ -4653,8 +4681,7 @@ class AttributeDeadline extends AttributeDateTime
|
||||
$days = floor($duration / 86400);
|
||||
$hours = floor(($duration - (86400*$days)) / 3600);
|
||||
$minutes = floor(($duration - (86400*$days + 3600*$hours)) / 60);
|
||||
$sResult = '';
|
||||
|
||||
|
||||
if ($duration < 60)
|
||||
{
|
||||
// Less than 1 min
|
||||
@@ -4841,6 +4868,7 @@ class AttributeExternalKey extends AttributeDBFieldVoid
|
||||
/**
|
||||
* Find the corresponding "link" attribute on the target class, if any
|
||||
* @return null | AttributeDefinition
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function GetMirrorLinkAttribute()
|
||||
{
|
||||
@@ -5210,6 +5238,7 @@ class AttributeExternalField extends AttributeDefinition
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function IsFriendlyName()
|
||||
{
|
||||
@@ -5619,12 +5648,6 @@ class AttributeBlob extends AttributeDefinition
|
||||
public function GetFilterDefinitions()
|
||||
{
|
||||
return array();
|
||||
// still not working... see later...
|
||||
return array(
|
||||
$this->GetCode().'->filename' => new FilterFromAttribute($this, '_filename'),
|
||||
$this->GetCode().'_mimetype' => new FilterFromAttribute($this, '_mimetype'),
|
||||
$this->GetCode().'_mimetype' => new FilterFromAttribute($this, '_mimetype')
|
||||
);
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
@@ -5647,6 +5670,7 @@ class AttributeBlob extends AttributeDefinition
|
||||
{
|
||||
return $value->GetAsHTML();
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
@@ -5943,7 +5967,6 @@ class AttributeStopWatch extends AttributeDefinition
|
||||
self::DateToSeconds($aCols[$sPrefix.'_stopped'])
|
||||
);
|
||||
|
||||
$aThresholds = array();
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aDefinition)
|
||||
{
|
||||
$sThPrefix = '_'.$iThreshold;
|
||||
@@ -6047,6 +6070,7 @@ class AttributeStopWatch extends AttributeDefinition
|
||||
{
|
||||
return $value->GetAsHTML($this, $oHostObject);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
@@ -6799,6 +6823,7 @@ class AttributeOneWayPassword extends AttributeDefinition
|
||||
{
|
||||
return $value->GetAsHTML();
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
@@ -7054,6 +7079,7 @@ class AttributePropertySet extends AttributeTable
|
||||
class AttributeFriendlyName extends AttributeDefinition
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
|
||||
public $m_sValue;
|
||||
|
||||
public function __construct($sCode)
|
||||
{
|
||||
@@ -7309,12 +7335,16 @@ class AttributeRedundancySettings extends AttributeDBField
|
||||
}
|
||||
}
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the user option label
|
||||
* @param user option : disabled|cout|percent
|
||||
*/
|
||||
*
|
||||
* @param user option : disabled|cout|percent
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetUserOptionFormat($sUserOption, $sDefault = null)
|
||||
{
|
||||
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/'.$sUserOption, null, true /*user lang*/);
|
||||
@@ -7564,7 +7594,7 @@ class AttributeRedundancySettings extends AttributeDBField
|
||||
$sOptionName = $sHtmlNamesPrefix.'_user_option';
|
||||
$sOptionId = $sOptionName.'_'.$sUserOption;
|
||||
$sChecked = $bSelected ? 'checked' : '';
|
||||
$sRet = '<input type="radio" name="'.$sOptionName.'" id="'.$sOptionId.'" value="'.$sUserOption.'"'.$sChecked.'> <label for="'.$sOptionId.'">'.$sLabel.'</label>';
|
||||
$sRet = '<input type="radio" name="'.$sOptionName.'" id="'.$sOptionId.'" value="'.$sUserOption.'" '.$sChecked.'> <label for="'.$sOptionId.'">'.$sLabel.'</label>';
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -7716,7 +7746,9 @@ class AttributeCustomFields extends AttributeDefinition
|
||||
|
||||
/**
|
||||
* @param DBObject $oHostObject
|
||||
* @param null $sFormPrefix
|
||||
* @return Combodo\iTop\Form\Form
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetForm(DBObject $oHostObject, $sFormPrefix = null)
|
||||
{
|
||||
@@ -7785,7 +7817,7 @@ class AttributeCustomFields extends AttributeDefinition
|
||||
|
||||
/**
|
||||
* The part of the current attribute in the object's signature, for the supplied value
|
||||
* @param $value The value of this attribute for the object
|
||||
* @param ormCustomFieldsValue $value The value of this attribute for the object
|
||||
* @return string The "signature" for this field/attribute
|
||||
*/
|
||||
public function Fingerprint($value)
|
||||
@@ -7832,6 +7864,8 @@ class AttributeCustomFields extends AttributeDefinition
|
||||
/**
|
||||
* Cleanup data upon object deletion (object id still available here)
|
||||
* @param DBObject $oHostObject
|
||||
* @return
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function DeleteValue(DBObject $oHostObject)
|
||||
{
|
||||
@@ -7893,10 +7927,13 @@ class AttributeCustomFields extends AttributeDefinition
|
||||
|
||||
/**
|
||||
* Get various representations of the value, for insertion into a template (e.g. in Notifications)
|
||||
*
|
||||
* @param $value mixed The current value of the field
|
||||
* @param $sVerb string The verb specifying the representation of the value
|
||||
* @param $oHostObject DBObject The object
|
||||
* @param $bLocalize bool Whether or not to localize the value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetForTemplate($value, $sVerb, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
@@ -8024,11 +8061,6 @@ class AttributeObsolescenceFlag extends AttributeBoolean
|
||||
public function GetSQLExpressions($sPrefix = '')
|
||||
{
|
||||
return array();
|
||||
if ($sPrefix == '')
|
||||
{
|
||||
$sPrefix = $this->GetCode(); // Warning AttributeComputedFieldVoid does not have any sql property
|
||||
}
|
||||
return array('' => $sPrefix);
|
||||
}
|
||||
public function GetSQLColumns($bFullSpec = false) {return array();} // returns column/spec pairs (1 in most of the cases), for STRUCTURING (DB creation)
|
||||
public function GetSQLValues($value) {return array();} // returns column/value pairs (1 in most of the cases), for WRITING (Insert, Update)
|
||||
|
||||
@@ -73,6 +73,8 @@ class BulkExportResult extends DBObject
|
||||
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())));
|
||||
MetaModel::Init_AddAttribute(new AttributeBoolean("localize_output", array("allowed_values"=>null, "sql"=>"localize_output", "default_value"=>true, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,13 +153,16 @@ abstract class BulkExport
|
||||
$this->sTmpFile = '';
|
||||
$this->bLocalizeOutput = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first class capable of exporting the data in the given format
|
||||
* @param string $sFormatCode 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 BulkExport|NULL
|
||||
*/
|
||||
|
||||
/**
|
||||
* Find the first class capable of exporting the data in the given format
|
||||
*
|
||||
* @param string $sFormatCode 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 BulkExport|null
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
static public function FindExporter($sFormatCode, $oSearch = null)
|
||||
{
|
||||
foreach(get_declared_classes() as $sPHPClass)
|
||||
@@ -178,12 +183,17 @@ abstract class BulkExport
|
||||
}
|
||||
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
|
||||
*/
|
||||
|
||||
/**
|
||||
* Find the exporter corresponding to the given persistent token
|
||||
*
|
||||
* @param int $iPersistentToken The identifier of the BulkExportResult object storing the information
|
||||
*
|
||||
* @return iBulkExport|null
|
||||
* @throws ArchivedObjectException
|
||||
* @throws CoreException
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
static public function FindExporterFromToken($iPersistentToken = null)
|
||||
{
|
||||
$oBulkExporter = null;
|
||||
@@ -192,7 +202,7 @@ abstract class BulkExport
|
||||
{
|
||||
$sFormatCode = $oInfo->Get('format');
|
||||
$oSearch = DBObjectSearch::unserialize($oInfo->Get('search'));
|
||||
|
||||
|
||||
$oBulkExporter = self::FindExporter($sFormatCode, $oSearch);
|
||||
if ($oBulkExporter)
|
||||
{
|
||||
@@ -200,6 +210,10 @@ abstract class BulkExport
|
||||
$oBulkExporter->SetObjectList($oSearch);
|
||||
$oBulkExporter->SetChunkSize($oInfo->Get('chunk_size'));
|
||||
$oBulkExporter->SetStatusInfo(json_decode($oInfo->Get('status_info'), true));
|
||||
|
||||
$oBulkExporter->SetLocalizeOutput($oInfo->Get('localize_output'));
|
||||
|
||||
|
||||
$oBulkExporter->sTmpFile = $oInfo->Get('temp_file_path');
|
||||
$oBulkExporter->oBulkExportResult = $oInfo;
|
||||
}
|
||||
@@ -258,6 +272,14 @@ abstract class BulkExport
|
||||
{
|
||||
$this->iChunkSize = $iChunkSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $bLocalizeOutput
|
||||
*/
|
||||
public function SetLocalizeOutput($bLocalizeOutput)
|
||||
{
|
||||
$this->bLocalizeOutput = $bLocalizeOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
@@ -320,9 +342,10 @@ abstract class BulkExport
|
||||
$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('chunk_size', $this->iChunkSize);
|
||||
$this->oBulkExportResult->Set('temp_file_path', $this->sTmpFile);
|
||||
$this->oBulkExportResult->Set('localize_output', $this->bLocalizeOutput);
|
||||
}
|
||||
$this->oBulkExportResult->Set('status_info', json_encode($this->GetStatusInfo()));
|
||||
utils::PushArchiveMode(false);
|
||||
$ret = $this->oBulkExportResult->DBWrite();
|
||||
|
||||
@@ -492,15 +492,19 @@ class CMDBSource
|
||||
public static function DBPwd() {return self::$m_sDBPwd;}
|
||||
public static function DBName() {return self::$m_sDBName;}
|
||||
|
||||
/**
|
||||
* Quote variable and protect against SQL injection attacks
|
||||
* Code found in the PHP documentation: quote_smart($value)
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param bool $bAlways should be set to true when the purpose is to create a IN clause,
|
||||
* otherwise and if there is a mix of strings and numbers, the clause would always be false
|
||||
* @param string $cQuoteStyle
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
public static function Quote($value, $bAlways = false, $cQuoteStyle = "'")
|
||||
{
|
||||
// Quote variable and protect against SQL injection attacks
|
||||
// Code found in the PHP documentation: quote_smart($value)
|
||||
|
||||
// bAlways should be set to true when the purpose is to create a IN clause,
|
||||
// otherwise and if there is a mix of strings and numbers, the clause
|
||||
// would always be false
|
||||
|
||||
if (is_null($value))
|
||||
{
|
||||
return 'NULL';
|
||||
|
||||
@@ -265,8 +265,8 @@ class Config
|
||||
'min_autocomplete_chars' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'The minimum number of characters to type in order to trigger the "autocomplete" behavior',
|
||||
'default' => 3,
|
||||
'value' => 3,
|
||||
'default' => 2,
|
||||
'value' => 2,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
@@ -495,6 +495,22 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'email_default_sender_address' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'Default address provided in the email from header field.',
|
||||
'default' => "",
|
||||
'value' => "",
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'email_default_sender_label' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'Default label provided in the email from header field.',
|
||||
'default' => "",
|
||||
'value' => "",
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'apc_cache.enabled' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'If set, the APC cache is allowed (the PHP extension must also be active)',
|
||||
@@ -1037,9 +1053,9 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'inline_image_garbage_collector_interval' => array(
|
||||
'draft_attachments_lifetime' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Frequency (in seconds) at which the inline image garbage collector will run.',
|
||||
'description' => 'Lifetime (in seconds) of drafts\' attachments and inline images: after this duration, the garbage collector will delete them.',
|
||||
'default' => 3600,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
@@ -1095,12 +1111,20 @@ class Config
|
||||
),
|
||||
'search_manual_submit' => array(
|
||||
'type' => 'array',
|
||||
'description' => 'Force manual submit of search requests (class => true)',
|
||||
'description' => 'Force manual submit of search all requests',
|
||||
'default' => false,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'high_cardinality_classes' => array(
|
||||
'type' => 'array',
|
||||
'description' => 'List of classes with high cardinality (Force manual submit of search)',
|
||||
'default' => array(),
|
||||
'value' => array(),
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
);
|
||||
|
||||
public function IsProperty($sPropCode)
|
||||
@@ -1160,10 +1184,10 @@ class Config
|
||||
|
||||
}
|
||||
|
||||
public function Get($sPropCode)
|
||||
{
|
||||
return $this->m_aSettings[$sPropCode]['value'];
|
||||
}
|
||||
public function Get($sPropCode)
|
||||
{
|
||||
return $this->m_aSettings[$sPropCode]['value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Event log options (see LOG_... definition)
|
||||
@@ -1336,6 +1360,12 @@ class Config
|
||||
throw new ConfigException('Error in configuration file',
|
||||
array('file' => $sConfigFile, 'error' => $e->getMessage()));
|
||||
}
|
||||
catch(Error $e)
|
||||
{
|
||||
// PHP 7
|
||||
throw new ConfigException('Error in configuration file',
|
||||
array('file' => $sConfigFile, 'error' => $e->getMessage().' at line '.$e->getLine()));
|
||||
}
|
||||
if (strlen($sNoise) > 0)
|
||||
{
|
||||
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.5">
|
||||
<user_rights>
|
||||
<profiles>
|
||||
<profile id="1024" _delta="define">
|
||||
@@ -9,4 +9,250 @@
|
||||
</profile>
|
||||
</profiles>
|
||||
</user_rights>
|
||||
<meta>
|
||||
<classes>
|
||||
<class id="User" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>core,grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="contactid" xsi:type="AttributeExternalKey">
|
||||
<target_class>Person</target_class>
|
||||
</field>
|
||||
<field id="last_name" xsi:type="AttributeExternalField"/>
|
||||
<field id="first_name" xsi:type="AttributeExternalField"/>
|
||||
<field id="email" xsi:type="AttributeExternalField"/>
|
||||
<field id="org_id" xsi:type="AttributeExternalField"/>
|
||||
<field id="login" xsi:type="AttributeString"/>
|
||||
<field id="language" xsi:type="AttributeApplicationLanguage"/>
|
||||
<field id="status" xsi:type="AttributeEnum"/>
|
||||
<field id="profile_list" xsi:type="AttributeLinkedSetIndirect"/>
|
||||
<field id="allowed_org_list" xsi:type="AttributeLinkedSetIndirect"/>
|
||||
<field id="finalclass" xsi:type="AttributeFinalClass"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
<field id="contactid_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
<field id="contactid_obsolescence_flag" xsi:type="AttributeExternalField"/>
|
||||
<field id="org_id_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
<field id="org_id_obsolescence_flag" xsi:type="AttributeExternalField"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="URP_Profiles" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>addon/userrights,grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString"/>
|
||||
<field id="description" xsi:type="AttributeString"/>
|
||||
<field id="user_list" xsi:type="AttributeLinkedSetIndirect"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="URP_UserProfile" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>addon/userrights,grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="userid" xsi:type="AttributeExternalKey">
|
||||
<target_class>User</target_class>
|
||||
</field>
|
||||
<field id="userlogin" xsi:type="AttributeExternalField"/>
|
||||
<field id="profileid" xsi:type="AttributeExternalKey">
|
||||
<target_class>URP_Profiles</target_class>
|
||||
</field>
|
||||
<field id="profile" xsi:type="AttributeExternalField"/>
|
||||
<field id="reason" xsi:type="AttributeString"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
<field id="userid_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
<field id="userid_finalclass_recall" xsi:type="AttributeExternalField"/>
|
||||
<field id="profileid_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="URP_UserOrg" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>addon/userrights,grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="userid" xsi:type="AttributeExternalKey">
|
||||
<target_class>User</target_class>
|
||||
</field>
|
||||
<field id="userlogin" xsi:type="AttributeExternalField"/>
|
||||
<field id="allowed_org_id" xsi:type="AttributeExternalKey">
|
||||
<target_class>Organization</target_class>
|
||||
</field>
|
||||
<field id="allowed_org_name" xsi:type="AttributeExternalField"/>
|
||||
<field id="reason" xsi:type="AttributeString"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
<field id="userid_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
<field id="userid_finalclass_recall" xsi:type="AttributeExternalField"/>
|
||||
<field id="allowed_org_id_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
<field id="allowed_org_id_obsolescence_flag" xsi:type="AttributeExternalField"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="Action" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>grant_by_profile,core/cmdb</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString"/>
|
||||
<field id="description" xsi:type="AttributeString"/>
|
||||
<field id="status" xsi:type="AttributeEnum"/>
|
||||
<field id="trigger_list" xsi:type="AttributeLinkedSetIndirect"/>
|
||||
<field id="finalclass" xsi:type="AttributeFinalClass"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="Trigger" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>grant_by_profile,core/cmdb</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="description" xsi:type="AttributeString"/>
|
||||
<field id="action_list" xsi:type="AttributeLinkedSetIndirect"/>
|
||||
<field id="finalclass" xsi:type="AttributeFinalClass"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="SynchroDataSource" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>core/cmdb,view_in_gui,grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString"/>
|
||||
<field id="description" xsi:type="AttributeText"/>
|
||||
<field id="status" xsi:type="AttributeEnum"/>
|
||||
<field id="user_id" xsi:type="AttributeExternalKey">
|
||||
<target_class>User</target_class>
|
||||
</field>
|
||||
<field id="notify_contact_id" xsi:type="AttributeExternalKey">
|
||||
<target_class>Contact</target_class>
|
||||
</field>
|
||||
<field id="scope_class" xsi:type="AttributeClass"/>
|
||||
<field id="database_table_name" xsi:type="AttributeString"/>
|
||||
<field id="scope_restriction" xsi:type="AttributeString"/>
|
||||
<field id="full_load_periodicity" xsi:type="AttributeDuration"/>
|
||||
<field id="reconciliation_policy" xsi:type="AttributeEnum"/>
|
||||
<field id="action_on_zero" xsi:type="AttributeEnum"/>
|
||||
<field id="action_on_one" xsi:type="AttributeEnum"/>
|
||||
<field id="action_on_multiple" xsi:type="AttributeEnum"/>
|
||||
<field id="delete_policy" xsi:type="AttributeEnum"/>
|
||||
<field id="delete_policy_update" xsi:type="AttributeString"/>
|
||||
<field id="delete_policy_retention" xsi:type="AttributeDuration"/>
|
||||
<field id="attribute_list" xsi:type="AttributeLinkedSet"/>
|
||||
<field id="user_delete_policy" xsi:type="AttributeEnum"/>
|
||||
<field id="url_icon" xsi:type="AttributeURL"/>
|
||||
<field id="url_application" xsi:type="AttributeString"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
<field id="user_id_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
<field id="user_id_finalclass_recall" xsi:type="AttributeExternalField"/>
|
||||
<field id="notify_contact_id_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
<field id="notify_contact_id_finalclass_recall" xsi:type="AttributeExternalField"/>
|
||||
<field id="notify_contact_id_obsolescence_flag" xsi:type="AttributeExternalField"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="SynchroAttribute" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>core/cmdb,view_in_gui,grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="sync_source_id" xsi:type="AttributeExternalKey">
|
||||
<target_class>SynchroDataSource</target_class>
|
||||
</field>
|
||||
<field id="sync_source_name" xsi:type="AttributeExternalField"/>
|
||||
<field id="attcode" xsi:type="AttributeString"/>
|
||||
<field id="update" xsi:type="AttributeBoolean"/>
|
||||
<field id="reconcile" xsi:type="AttributeBoolean"/>
|
||||
<field id="update_policy" xsi:type="AttributeEnum"/>
|
||||
<field id="finalclass" xsi:type="AttributeFinalClass"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
<field id="sync_source_id_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="AuditRule" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>application, grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString"/>
|
||||
<field id="description" xsi:type="AttributeString"/>
|
||||
<field id="query" xsi:type="AttributeOQL"/>
|
||||
<field id="valid_flag" xsi:type="AttributeEnum"/>
|
||||
<field id="category_id" xsi:type="AttributeExternalKey">
|
||||
<target_class>AuditCategory</target_class>
|
||||
</field>
|
||||
<field id="category_name" xsi:type="AttributeExternalField"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
<field id="category_id_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="AuditCategory" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>application, grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString"/>
|
||||
<field id="description" xsi:type="AttributeString"/>
|
||||
<field id="definition_set" xsi:type="AttributeOQL"/>
|
||||
<field id="rules_list" xsi:type="AttributeLinkedSet"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="Query" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>core/cmdb,view_in_gui,application,grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString"/>
|
||||
<field id="description" xsi:type="AttributeText"/>
|
||||
<field id="fields" xsi:type="AttributeText"/>
|
||||
<field id="finalclass" xsi:type="AttributeFinalClass"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="lnkTriggerAction" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>grant_by_profile,core/cmdb,application</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="action_id" xsi:type="AttributeExternalKey">
|
||||
<target_class>Action</target_class>
|
||||
</field>
|
||||
<field id="action_name" xsi:type="AttributeExternalField"/>
|
||||
<field id="trigger_id" xsi:type="AttributeExternalKey">
|
||||
<target_class>Trigger</target_class>
|
||||
</field>
|
||||
<field id="trigger_name" xsi:type="AttributeExternalField"/>
|
||||
<field id="order" xsi:type="AttributeInteger"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
<field id="action_id_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
<field id="action_id_finalclass_recall" xsi:type="AttributeExternalField"/>
|
||||
<field id="trigger_id_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
<field id="trigger_id_finalclass_recall" xsi:type="AttributeExternalField"/>
|
||||
</fields>
|
||||
</class>
|
||||
</classes>
|
||||
</meta>
|
||||
</itop_design>
|
||||
@@ -1234,7 +1234,7 @@ abstract class DBObject implements iDisplay
|
||||
elseif ($oAtt->IsScalar())
|
||||
{
|
||||
$aValues = $oAtt->GetAllowedValues($this->ToArgsForQuery());
|
||||
if (count($aValues) > 0)
|
||||
if (is_array($aValues) && (count($aValues) > 0))
|
||||
{
|
||||
if (!array_key_exists($toCheck, $aValues))
|
||||
{
|
||||
@@ -2939,7 +2939,7 @@ abstract class DBObject implements iDisplay
|
||||
$oSearch->AllowAllData();
|
||||
}
|
||||
$oSet = new CMDBObjectSet($oSearch);
|
||||
if ($oSet->Count() > 0)
|
||||
if ($oSet->CountExceeds(0))
|
||||
{
|
||||
$aDependentObjects[$sRemoteClass][$sExtKeyAttCode] = array(
|
||||
'attribute' => $oExtKeyAttDef,
|
||||
|
||||
@@ -100,8 +100,13 @@ class DBObjectSearch extends DBSearch
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
* Defaults to the first selected class (most of the time it is also the first joined class
|
||||
*
|
||||
* @param $sNewClass
|
||||
* @param null $sAlias
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function ChangeClass($sNewClass, $sAlias = null)
|
||||
{
|
||||
if (is_null($sAlias))
|
||||
@@ -186,7 +191,9 @@ class DBObjectSearch extends DBSearch
|
||||
*
|
||||
* @param $sOldName
|
||||
* @param $sNewName
|
||||
*
|
||||
* @return bool True if the alias has been found and changed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function RenameAlias($sOldName, $sNewName)
|
||||
{
|
||||
@@ -381,7 +388,7 @@ class DBObjectSearch extends DBSearch
|
||||
else
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->GetClass(), $sFilterCode);
|
||||
$oNewCondition = $oAttDef->GetSmartConditionExpression($value, $oField, $this->m_aParams, $bParseSearchString);
|
||||
$oNewCondition = $oAttDef->GetSmartConditionExpression($value, $oField, $this->m_aParams);
|
||||
$this->AddConditionExpression($oNewCondition);
|
||||
return;
|
||||
}
|
||||
@@ -501,7 +508,9 @@ class DBObjectSearch extends DBSearch
|
||||
$oParamExpression = new VariableExpression($sInParamName);
|
||||
$this->SetInternalParams(array($sInParamName => $aValues));
|
||||
|
||||
$oInCondition = new BinaryExpression($oFieldExpression, $sOperator, $oParamExpression);
|
||||
$oListExpression = new ListExpression(array($oParamExpression));
|
||||
|
||||
$oInCondition = new BinaryExpression($oFieldExpression, $sOperator, $oListExpression);
|
||||
$this->AddConditionExpression($oInCondition);
|
||||
}
|
||||
|
||||
@@ -522,10 +531,12 @@ class DBObjectSearch extends DBSearch
|
||||
|
||||
/**
|
||||
* 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...)
|
||||
* @param string $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
|
||||
* @return void
|
||||
* @throws \CoreException
|
||||
* @throws \CoreWarning
|
||||
*/
|
||||
public function AddConditionAdvanced($sAttSpec, $value)
|
||||
{
|
||||
@@ -722,9 +733,9 @@ class DBObjectSearch extends DBSearch
|
||||
* - convert a translation table (format optimized for the translation in an expression tree) into simple hash
|
||||
* - compile over an eventually existing map
|
||||
*
|
||||
* @param $aRealiasingMap Map to update
|
||||
* @param $aAliasTranslation Translation table resulting from calls to MergeWith_InNamespace
|
||||
* @return array of <old-alias> => <new-alias>
|
||||
* @param array $aRealiasingMap Map to update
|
||||
* @param array $aAliasTranslation Translation table resulting from calls to MergeWith_InNamespace
|
||||
* @return void of <old-alias> => <new-alias>
|
||||
*/
|
||||
protected function UpdateRealiasingMap(&$aRealiasingMap, $aAliasTranslation)
|
||||
{
|
||||
@@ -754,7 +765,7 @@ class DBObjectSearch extends DBSearch
|
||||
* This a workaround to handle some cases in which the list of classes is not correctly updated.
|
||||
* This code should disappear as soon as DBObjectSearch get split between a container search class and a Node class
|
||||
*
|
||||
* @param $aClasses List to be completed
|
||||
* @param array $aClasses List to be completed
|
||||
*/
|
||||
protected function RecomputeClassList(&$aClasses)
|
||||
{
|
||||
@@ -874,6 +885,8 @@ class DBObjectSearch extends DBSearch
|
||||
* @param $sForeignExtKeyAttCode
|
||||
* @param int $iOperatorCode
|
||||
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
|
||||
* @return void
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
|
||||
{
|
||||
@@ -896,7 +909,7 @@ class DBObjectSearch extends DBSearch
|
||||
// NO: $oFilter = $oFilter->DeepClone();
|
||||
// See also: Trac #639, and self::AddCondition_PointingTo()
|
||||
$aAliasTranslation = array();
|
||||
$res = $this->AddCondition_ReferencedBy_InNameSpace($oFilter, $sForeignExtKeyAttCode, $this->m_aClasses, $aAliasTranslation, $iOperatorCode);
|
||||
$this->AddCondition_ReferencedBy_InNameSpace($oFilter, $sForeignExtKeyAttCode, $this->m_aClasses, $aAliasTranslation, $iOperatorCode);
|
||||
$this->TransferConditionExpression($oFilter, $aAliasTranslation);
|
||||
$this->UpdateRealiasingMap($aRealiasingMap, $aAliasTranslation);
|
||||
|
||||
@@ -920,7 +933,6 @@ class DBObjectSearch extends DBSearch
|
||||
}
|
||||
}
|
||||
$this->RecomputeClassList($this->m_aClasses);
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected function AddCondition_ReferencedBy_InNameSpace(DBSearch $oFilter, $sForeignExtKeyAttCode, &$aClassAliases, &$aAliasTranslation, $iOperatorCode)
|
||||
@@ -979,7 +991,10 @@ class DBObjectSearch extends DBSearch
|
||||
$oRightFilter = $oRightFilter->DeepClone();
|
||||
|
||||
$bAllowAllData = ($oLeftFilter->IsAllDataAllowed() && $oRightFilter->IsAllDataAllowed());
|
||||
$oLeftFilter->AllowAllData($bAllowAllData);
|
||||
if ($bAllowAllData)
|
||||
{
|
||||
$oLeftFilter->AllowAllData();
|
||||
}
|
||||
|
||||
if ($oLeftFilter->GetClass() != $oRightFilter->GetClass())
|
||||
{
|
||||
@@ -1124,117 +1139,17 @@ class DBObjectSearch extends DBSearch
|
||||
{
|
||||
return $this->m_oSearchCondition->ListConstantFields();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Turn the parameters (:xxx) into scalar values in order to easily
|
||||
* serialize a search
|
||||
*/
|
||||
* @param $aArgs
|
||||
*/
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
return $this->m_oSearchCondition->ApplyParameters(array_merge($this->m_aParams, $aArgs));
|
||||
$this->m_oSearchCondition->ApplyParameters(array_merge($this->m_aParams, $aArgs));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @todo: check if the clone is mandatory or optional. (the performance would be better without cloning)
|
||||
*
|
||||
* @param bool $bClone
|
||||
*
|
||||
* @return DBObjectSearch|DBSearch
|
||||
*/
|
||||
public function ShorthandExpansion($bClone = false)
|
||||
{
|
||||
if ($bClone)
|
||||
{
|
||||
$oDbObject = $this->DeepClone();
|
||||
}
|
||||
else
|
||||
{
|
||||
$oDbObject = $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This callback add joins based on the classes found inside ExternalFieldExpression::$aFields
|
||||
* to do so, it is applied on each Expression of $this->m_oSearchCondition.
|
||||
*
|
||||
* @param $oExpression
|
||||
*/
|
||||
$callback = function ($oExpression) use ($oDbObject)
|
||||
{
|
||||
if (!$oExpression instanceof ExternalFieldExpression) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/** @var FieldExpression[] $aFieldExpressionsPointingTo */
|
||||
$aFields = $oExpression->GetFields();
|
||||
|
||||
$aRealiasingMap = array();
|
||||
$aExpressionNewConditions = array();
|
||||
foreach ($aFields as $aFieldExpressionPointingTo)
|
||||
{
|
||||
$oFilter = new DBObjectSearch($aFieldExpressionPointingTo['sClass']);
|
||||
$aExpressionNewConditions[] = array(
|
||||
'oFilter' => $oFilter,
|
||||
'sExtKeyAttCode' => $aFieldExpressionPointingTo['sAttCode'],
|
||||
);
|
||||
|
||||
$aRealiasingMap[$aFieldExpressionPointingTo['sClass']] = $aFieldExpressionPointingTo['sAlias'];
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* the iteration below is weird because wee need to
|
||||
* - iterate in the reverse order
|
||||
* - the iteration access the "index+1" so wee start at "length-1" (which is "count()-2")
|
||||
* - whe stop at the index 1, because the index 0 is merged into the $oDbObject
|
||||
*/
|
||||
for ($i = count($aExpressionNewConditions) - 2; $i > 0; $i--)
|
||||
{
|
||||
$aExpressionNewConditions[$i]['oFilter']->AddCondition_PointingTo(
|
||||
$aExpressionNewConditions[$i+1]['oFilter'],
|
||||
$aExpressionNewConditions[$i]['sExtKeyAttCode'],
|
||||
TREE_OPERATOR_EQUALS,
|
||||
$aRealiasingMap
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
$oDbObject->AddCondition_PointingTo(
|
||||
$aExpressionNewConditions[1]['oFilter'],
|
||||
$aExpressionNewConditions[0]['sExtKeyAttCode'],
|
||||
TREE_OPERATOR_EQUALS,
|
||||
$aRealiasingMap
|
||||
);
|
||||
|
||||
foreach ($aRealiasingMap as $sOldAlias => $sNewAlias)
|
||||
{
|
||||
if ($sOldAlias == $sNewAlias)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($sOldAlias != $aFields['sAlias'])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$aFields['sAlias'] = $sNewAlias;
|
||||
}
|
||||
|
||||
$oExpression->SetFields($aFields);
|
||||
}; //end of the callback definition
|
||||
|
||||
|
||||
//Add the joins
|
||||
$this->m_oSearchCondition->Browse($callback);
|
||||
|
||||
//replace the ExternalFieldExpression by a FieldExpression (based on last ExternalFieldExpression::$aFields)
|
||||
$this->m_oSearchCondition->Translate(array(), false, false);//TODO: check if this call is correct
|
||||
|
||||
return $oDbObject;
|
||||
}
|
||||
|
||||
|
||||
public function ToOQL($bDevelopParams = false, $aContextParams = null, $bWithAllowAllFlag = false)
|
||||
{
|
||||
// Currently unused, but could be useful later
|
||||
@@ -1363,23 +1278,6 @@ class DBObjectSearch extends DBSearch
|
||||
$oRight = $this->OQLExpressionToCondition($sQuery, $oExpression->GetRightExpr(), $aClassAliases);
|
||||
return new BinaryExpression($oLeft, $sOperator, $oRight);
|
||||
}
|
||||
elseif ($oExpression instanceof ExternalFieldOqlExpression)
|
||||
{
|
||||
//TODO : convert FieldOqlExpression[] to FieldExpression[]
|
||||
$aOqlFieldExpression = $oExpression->GetExpressions();
|
||||
$aFields = array();
|
||||
|
||||
foreach ($aOqlFieldExpression as $oOqlFieldExpression)
|
||||
{
|
||||
$aFields[] = array(
|
||||
'sClass' => $oOqlFieldExpression->GetParent(),
|
||||
'sAlias' => $oOqlFieldExpression->GetParent(),
|
||||
'sAttCode' => $oOqlFieldExpression->GetName(),
|
||||
);
|
||||
}
|
||||
|
||||
return new ExternalFieldExpression($oExpression->GetName(), $aFields);
|
||||
}
|
||||
elseif ($oExpression instanceof FieldOqlExpression)
|
||||
{
|
||||
$sClassAlias = $oExpression->GetParent();
|
||||
@@ -1618,11 +1516,6 @@ class DBObjectSearch extends DBSearch
|
||||
$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.)
|
||||
@@ -1648,7 +1541,7 @@ class DBObjectSearch extends DBSearch
|
||||
$aContextData['sRequestUri'] = '';
|
||||
}
|
||||
|
||||
// Need to identify the querySELECT `Contact` FROM Contact AS `Contact` JOIN Organization AS `Organization` ON `Contact`.org_id = `Organization`.id JOIN DeliveryModel AS `DeliveryModel` ON `Organization`.deliverymodel_id = `DeliveryModel`.id WHERE ((((Contact.org_id->deliverymodel_id->name = 'Standard support') AND 1) AND 1) AND 1)
|
||||
// Need to identify the query
|
||||
$sOqlQuery = $oSearch->ToOql(false, null, true);
|
||||
if ((strpos($sOqlQuery, '`id` IN (') !== false) || (strpos($sOqlQuery, '`id` NOT IN (') !== false))
|
||||
{
|
||||
@@ -1754,8 +1647,7 @@ class DBObjectSearch extends DBSearch
|
||||
|
||||
if (!isset($oSQLQuery))
|
||||
{
|
||||
$oSearch = $oSearch->ShorthandExpansion(true);//TODO : check if the clone is really needed (1st param of ShorthandExpansion)
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oSQLQuery = $oSearch->BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr, $aSelectedClasses, $aSelectExpr);
|
||||
$oKPI->ComputeStats('BuildSQLQueryStruct', $sOqlQuery);
|
||||
|
||||
@@ -1782,7 +1674,9 @@ class DBObjectSearch extends DBSearch
|
||||
* @param array $aGroupByExpr
|
||||
* @param array $aSelectedClasses
|
||||
* @param array $aSelectExpr
|
||||
*
|
||||
* @return null|SQLObjectQuery
|
||||
* @throws \CoreException
|
||||
*/
|
||||
protected function BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
|
||||
{
|
||||
@@ -1790,7 +1684,7 @@ class DBObjectSearch extends DBSearch
|
||||
|
||||
$oSQLQuery = $this->MakeSQLObjectQuery($oBuild, $aAttToLoad, array());
|
||||
$oSQLQuery->SetCondition($oBuild->m_oQBExpressions->GetCondition());
|
||||
if ($aGroupByExpr)
|
||||
if (is_array($aGroupByExpr))
|
||||
{
|
||||
$aCols = $oBuild->m_oQBExpressions->GetGroupBy();
|
||||
$oSQLQuery->SetGroupBy($aCols);
|
||||
@@ -1844,6 +1738,7 @@ class DBObjectSearch extends DBSearch
|
||||
* @param null $aAttToLoad
|
||||
* @param array $aValues
|
||||
* @return null|SQLObjectQuery
|
||||
* @throws \CoreException
|
||||
*/
|
||||
protected function MakeSQLObjectQuery(&$oBuild, $aAttToLoad = null, $aValues = array())
|
||||
{
|
||||
@@ -2324,7 +2219,7 @@ class DBObjectSearch extends DBSearch
|
||||
self::DbgTrace("External key $sKeyAttCode, Join on $sLocalKeyField = $sExternalKeyField");
|
||||
if ($oKeyAttDef->IsNullAllowed())
|
||||
{
|
||||
$oSelectBase->AddLeftJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField, $sExternalKeyTable);
|
||||
$oSelectBase->AddLeftJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2376,9 +2271,13 @@ class DBObjectSearch extends DBSearch
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the expression for the class and its subclasses (if finalclass = 'subclass' ...)
|
||||
* Simplifies the final expression by grouping classes having the same expression
|
||||
*/
|
||||
* Get the expression for the class and its subclasses (if finalclass = 'subclass' ...)
|
||||
* Simplifies the final expression by grouping classes having the same expression
|
||||
* @param $sClass
|
||||
* @param $sAttCode
|
||||
* @return \FunctionExpression|mixed|null
|
||||
* @throws \CoreException
|
||||
*/
|
||||
static public function GetPolymorphicExpression($sClass, $sAttCode)
|
||||
{
|
||||
$oExpression = ExpressionCache::GetCachedExpression($sClass, $sAttCode);
|
||||
|
||||
@@ -77,18 +77,20 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
* @var mysqli_result
|
||||
*/
|
||||
protected $m_oSQLResult;
|
||||
protected $m_bSort;
|
||||
|
||||
/**
|
||||
* Create a new set based on a Search definition.
|
||||
*
|
||||
* @param DBSearch $oFilter The search filter defining the objects which are part of the set (multiple columns/objects per row are supported)
|
||||
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
||||
* @param hash $aArgs Values to substitute for the search/query parameters (if any). Format: param_name => value
|
||||
* @param hash $aExtendedDataSpec
|
||||
* @param array $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
||||
* @param array $aArgs Values to substitute for the search/query parameters (if any). Format: param_name => value
|
||||
* @param array $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)
|
||||
* @param bool $bSort if false no order by is done
|
||||
*/
|
||||
public function __construct(DBSearch $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, $bSort = true)
|
||||
{
|
||||
$this->m_oFilter = $oFilter->DeepClone();
|
||||
$this->m_aAddedIds = array();
|
||||
@@ -98,6 +100,7 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
$this->m_aExtendedDataSpec = $aExtendedDataSpec;
|
||||
$this->m_iLimitCount = $iLimitCount;
|
||||
$this->m_iLimitStart = $iLimitStart;
|
||||
$this->m_bSort = $bSort;
|
||||
|
||||
$this->m_iNumTotalDBRows = null;
|
||||
$this->m_iNumLoadedDBRows = 0;
|
||||
@@ -567,7 +570,7 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
$aAttributes = array();
|
||||
foreach ($aAliases as $sAlias => $bClassDirection)
|
||||
{
|
||||
foreach (MetaModel::GetOrderByDefault($this->m_oFilter->GetClass($sAlias)) as $sAttCode => $bAttributeDirection)
|
||||
foreach (MetaModel::GetOrderByDefault($this->m_oFilter->GetClassName($sAlias)) as $sAttCode => $bAttributeDirection)
|
||||
{
|
||||
$bDirection = $bClassDirection ? $bAttributeDirection : !$bAttributeDirection;
|
||||
$aAttributes[$sAlias.'.'.$sAttCode] = $bDirection;
|
||||
@@ -601,10 +604,15 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
*
|
||||
* Limitation: the sort order has no effect on objects added in-memory
|
||||
*
|
||||
* @return hash Format: field_code => boolean (true = ascending, false = descending)
|
||||
* @return array Format: field_code => boolean (true = ascending, false = descending)
|
||||
*/
|
||||
public function GetRealSortOrder()
|
||||
{
|
||||
if (!$this->m_bSort)
|
||||
{
|
||||
// No order by
|
||||
return array();
|
||||
}
|
||||
// Get the class default sort order if not specified with the API
|
||||
//
|
||||
if (empty($this->m_aOrderBy))
|
||||
@@ -719,7 +727,75 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
|
||||
return $this->m_iNumTotalDBRows + count($this->m_aAddedObjects); // Does it fix Trac #887 ??
|
||||
}
|
||||
|
||||
|
||||
/** Check if the count exceeds a given limit
|
||||
* @param $iLimit
|
||||
*
|
||||
* @return bool
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
public function CountExceeds($iLimit)
|
||||
{
|
||||
if (is_null($this->m_iNumTotalDBRows))
|
||||
{
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
if ($resQuery)
|
||||
{
|
||||
$aRow = CMDBSource::FetchArray($resQuery);
|
||||
CMDBSource::FreeResult($resQuery);
|
||||
$iCount = intval($aRow['COUNT']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCount = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCount = $this->m_iNumTotalDBRows;
|
||||
}
|
||||
|
||||
return ($iCount > $iLimit);
|
||||
}
|
||||
|
||||
/** Count only up to the given limit
|
||||
* @param $iLimit
|
||||
*
|
||||
* @return int
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
public function CountWithLimit($iLimit)
|
||||
{
|
||||
if (is_null($this->m_iNumTotalDBRows))
|
||||
{
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
if ($resQuery)
|
||||
{
|
||||
$aRow = CMDBSource::FetchArray($resQuery);
|
||||
CMDBSource::FreeResult($resQuery);
|
||||
$iCount = intval($aRow['COUNT']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCount = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCount = $this->m_iNumTotalDBRows;
|
||||
}
|
||||
|
||||
return $iCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of rows available in memory (loaded from DB + added in memory)
|
||||
*
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
<?php
|
||||
// Copyright (C) 2016 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/>
|
||||
/**
|
||||
* Copyright (c) 2010-2018 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/>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Design document and associated nodes
|
||||
@@ -57,6 +60,9 @@ class DesignDocument extends DOMDocument
|
||||
|
||||
/**
|
||||
* Overload of the standard API
|
||||
*
|
||||
* @param $filename
|
||||
* @param int $options
|
||||
*/
|
||||
public function load($filename, $options = 0)
|
||||
{
|
||||
@@ -65,6 +71,11 @@ class DesignDocument extends DOMDocument
|
||||
|
||||
/**
|
||||
* Overload of the standard API
|
||||
*
|
||||
* @param $filename
|
||||
* @param int $options
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function save($filename, $options = 0)
|
||||
{
|
||||
@@ -84,18 +95,18 @@ class DesignDocument extends DOMDocument
|
||||
{
|
||||
return $sXml;
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "<pre>\n";
|
||||
echo htmlentities($sXml);
|
||||
echo "</pre>\n";
|
||||
}
|
||||
|
||||
echo "<pre>\n";
|
||||
echo htmlentities($sXml);
|
||||
echo "</pre>\n";
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Quote and escape strings for use within an XPath expression
|
||||
* Usage: DesignDocument::GetNodes('class[@id='.DesignDocument::XPathQuote($sId).']');
|
||||
* @param $sValue The value to be quoted
|
||||
* @param string $sValue The value to be quoted
|
||||
* @return string to be used within an XPath
|
||||
*/
|
||||
public static function XPathQuote($sValue)
|
||||
@@ -115,7 +126,7 @@ class DesignDocument extends DOMDocument
|
||||
/**
|
||||
* Extracts some nodes from the DOM
|
||||
* @param string $sXPath A XPath expression
|
||||
* @param DesignNode|null $oContextNode The node to start the search from
|
||||
* @param DesignElement $oContextNode The node to start the search from
|
||||
* @return \DOMNodeList
|
||||
*/
|
||||
public function GetNodes($sXPath, $oContextNode = null)
|
||||
@@ -134,7 +145,7 @@ class DesignDocument extends DOMDocument
|
||||
|
||||
/**
|
||||
* An alternative to getNodePath, that gives the id of nodes instead of the position within the children
|
||||
* @param $oNode The node to describe
|
||||
* @param DesignElement $oNode The node to describe
|
||||
* @return string
|
||||
*/
|
||||
public static function GetItopNodePath($oNode)
|
||||
@@ -166,8 +177,11 @@ class DesignElement extends \DOMElement
|
||||
|
||||
/**
|
||||
* Create an HTML representation of the DOM, for debugging purposes
|
||||
*
|
||||
* @param bool|false $bReturnRes Echoes or returns the HTML representation
|
||||
*
|
||||
* @return mixed void or the HTML representation of the DOM
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function Dump($bReturnRes = false)
|
||||
{
|
||||
@@ -180,19 +194,16 @@ class DesignElement extends \DOMElement
|
||||
{
|
||||
return $sXml;
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "<pre>\n";
|
||||
echo htmlentities($sXml);
|
||||
echo "</pre>\n";
|
||||
}
|
||||
echo "<pre>\n";
|
||||
echo htmlentities($sXml);
|
||||
echo "</pre>\n";
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node directly under the given node
|
||||
* @param $sTagName
|
||||
* @param bool|true $bMustExist
|
||||
* @return MFElement
|
||||
* @return \MFElement
|
||||
* @throws DOMFormatException
|
||||
*/
|
||||
public function GetUniqueElement($sTagName, $bMustExist = true)
|
||||
@@ -216,7 +227,7 @@ class DesignElement extends \DOMElement
|
||||
/**
|
||||
* Returns the node directly under the current node, or null if missing
|
||||
* @param $sTagName
|
||||
* @return MFElement
|
||||
* @return \MFElement
|
||||
* @throws DOMFormatException
|
||||
*/
|
||||
public function GetOptionalElement($sTagName)
|
||||
@@ -252,9 +263,12 @@ class DesignElement extends \DOMElement
|
||||
|
||||
/**
|
||||
* Get the TEXT value from a child node
|
||||
*
|
||||
* @param string $sTagName
|
||||
* @param string|null $sDefault
|
||||
*
|
||||
* @return string
|
||||
* @throws \DOMFormatException
|
||||
*/
|
||||
public function GetChildText($sTagName, $sDefault = null)
|
||||
{
|
||||
|
||||
@@ -1438,22 +1438,22 @@ class DisplayableGraph extends SimpleGraph
|
||||
}
|
||||
$aExcludedByClass[get_class($oObj)][] = $oObj->GetKey();
|
||||
}
|
||||
$sSftShort = Dict::S('UI:ElementsDisplayed');
|
||||
$sSearchToggle = Dict::S('UI:Search:Toggle');
|
||||
$oP->add("<div class=\"not-printable\">\n");
|
||||
$oP->add("<div id=\"ds_flash\" class=\"search_box\" style=\"display:none;\">\n");
|
||||
if (!$oP->IsPrintableVersion())
|
||||
{
|
||||
$oP->add_ready_script(
|
||||
$oP->add(
|
||||
<<<EOF
|
||||
$( "#tabbedContent_0" ).tabs({ heightStyle: "fill" });
|
||||
<div id="ds_flash" class="search_box">
|
||||
<form id="dh_flash" class="search_form_handler closed">
|
||||
<h2 class="sf_title"><span class="sft_long">$sSftShort</span><span class="sft_short">$sSftShort</span><span class="sft_toggler fa fa-caret-down pull-right" title="$sSearchToggle"></span></h2>
|
||||
<div id="dh_flash_criterion_outer" class="sf_criterion_area"><div class="sf_criterion_row">
|
||||
EOF
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$("#dh_flash").click( function() {
|
||||
$("#ds_flash").slideToggle('normal', function() { $("#ds_flash").parent().resize(); $("#dh_flash").trigger('toggle_complete'); } );
|
||||
$("#dh_flash").toggleClass('open');
|
||||
$("#dh_flash > .sf_title").click( function() {
|
||||
$("#dh_flash").toggleClass('closed');
|
||||
});
|
||||
$('#ReloadMovieBtn').button().button('disable');
|
||||
EOF
|
||||
@@ -1476,9 +1476,8 @@ EOF
|
||||
$idx++;
|
||||
}
|
||||
$oP->add("<p style=\"text-align:right\"><button type=\"button\" id=\"ReloadMovieBtn\" onClick=\"DoReload()\">".Dict::S('UI:Button:Refresh')."</button></p>");
|
||||
$oP->add("</div></div></form>");
|
||||
$oP->add("</div>\n");
|
||||
$oP->add("<div class=\"HRDrawer\"></div>\n");
|
||||
$oP->add("<div id=\"dh_flash\" class=\"DrawerHandle\">".Dict::S('UI:ElementsDisplayed')."</div>\n");
|
||||
$oP->add("</div>\n"); // class="not-printable"
|
||||
|
||||
$aAdditionalContexts = array();
|
||||
|
||||
@@ -57,6 +57,7 @@ class EMail
|
||||
{
|
||||
$this->m_aData = array();
|
||||
$this->m_oMessage = Swift_Message::newInstance();
|
||||
$this->SetRecipientFrom(MetaModel::GetConfig()->Get('email_default_sender_address'), MetaModel::GetConfig()->Get('email_default_sender_label'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,6 +261,11 @@ class EMail
|
||||
|
||||
public function Send(&$aIssues, $bForceSynchronous = false, $oLog = null)
|
||||
{
|
||||
//select a default sender if none is provided.
|
||||
if(empty($this->m_aData['from']['address']) && !empty($this->m_aData['to'])){
|
||||
$this->SetRecipientFrom($this->m_aData['to']);
|
||||
}
|
||||
|
||||
if ($bForceSynchronous)
|
||||
{
|
||||
return $this->SendSynchronous($aIssues, $oLog);
|
||||
|
||||
@@ -184,9 +184,9 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
'table' => array('style', 'width', 'summary', 'align', 'border', 'cellpadding', 'cellspacing'),
|
||||
'thead' => array('style'),
|
||||
'tbody' => array('style'),
|
||||
'tr' => array('style'),
|
||||
'td' => array('style', 'colspan'),
|
||||
'th' => array('style'),
|
||||
'tr' => array('style', 'colspan', 'rowspan'),
|
||||
'td' => array('style', 'colspan', 'rowspan'),
|
||||
'th' => array('style', 'colspan', 'rowspan'),
|
||||
'fieldset' => array('style'),
|
||||
'legend' => array('style'),
|
||||
'font' => array('face', 'color', 'style', 'size'),
|
||||
|
||||
@@ -497,10 +497,10 @@ EOF
|
||||
*/
|
||||
class InlineImageGC implements iBackgroundProcess
|
||||
{
|
||||
public function GetPeriodicity()
|
||||
{
|
||||
return MetaModel::GetConfig()->Get('inline_image_garbage_collector_interval'); // run every definied time
|
||||
}
|
||||
public function GetPeriodicity()
|
||||
{
|
||||
return 3600; // Runs every hour
|
||||
}
|
||||
|
||||
public function Process($iTimeLimit)
|
||||
{
|
||||
|
||||
@@ -638,6 +638,20 @@ abstract class MetaModel
|
||||
return reset($aAttributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of attributes composing the friendlyname
|
||||
*
|
||||
* @param $sClass
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
final static public function GetFriendlyNameAttributeCodeList($sClass)
|
||||
{
|
||||
$aNameSpec = self::GetNameSpec($sClass);
|
||||
$aAttributes = $aNameSpec[1];
|
||||
return $aAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
*
|
||||
@@ -5034,6 +5048,7 @@ abstract class MetaModel
|
||||
// Check that any defined field exists
|
||||
//
|
||||
$aTableInfo['Fields'][$sKeyField]['used'] = true;
|
||||
$aFriendlynameAttcodes = self::GetFriendlyNameAttributeCodeList($sClass);
|
||||
foreach(self::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
if (!$oAttDef->CopyOnAllTables())
|
||||
@@ -5050,6 +5065,14 @@ abstract class MetaModel
|
||||
$aTableInfo['Fields'][$sField]['used'] = true;
|
||||
|
||||
$bIndexNeeded = $oAttDef->RequiresIndex();
|
||||
if (!$bIndexNeeded)
|
||||
{
|
||||
// Add an index on the columns of the friendlyname
|
||||
if (in_array($sField, $aFriendlynameAttcodes))
|
||||
{
|
||||
$bIndexNeeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
$sFieldDefinition = "`$sField` $sDBFieldSpec";
|
||||
if (!CMDBSource::IsField($sTable, $sField))
|
||||
@@ -5155,6 +5178,11 @@ abstract class MetaModel
|
||||
{
|
||||
$sIndexId = implode('_', $aColumns);
|
||||
|
||||
if (isset($aTableInfo['Indexes'][$sIndexId]['used']) && $aTableInfo['Indexes'][$sIndexId]['used'])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$aLength = self::DBGetIndexesLength($sClass, $aColumns, $aTableInfo);
|
||||
$aTableInfo['Indexes'][$sIndexId]['used'] = true;
|
||||
|
||||
@@ -6185,7 +6213,7 @@ abstract class MetaModel
|
||||
{
|
||||
$sQuerySign .= '_all_';
|
||||
}
|
||||
if (count($aModifierProperties))
|
||||
if (is_array($aModifierProperties) && (count($aModifierProperties) > 0))
|
||||
{
|
||||
array_multisort($aModifierProperties);
|
||||
$sModifierProperties = json_encode($aModifierProperties);
|
||||
|
||||
@@ -44,7 +44,15 @@ abstract class Expression
|
||||
*/
|
||||
abstract public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true);
|
||||
|
||||
// recursive rendering (aArgs used as input by default, or used as output if bRetrofitParams set to True
|
||||
/**
|
||||
* recursive rendering
|
||||
*
|
||||
* @param array $aArgs used as input by default, or used as output if bRetrofitParams set to True
|
||||
* @param bool $bRetrofitParams
|
||||
*
|
||||
* @return array|string
|
||||
* @throws \MissingQueryArgument
|
||||
*/
|
||||
abstract public function Render(&$aArgs = null, $bRetrofitParams = false);
|
||||
|
||||
/**
|
||||
@@ -295,6 +303,10 @@ class BinaryExpression extends Expression
|
||||
{
|
||||
throw new CoreException('Expecting an Expression object on the right hand', array('found_class' => get_class($oRightExpr)));
|
||||
}
|
||||
if ( (($sOperator == "IN") || ($sOperator == "NOT IN")) && !$oRightExpr instanceof ListExpression)
|
||||
{
|
||||
throw new CoreException("Expecting a List Expression object on the right hand for operator $sOperator", array('found_class' => get_class($oRightExpr)));
|
||||
}
|
||||
$this->m_oLeftExpr = $oLeftExpr;
|
||||
$this->m_oRightExpr = $oRightExpr;
|
||||
$this->m_sOperator = $sOperator;
|
||||
@@ -538,27 +550,27 @@ class BinaryExpression extends Expression
|
||||
$oAttDef = $oRightExpr->GetAttDef($oSearch->GetJoinedClasses());
|
||||
$aCriteriaLeft = $oLeftExpr->GetCriterion($oSearch, $aArgs, $bRetrofitParams, $oAttDef);
|
||||
|
||||
$aCriteria = array_merge($aCriteriaRight, $aCriteriaLeft);
|
||||
// switch left and right expressions so reverse the operator
|
||||
// Note that the operation is the same so < becomes > and not >=
|
||||
switch ($this->GetOperator())
|
||||
{
|
||||
case '>':
|
||||
$aCriteria['operator'] = '<';
|
||||
$sOperator = '<';
|
||||
break;
|
||||
case '<':
|
||||
$aCriteria['operator'] = '>';
|
||||
$sOperator = '>';
|
||||
break;
|
||||
case '>=':
|
||||
$aCriteria['operator'] = '<=';
|
||||
$sOperator = '<=';
|
||||
break;
|
||||
case '<=':
|
||||
$aCriteria['operator'] = '>=';
|
||||
$sOperator = '>=';
|
||||
break;
|
||||
default:
|
||||
$aCriteria['operator'] = $this->GetOperator();
|
||||
$sOperator = $this->GetOperator();
|
||||
break;
|
||||
}
|
||||
$aCriteria = self::MergeCriteria($aCriteriaRight, $aCriteriaLeft, $sOperator);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -567,8 +579,7 @@ class BinaryExpression extends Expression
|
||||
$oAttDef = $oLeftExpr->GetAttDef($oSearch->GetJoinedClasses());
|
||||
$aCriteriaRight = $oRightExpr->GetCriterion($oSearch, $aArgs, $bRetrofitParams, $oAttDef);
|
||||
|
||||
$aCriteria = array_merge($aCriteriaLeft, $aCriteriaRight);
|
||||
$aCriteria['operator'] = $this->GetOperator();
|
||||
$aCriteria = self::MergeCriteria($aCriteriaLeft, $aCriteriaRight, $this->GetOperator());
|
||||
}
|
||||
$aCriteria['oql'] = $this->Render($aArgs, $bRetrofitParams);
|
||||
$aCriteria['label'] = $this->Display($oSearch, $aArgs, $oAttDef);
|
||||
@@ -581,6 +592,30 @@ class BinaryExpression extends Expression
|
||||
|
||||
return $aCriteria;
|
||||
}
|
||||
|
||||
protected static function MergeCriteria($aCriteriaLeft, $aCriteriaRight, $sOperator)
|
||||
{
|
||||
$aCriteriaOverride = array();
|
||||
$aCriteriaOverride['operator'] = $sOperator;
|
||||
if ($sOperator == 'OR')
|
||||
{
|
||||
if (isset($aCriteriaLeft['ref']) && isset($aCriteriaRight['ref']) && ($aCriteriaLeft['ref'] == $aCriteriaRight['ref']))
|
||||
{
|
||||
if (isset($aCriteriaLeft['widget']) && isset($aCriteriaRight['widget']) && ($aCriteriaLeft['widget'] == AttributeDefinition::SEARCH_WIDGET_TYPE_HIERARCHICAL_KEY) && ($aCriteriaRight['widget'] == AttributeDefinition::SEARCH_WIDGET_TYPE_HIERARCHICAL_KEY))
|
||||
{
|
||||
$aCriteriaOverride['operator'] = 'IN';
|
||||
$aCriteriaOverride['is_hierarchical'] = true;
|
||||
|
||||
if (isset($aCriteriaLeft['values']) && isset($aCriteriaRight['values']))
|
||||
{
|
||||
$aCriteriaOverride['values'] = array_merge($aCriteriaLeft['values'], $aCriteriaRight['values']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge($aCriteriaLeft, $aCriteriaRight, $aCriteriaOverride);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -685,7 +720,11 @@ class ScalarExpression extends UnaryExpression
|
||||
{
|
||||
/** @var AttributeExternalKey $oAttDef */
|
||||
$sTarget = $oAttDef->GetTargetClass();
|
||||
$oObj = MetaModel::GetObject($sTarget, $this->m_value);
|
||||
$oObj = MetaModel::GetObject($sTarget, $this->m_value, false);
|
||||
if (empty($oObj))
|
||||
{
|
||||
return Dict::S('Enum:Undefined');
|
||||
}
|
||||
|
||||
return $oObj->Get("friendlyname");
|
||||
} catch (CoreException $e)
|
||||
@@ -841,74 +880,6 @@ class FalseExpression extends ScalarExpression
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class ExternalFieldExpression
|
||||
*
|
||||
* @todo verify if all required methods are implemented (ie: at first I missed the `Render` method, so DBObjectSearch::toOQL() result was wrong)
|
||||
*
|
||||
*/
|
||||
class ExternalFieldExpression extends UnaryExpression
|
||||
{
|
||||
/**
|
||||
* @var array[] array containing the shorthand chained fields & their classes
|
||||
* ['sClass'] string the Class
|
||||
* ['sAlias'] string the Class alias
|
||||
* ['sAttCode'] string the attribute code
|
||||
*/
|
||||
protected $m_aFields = array();
|
||||
protected $m_sName;
|
||||
|
||||
public function __construct($sName, $aFields)
|
||||
{
|
||||
parent::__construct($sName);
|
||||
|
||||
$this->SetFields($aFields);
|
||||
}
|
||||
|
||||
public function SetFields($aFields)
|
||||
{
|
||||
$this->m_aFields = $aFields;
|
||||
}
|
||||
|
||||
public function GetFields()
|
||||
{
|
||||
return $this->m_aFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* used by DBObjectSearch::ShorthandExpansion(). it result in the ExternalFieldExpression being replaced by a FieldExpression mathching the final entry in self::$m_aFields
|
||||
*
|
||||
* @param array $aTranslationData
|
||||
* @param bool $bMatchAll
|
||||
* @param bool $bMarkFieldsAsResolved
|
||||
*
|
||||
* @return Expression|FieldExpression|UnaryExpression
|
||||
*/
|
||||
public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
|
||||
{
|
||||
$aFields = $this->GetFields();
|
||||
$aLastField = end($aFields);
|
||||
|
||||
$oRet = new FieldExpression($aLastField['sAttCode'], $aLastField['sAlias']);
|
||||
|
||||
return $oRet;
|
||||
}
|
||||
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
$aAttCode = array();
|
||||
|
||||
$aFields = $this->GetFields();
|
||||
foreach ($aFields as $field) {
|
||||
$aAttCode[] = $field['sAttCode'];
|
||||
}
|
||||
|
||||
return $aFields[0]['sAlias'] . '.' . implode('->', $aAttCode);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FieldExpression extends UnaryExpression
|
||||
{
|
||||
protected $m_sParent;
|
||||
@@ -1327,6 +1298,14 @@ class VariableExpression extends UnaryExpression
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
|
||||
/**
|
||||
* @param null $aArgs
|
||||
* @param bool $bRetrofitParams
|
||||
*
|
||||
* @return array|string
|
||||
* @throws \MissingQueryArgument
|
||||
*/
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
if (is_null($aArgs))
|
||||
|
||||
@@ -115,8 +115,6 @@ class OQLLexerRaw
|
||||
'/\GWHERE/ ',
|
||||
'/\GJOIN/ ',
|
||||
'/\GON/ ',
|
||||
'/\G->/ ',
|
||||
'/\G:/ ',
|
||||
'/\G\// ',
|
||||
'/\G\\*/ ',
|
||||
'/\G\\+/ ',
|
||||
@@ -318,339 +316,329 @@ class OQLLexerRaw
|
||||
function yy_r1_8($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::ARROW;
|
||||
$this->token = OQLParser::MATH_DIV;
|
||||
}
|
||||
function yy_r1_9($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::COLON;
|
||||
$this->token = OQLParser::MATH_MULT;
|
||||
}
|
||||
function yy_r1_10($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::MATH_DIV;
|
||||
$this->token = OQLParser::MATH_PLUS;
|
||||
}
|
||||
function yy_r1_11($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::MATH_MULT;
|
||||
$this->token = OQLParser::MATH_MINUS;
|
||||
}
|
||||
function yy_r1_12($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::MATH_PLUS;
|
||||
$this->token = OQLParser::LOG_AND;
|
||||
}
|
||||
function yy_r1_13($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::MATH_MINUS;
|
||||
$this->token = OQLParser::LOG_OR;
|
||||
}
|
||||
function yy_r1_14($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LOG_AND;
|
||||
$this->token = OQLParser::BITWISE_OR;
|
||||
}
|
||||
function yy_r1_15($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LOG_OR;
|
||||
$this->token = OQLParser::BITWISE_AND;
|
||||
}
|
||||
function yy_r1_16($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BITWISE_OR;
|
||||
$this->token = OQLParser::BITWISE_XOR;
|
||||
}
|
||||
function yy_r1_17($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BITWISE_AND;
|
||||
$this->token = OQLParser::BITWISE_LEFT_SHIFT;
|
||||
}
|
||||
function yy_r1_18($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BITWISE_XOR;
|
||||
$this->token = OQLParser::BITWISE_RIGHT_SHIFT;
|
||||
}
|
||||
function yy_r1_19($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BITWISE_LEFT_SHIFT;
|
||||
$this->token = OQLParser::COMA;
|
||||
}
|
||||
function yy_r1_20($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BITWISE_RIGHT_SHIFT;
|
||||
$this->token = OQLParser::PAR_OPEN;
|
||||
}
|
||||
function yy_r1_21($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::COMA;
|
||||
$this->token = OQLParser::PAR_CLOSE;
|
||||
}
|
||||
function yy_r1_22($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::PAR_OPEN;
|
||||
$this->token = OQLParser::REGEXP;
|
||||
}
|
||||
function yy_r1_23($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::PAR_CLOSE;
|
||||
$this->token = OQLParser::EQ;
|
||||
}
|
||||
function yy_r1_24($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::REGEXP;
|
||||
$this->token = OQLParser::NOT_EQ;
|
||||
}
|
||||
function yy_r1_25($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::EQ;
|
||||
$this->token = OQLParser::GT;
|
||||
}
|
||||
function yy_r1_26($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_EQ;
|
||||
$this->token = OQLParser::LT;
|
||||
}
|
||||
function yy_r1_27($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::GT;
|
||||
$this->token = OQLParser::GE;
|
||||
}
|
||||
function yy_r1_28($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LT;
|
||||
$this->token = OQLParser::LE;
|
||||
}
|
||||
function yy_r1_29($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::GE;
|
||||
$this->token = OQLParser::LIKE;
|
||||
}
|
||||
function yy_r1_30($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LE;
|
||||
$this->token = OQLParser::NOT_LIKE;
|
||||
}
|
||||
function yy_r1_31($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LIKE;
|
||||
$this->token = OQLParser::IN;
|
||||
}
|
||||
function yy_r1_32($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_LIKE;
|
||||
$this->token = OQLParser::NOT_IN;
|
||||
}
|
||||
function yy_r1_33($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::IN;
|
||||
$this->token = OQLParser::INTERVAL;
|
||||
}
|
||||
function yy_r1_34($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_IN;
|
||||
$this->token = OQLParser::F_IF;
|
||||
}
|
||||
function yy_r1_35($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::INTERVAL;
|
||||
$this->token = OQLParser::F_ELT;
|
||||
}
|
||||
function yy_r1_36($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_IF;
|
||||
$this->token = OQLParser::F_COALESCE;
|
||||
}
|
||||
function yy_r1_37($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_ELT;
|
||||
$this->token = OQLParser::F_ISNULL;
|
||||
}
|
||||
function yy_r1_38($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_COALESCE;
|
||||
$this->token = OQLParser::F_CONCAT;
|
||||
}
|
||||
function yy_r1_39($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_ISNULL;
|
||||
$this->token = OQLParser::F_SUBSTR;
|
||||
}
|
||||
function yy_r1_40($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_CONCAT;
|
||||
$this->token = OQLParser::F_TRIM;
|
||||
}
|
||||
function yy_r1_41($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_SUBSTR;
|
||||
$this->token = OQLParser::F_DATE;
|
||||
}
|
||||
function yy_r1_42($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_TRIM;
|
||||
$this->token = OQLParser::F_DATE_FORMAT;
|
||||
}
|
||||
function yy_r1_43($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE;
|
||||
$this->token = OQLParser::F_CURRENT_DATE;
|
||||
}
|
||||
function yy_r1_44($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE_FORMAT;
|
||||
$this->token = OQLParser::F_NOW;
|
||||
}
|
||||
function yy_r1_45($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_CURRENT_DATE;
|
||||
$this->token = OQLParser::F_TIME;
|
||||
}
|
||||
function yy_r1_46($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_NOW;
|
||||
$this->token = OQLParser::F_TO_DAYS;
|
||||
}
|
||||
function yy_r1_47($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_TIME;
|
||||
$this->token = OQLParser::F_FROM_DAYS;
|
||||
}
|
||||
function yy_r1_48($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_TO_DAYS;
|
||||
$this->token = OQLParser::F_YEAR;
|
||||
}
|
||||
function yy_r1_49($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_FROM_DAYS;
|
||||
$this->token = OQLParser::F_MONTH;
|
||||
}
|
||||
function yy_r1_50($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_YEAR;
|
||||
$this->token = OQLParser::F_DAY;
|
||||
}
|
||||
function yy_r1_51($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_MONTH;
|
||||
$this->token = OQLParser::F_HOUR;
|
||||
}
|
||||
function yy_r1_52($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DAY;
|
||||
$this->token = OQLParser::F_MINUTE;
|
||||
}
|
||||
function yy_r1_53($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_HOUR;
|
||||
$this->token = OQLParser::F_SECOND;
|
||||
}
|
||||
function yy_r1_54($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_MINUTE;
|
||||
$this->token = OQLParser::F_DATE_ADD;
|
||||
}
|
||||
function yy_r1_55($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_SECOND;
|
||||
$this->token = OQLParser::F_DATE_SUB;
|
||||
}
|
||||
function yy_r1_56($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE_ADD;
|
||||
$this->token = OQLParser::F_ROUND;
|
||||
}
|
||||
function yy_r1_57($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE_SUB;
|
||||
$this->token = OQLParser::F_FLOOR;
|
||||
}
|
||||
function yy_r1_58($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_ROUND;
|
||||
$this->token = OQLParser::F_INET_ATON;
|
||||
}
|
||||
function yy_r1_59($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_FLOOR;
|
||||
$this->token = OQLParser::F_INET_NTOA;
|
||||
}
|
||||
function yy_r1_60($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_INET_ATON;
|
||||
$this->token = OQLParser::BELOW;
|
||||
}
|
||||
function yy_r1_61($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_INET_NTOA;
|
||||
$this->token = OQLParser::BELOW_STRICT;
|
||||
}
|
||||
function yy_r1_62($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BELOW;
|
||||
$this->token = OQLParser::NOT_BELOW;
|
||||
}
|
||||
function yy_r1_63($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BELOW_STRICT;
|
||||
$this->token = OQLParser::NOT_BELOW_STRICT;
|
||||
}
|
||||
function yy_r1_64($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_BELOW;
|
||||
$this->token = OQLParser::ABOVE;
|
||||
}
|
||||
function yy_r1_65($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_BELOW_STRICT;
|
||||
$this->token = OQLParser::ABOVE_STRICT;
|
||||
}
|
||||
function yy_r1_66($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::ABOVE;
|
||||
$this->token = OQLParser::NOT_ABOVE;
|
||||
}
|
||||
function yy_r1_67($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::ABOVE_STRICT;
|
||||
$this->token = OQLParser::NOT_ABOVE_STRICT;
|
||||
}
|
||||
function yy_r1_68($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_ABOVE;
|
||||
$this->token = OQLParser::HEXVAL;
|
||||
}
|
||||
function yy_r1_69($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_ABOVE_STRICT;
|
||||
$this->token = OQLParser::NUMVAL;
|
||||
}
|
||||
function yy_r1_70($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::HEXVAL;
|
||||
$this->token = OQLParser::STRVAL;
|
||||
}
|
||||
function yy_r1_71($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NUMVAL;
|
||||
$this->token = OQLParser::NAME;
|
||||
}
|
||||
function yy_r1_72($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::STRVAL;
|
||||
}
|
||||
function yy_r1_73($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NAME;
|
||||
}
|
||||
function yy_r1_74($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::VARNAME;
|
||||
}
|
||||
function yy_r1_75($yy_subpatterns)
|
||||
function yy_r1_73($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::DOT;
|
||||
|
||||
@@ -88,8 +88,6 @@ where = "WHERE"
|
||||
join = "JOIN"
|
||||
on = "ON"
|
||||
coma = ","
|
||||
arrow = "->"
|
||||
colon = ":"
|
||||
par_open = "("
|
||||
par_close = ")"
|
||||
math_div = "/"
|
||||
@@ -172,7 +170,7 @@ numval = /([0-9]+)/
|
||||
strval = /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/
|
||||
name = /([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/
|
||||
varname = /:([_a-zA-Z][_a-zA-Z0-9]*->[_a-zA-Z][_a-zA-Z0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)/
|
||||
dot = "."
|
||||
dot = "."
|
||||
*/
|
||||
|
||||
/*!lex2php
|
||||
@@ -200,12 +198,6 @@ join {
|
||||
on {
|
||||
$this->token = OQLParser::ON;
|
||||
}
|
||||
arrow {
|
||||
$this->token = OQLParser::ARROW;
|
||||
}
|
||||
colon {
|
||||
$this->token = OQLParser::COLON;
|
||||
}
|
||||
math_div {
|
||||
$this->token = OQLParser::MATH_DIV;
|
||||
}
|
||||
@@ -404,7 +396,6 @@ varname {
|
||||
dot {
|
||||
$this->token = OQLParser::DOT;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -154,16 +154,8 @@ scalar(A) ::= str_scalar(X). { A = X; }
|
||||
num_scalar(A) ::= num_value(X). { A = new ScalarOqlExpression(X); }
|
||||
str_scalar(A) ::= str_value(X). { A = new ScalarOqlExpression(X); }
|
||||
|
||||
basic_field_id(A) ::= name(X). { A = new FieldOqlExpression(X); }
|
||||
basic_field_id(A) ::= class_name(X) DOT name(Y). { A = new FieldOqlExpression(Y, X); }
|
||||
|
||||
field_id(A) ::= basic_field_id(X). { A = X; }
|
||||
field_id(A) ::= field_id(X) ARROW name(Y).
|
||||
{
|
||||
$expr = new FieldOqlExpression(Y);
|
||||
A = new ExternalFieldOqlExpression(X, $expr);
|
||||
}
|
||||
|
||||
field_id(A) ::= name(X). { A = new FieldOqlExpression(X); }
|
||||
field_id(A) ::= class_name(X) DOT name(Y). { A = new FieldOqlExpression(Y, X); }
|
||||
class_name(A) ::= name(X). { A=X; }
|
||||
|
||||
|
||||
|
||||
@@ -168,108 +168,9 @@ class ScalarOqlExpression extends ScalarExpression implements CheckableExpressio
|
||||
}
|
||||
}
|
||||
|
||||
class ExternalFieldOqlExpression extends ExternalFieldExpression implements CheckableExpression
|
||||
{
|
||||
protected $m_aExpression = array();
|
||||
protected $m_sName = null;
|
||||
|
||||
|
||||
function __construct($oExpr1, $oExpr2)
|
||||
{
|
||||
$this->m_sName = '';
|
||||
|
||||
if ($oExpr1 instanceof ExternalFieldOqlExpression)
|
||||
{
|
||||
$this->m_aExpression = $oExpr1->GetExpressions();
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->m_aExpression[] = $oExpr1;
|
||||
}
|
||||
$this->m_aExpression[] = $oExpr2;
|
||||
$this->m_sName = $oExpr1->GetValue().'->'.$oExpr2->GetValue();
|
||||
|
||||
|
||||
parent::__construct($this->m_sName, $this->m_aExpression);
|
||||
}
|
||||
|
||||
public function GetExpressions()
|
||||
{
|
||||
return $this->m_aExpression;
|
||||
}
|
||||
|
||||
public function GetName()
|
||||
{
|
||||
return $this->m_sName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the validity of the expression with regard to the data model
|
||||
* and the query in which it is used
|
||||
*
|
||||
* @param ModelReflection $oModelReflection MetaModel to consider
|
||||
* @param array $aAliases Aliases to class names (for the current query)
|
||||
* @param string $sSourceQuery For the reporting
|
||||
*
|
||||
* @throws OqlNormalizeException
|
||||
*/
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
|
||||
{
|
||||
$sParentAlias = null;
|
||||
foreach($this->m_aExpression as $i => $oFieldOqlExpression)
|
||||
{
|
||||
|
||||
if (is_null($sParentAlias))
|
||||
{
|
||||
$oFieldOqlExpression->RefreshAlias($oModelReflection, $aAliases, $sSourceQuery, FieldOqlExpression::ALLOW_EXTERNAL_FIELDS);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oFieldOqlExpression->SetParent($sParentAlias);
|
||||
}
|
||||
$oFieldOqlExpression->Check($oModelReflection, $aAliases, $sSourceQuery);
|
||||
|
||||
$sClass = $aAliases[$oFieldOqlExpression->GetParent()];
|
||||
|
||||
$bLastIteration = ($i == (count($this->m_aExpression) - 1));
|
||||
|
||||
if (!$bLastIteration)
|
||||
{
|
||||
if ($oFieldOqlExpression->GetName() == 'id')
|
||||
{
|
||||
$sTargetClass = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sTargetClass = $oModelReflection->GetAttributeProperty($sClass, $oFieldOqlExpression->GetName(), 'targetclass');
|
||||
}
|
||||
|
||||
if (is_null($sTargetClass))
|
||||
{
|
||||
throw new OqlNormalizeException('Forbidden operation for attribute', $sSourceQuery, $oFieldOqlExpression->GetNameDetails(), $oModelReflection->GetFiltersList($sClass));
|
||||
}
|
||||
$aAliases[$sTargetClass] = $sTargetClass;
|
||||
$sParentAlias = $sTargetClass;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($oFieldOqlExpression->GetName() != 'id') //on lastIteration, the id field can be used, but since it is not supporter by IsValidAttCode we must avoid it
|
||||
{
|
||||
if (!$oModelReflection->IsValidAttCode($sClass, $oFieldOqlExpression->GetName()))
|
||||
{
|
||||
throw new OqlNormalizeException('Invalid attribute', $sSourceQuery, $oFieldOqlExpression->GetNameDetails(), $oModelReflection->GetFiltersList($sClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FieldOqlExpression extends FieldExpression implements CheckableExpression
|
||||
{
|
||||
const ALLOW_EXTERNAL_FIELDS = true;
|
||||
const DISALLOW_EXTERNAL_FIELDS = false;
|
||||
protected $m_oParent;
|
||||
protected $m_oParent;
|
||||
protected $m_oName;
|
||||
|
||||
public function __construct($oName, $oParent = null)
|
||||
@@ -302,7 +203,23 @@ class FieldOqlExpression extends FieldExpression implements CheckableExpression
|
||||
{
|
||||
// Try to find an alias
|
||||
// Build an array of field => array of aliases
|
||||
$this->RefreshAlias($oModelReflection, $aAliases, $sSourceQuery);
|
||||
$aFieldClasses = array();
|
||||
foreach($aAliases as $sAlias => $sReal)
|
||||
{
|
||||
foreach($oModelReflection->GetFiltersList($sReal) as $sAnFltCode)
|
||||
{
|
||||
$aFieldClasses[$sAnFltCode][] = $sAlias;
|
||||
}
|
||||
}
|
||||
if (!array_key_exists($sFltCode, $aFieldClasses))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown filter code', $sSourceQuery, $this->GetNameDetails(), array_keys($aFieldClasses));
|
||||
}
|
||||
if (count($aFieldClasses[$sFltCode]) > 1)
|
||||
{
|
||||
throw new OqlNormalizeException('Ambiguous filter code', $sSourceQuery, $this->GetNameDetails());
|
||||
}
|
||||
$sClassAlias = $aFieldClasses[$sFltCode][0];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -317,62 +234,6 @@ class FieldOqlExpression extends FieldExpression implements CheckableExpression
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by self::Check and ExternalFieldOqlExpression::Check => throws exception if error
|
||||
*
|
||||
* this method has a side effect : if not previously set $this->m_sParent if computed then set.
|
||||
*
|
||||
* @param \ModelReflection $oModelReflection
|
||||
* @param $aAliases
|
||||
* @param $sSourceQuery
|
||||
* @param bool $bAllowExternalFields should external fields be authorised? used only by @see ExternalFieldOqlExpression
|
||||
*
|
||||
* @throws OqlNormalizeException
|
||||
*/
|
||||
public function RefreshAlias(ModelReflection $oModelReflection, $aAliases, $sSourceQuery, $bAllowExternalFields = self::DISALLOW_EXTERNAL_FIELDS)
|
||||
{
|
||||
$sClassAlias = $this->GetParent();
|
||||
$sFltCode = $this->GetName();
|
||||
if (empty($sClassAlias))
|
||||
{
|
||||
// Try to find an alias
|
||||
// Build an array of field => array of aliases
|
||||
$aFieldClasses = array();
|
||||
foreach($aAliases as $sAlias => $sReal)
|
||||
{
|
||||
foreach($oModelReflection->GetFiltersList($sReal) as $sAnFltCode)
|
||||
{
|
||||
$aFieldClasses[$sAnFltCode][] = $sAlias;
|
||||
}
|
||||
}
|
||||
if (!array_key_exists($sFltCode, $aFieldClasses))
|
||||
{
|
||||
if (count($aAliases) > 1)
|
||||
{
|
||||
throw new OqlNormalizeException('Ambiguous filter code', $sSourceQuery, $this->GetNameDetails(), array_keys($aFieldClasses));
|
||||
}
|
||||
|
||||
$sClassAlias = reset($aAliases);
|
||||
if (self::DISALLOW_EXTERNAL_FIELDS == $bAllowExternalFields || false == $oModelReflection->IsValidAttCode($sClassAlias, $sFltCode))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown filter code', $sSourceQuery, $this->GetNameDetails(), array_keys($aFieldClasses));
|
||||
}
|
||||
$this->SetParent($sClassAlias);
|
||||
|
||||
}
|
||||
elseif (count($aFieldClasses[$sFltCode]) > 1)
|
||||
{
|
||||
throw new OqlNormalizeException('Ambiguous filter code', $sSourceQuery, $this->GetNameDetails());
|
||||
}
|
||||
else
|
||||
{
|
||||
$sClassAlias = $aFieldClasses[$sFltCode][0];
|
||||
$this->SetParent($sClassAlias);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VariableOqlExpression extends VariableExpression implements CheckableExpression
|
||||
|
||||
@@ -1 +1 @@
|
||||
2018-04-26
|
||||
2015-08-31
|
||||
|
||||
@@ -74,6 +74,14 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
*/
|
||||
protected $iCursor = 0;
|
||||
|
||||
/**
|
||||
* __toString magical function overload.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* ormLinkSet constructor.
|
||||
* @param $sHostClass
|
||||
@@ -531,6 +539,27 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of all modified (added, modified and removed) links
|
||||
*
|
||||
* @return array of link objects
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function ListModifiedLinks()
|
||||
{
|
||||
$aAdded = $this->aAdded;
|
||||
$aModified = $this->aModified;
|
||||
$aRemoved = array();
|
||||
if (count($this->aRemoved) > 0)
|
||||
{
|
||||
$oSearch = new DBObjectSearch($this->sClass);
|
||||
$oSearch->AddCondition('id', $this->aRemoved, 'IN');
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$aRemoved = $oSet->ToArray();
|
||||
}
|
||||
return array_merge($aAdded, $aModified, $aRemoved);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DBObject $oHostObject
|
||||
*/
|
||||
|
||||
@@ -433,15 +433,19 @@ class RelationGraph extends SimpleGraph
|
||||
}
|
||||
elseif ($oObjectNode->GetProperty('developped', false))
|
||||
{
|
||||
// No need to execute the queries again... just dig into the nodes down/up to iMaxDepth
|
||||
//
|
||||
$aRelatedEdges = $bDown ? $oObjectNode->GetOutgoingEdges() : $oObjectNode->GetIncomingEdges();
|
||||
foreach ($aRelatedEdges as $oRelatedEdge)
|
||||
{
|
||||
$oRelatedNode = $bDown ? $oRelatedEdge->GetSinkNode() : $oRelatedEdge->GetSourceNode();
|
||||
// Recurse (decrement the depth)
|
||||
$this->AddRelatedObjects($sRelCode, $bDown, $oRelatedNode, $iMaxDepth - 1, $bEnableRedundancy);
|
||||
}
|
||||
// No need to explore the underlying graph at all. We can stop here since the node has already been developped.
|
||||
// Otherwise in case of "loops" in the graph we would recurse up to the max depth limit
|
||||
// without producing any difference in the resulting graph... but potentially taking a LOOOONG time.
|
||||
return;
|
||||
|
||||
// Former code was
|
||||
//$aRelatedEdges = $bDown ? $oObjectNode->GetOutgoingEdges() : $oObjectNode->GetIncomingEdges();
|
||||
//foreach ($aRelatedEdges as $oRelatedEdge)
|
||||
//{
|
||||
// $oRelatedNode = $bDown ? $oRelatedEdge->GetSinkNode() : $oRelatedEdge->GetSourceNode();
|
||||
// // Recurse (decrement the depth)
|
||||
// $this->AddRelatedObjects($sRelCode, $bDown, $oRelatedNode, $iMaxDepth - 1, $bEnableRedundancy);
|
||||
//}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -339,6 +339,14 @@ class SQLObjectQuery extends SQLQuery
|
||||
$this->PrepareRendering();
|
||||
$sFrom = self::ClauseFrom($this->__aFrom, $sIndent);
|
||||
$sWhere = self::ClauseWhere($this->m_oConditionExpr, $aArgs);
|
||||
if ($iLimitCount > 0)
|
||||
{
|
||||
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLimit = '';
|
||||
}
|
||||
if ($bGetCount)
|
||||
{
|
||||
if (count($this->__aSelectedIdFields) > 0)
|
||||
@@ -349,11 +357,13 @@ class SQLObjectQuery extends SQLQuery
|
||||
$aCountFields[] = "COALESCE($sFieldExpr, 0)"; // Null values are excluded from the count
|
||||
}
|
||||
$sCountFields = implode(', ', $aCountFields);
|
||||
$sSQL = "SELECT$sLineSep COUNT(DISTINCT $sCountFields) AS COUNT$sLineSep FROM $sFrom$sLineSep WHERE $sWhere";
|
||||
// Count can be limited for performance reason, in this case the total amount is not important,
|
||||
// we only need to know if the number of entries is greater than a certain amount.
|
||||
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep DISTINCT $sCountFields $sLineSep FROM $sFrom$sLineSep WHERE $sWhere $sLimit) AS _tatooine_";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSQL = "SELECT$sLineSep COUNT(*) AS COUNT$sLineSep FROM $sFrom$sLineSep WHERE $sWhere";
|
||||
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep 1 $sLineSep FROM $sFrom$sLineSep WHERE $sWhere $sLimit) AS _tatooine_";
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -364,14 +374,7 @@ class SQLObjectQuery extends SQLQuery
|
||||
{
|
||||
$sOrderBy = "ORDER BY $sOrderBy$sLineSep";
|
||||
}
|
||||
if ($iLimitCount > 0)
|
||||
{
|
||||
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLimit = '';
|
||||
}
|
||||
|
||||
$sSQL = "SELECT$sLineSep DISTINCT $sSelect$sLineSep FROM $sFrom$sLineSep WHERE $sWhere$sLineSep $sOrderBy $sLimit";
|
||||
}
|
||||
return $sSQL;
|
||||
|
||||
@@ -103,29 +103,33 @@ class SQLUnionQuery extends SQLQuery
|
||||
// Render SELECTS without orderby/limit/count
|
||||
$aSelects[] = $oSQLQuery->RenderSelect(array(), $aArgs, 0, 0, false, $bBeautifulQuery);
|
||||
}
|
||||
$sSelects = '('.implode(")$sLineSep UNION$sLineSep(", $aSelects).')';
|
||||
if ($iLimitCount > 0)
|
||||
{
|
||||
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLimit = '';
|
||||
}
|
||||
|
||||
if ($bGetCount)
|
||||
{
|
||||
$sSelects = '('.implode(" $sLimit)$sLineSep UNION$sLineSep(", $aSelects)." $sLimit)";
|
||||
$sFrom = "($sLineSep$sSelects$sLineSep) as __selects__";
|
||||
$sSQL = "SELECT$sLineSep COUNT(*) AS COUNT$sLineSep FROM $sFrom$sLineSep";
|
||||
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep 1 $sLineSep FROM $sFrom$sLineSep) AS _union_tatooine_";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sOrderBy = $this->aQueries[0]->RenderOrderByClause($aOrderBy);
|
||||
if (!empty($sOrderBy))
|
||||
{
|
||||
$sOrderBy = "ORDER BY $sOrderBy$sLineSep";
|
||||
}
|
||||
if ($iLimitCount > 0)
|
||||
{
|
||||
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
|
||||
$sOrderBy = "ORDER BY $sOrderBy$sLineSep $sLimit";
|
||||
$sSQL = '('.implode(")$sLineSep UNION$sLineSep (", $aSelects).')'.$sLineSep.$sOrderBy;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLimit = '';
|
||||
$sSQL = '('.implode(" $sLimit)$sLineSep UNION$sLineSep (", $aSelects)." $sLimit)";
|
||||
}
|
||||
$sSQL = $sSelects.$sLineSep.$sOrderBy.' '.$sLimit;
|
||||
}
|
||||
return $sSQL;
|
||||
}
|
||||
|
||||
@@ -283,18 +283,21 @@ abstract class User extends cmdbAbstractObject
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check that this user has at least one profile assigned
|
||||
$oSet = $this->Get('profile_list');
|
||||
if ($oSet->Count() == 0)
|
||||
// Check that this user has at least one profile assigned when profiles have changed
|
||||
if (array_key_exists('profile_list', $aChanges))
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:AtLeastOneProfileIsNeeded');
|
||||
$oSet = $this->Get('profile_list');
|
||||
if ($oSet->Count() == 0)
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:AtLeastOneProfileIsNeeded');
|
||||
}
|
||||
}
|
||||
// Only administrators can manage administrators
|
||||
if (UserRights::IsAdministrator($this) && !UserRights::IsAdministrator())
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::Format('UI:Login:Error:AccessRestricted');
|
||||
}
|
||||
// Check users with restricted organizations
|
||||
|
||||
if (!UserRights::IsAdministrator())
|
||||
{
|
||||
$oUser = UserRights::GetUserObject();
|
||||
@@ -304,19 +307,28 @@ abstract class User extends cmdbAbstractObject
|
||||
$aOrgs = $oAddon->GetUserOrgs($oUser, '');
|
||||
if (count($aOrgs) > 0)
|
||||
{
|
||||
/** @var ORMLinkset $oSet */
|
||||
$oSet = $this->Get('allowed_org_list');
|
||||
if ($oSet->Count() == 0)
|
||||
// Check that the modified User belongs to one of our organization
|
||||
if (!in_array($this->GetOriginal('org_id'), $aOrgs) || !in_array($this->Get('org_id'), $aOrgs))
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:AtLeastOneOrganizationIsNeeded');
|
||||
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:UserOrganizationNotAllowed');
|
||||
}
|
||||
else
|
||||
// Check users with restricted organizations when allowed organizations have changed
|
||||
if ($this->IsNew() || array_key_exists('allowed_org_list', $aChanges))
|
||||
{
|
||||
while ($oUserOrg = $oSet->Fetch())
|
||||
$oSet = $this->get('allowed_org_list');
|
||||
if ($oSet->Count() == 0)
|
||||
{
|
||||
if (!in_array($oUserOrg->Get('allowed_org_id'), $aOrgs))
|
||||
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:AtLeastOneOrganizationIsNeeded');
|
||||
}
|
||||
else
|
||||
{
|
||||
$aModifiedLinks = $oSet->ListModifiedLinks();
|
||||
foreach($aModifiedLinks as $oLink)
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:OrganizationNotAllowed');
|
||||
if (!in_array($oLink->Get('allowed_org_id'), $aOrgs))
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:OrganizationNotAllowed');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -961,10 +973,12 @@ class UserRights
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional filter for organization silos to all the requests.
|
||||
*
|
||||
* @param $sClass
|
||||
* @param array $aSettings
|
||||
*
|
||||
* @return bool
|
||||
* @return bool|\Expression
|
||||
*/
|
||||
public static function GetSelectFilter($sClass, $aSettings = array())
|
||||
{
|
||||
@@ -975,7 +989,7 @@ class UserRights
|
||||
|
||||
try
|
||||
{
|
||||
// Check only bizmodel to let API work like in 2.4.1
|
||||
// Check Bug 1436 for details
|
||||
if (MetaModel::HasCategory($sClass, 'bizmodel'))
|
||||
{
|
||||
return self::$m_oAddOn->GetSelectFilter(self::$m_oUser, $sClass, $aSettings);
|
||||
|
||||
@@ -52,7 +52,7 @@ abstract class ValueSetDefinition
|
||||
}
|
||||
|
||||
|
||||
public function GetValues($aArgs, $sContains = '')
|
||||
public function GetValues($aArgs, $sContains = '', $sOperation = 'contains')
|
||||
{
|
||||
if (!$this->m_bIsLoaded)
|
||||
{
|
||||
@@ -93,12 +93,16 @@ abstract class ValueSetDefinition
|
||||
class ValueSetObjects extends ValueSetDefinition
|
||||
{
|
||||
protected $m_sContains;
|
||||
protected $m_sOperation;
|
||||
protected $m_sFilterExpr; // in OQL
|
||||
protected $m_sValueAttCode;
|
||||
protected $m_aOrderBy;
|
||||
protected $m_aExtraConditions;
|
||||
private $m_bAllowAllData;
|
||||
private $m_aModifierProperties;
|
||||
private $m_bSort;
|
||||
private $m_iLimit;
|
||||
|
||||
|
||||
/**
|
||||
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
||||
@@ -106,12 +110,15 @@ class ValueSetObjects extends ValueSetDefinition
|
||||
public function __construct($sFilterExp, $sValueAttCode = '', $aOrderBy = array(), $bAllowAllData = false, $aModifierProperties = array())
|
||||
{
|
||||
$this->m_sContains = '';
|
||||
$this->m_sOperation = '';
|
||||
$this->m_sFilterExpr = $sFilterExp;
|
||||
$this->m_sValueAttCode = $sValueAttCode;
|
||||
$this->m_aOrderBy = $aOrderBy;
|
||||
$this->m_bAllowAllData = $bAllowAllData;
|
||||
$this->m_aModifierProperties = $aModifierProperties;
|
||||
$this->m_aExtraConditions = array();
|
||||
$this->m_bSort = true;
|
||||
$this->m_iLimit = 0;
|
||||
}
|
||||
|
||||
public function SetModifierProperty($sPluginClass, $sProperty, $value)
|
||||
@@ -163,11 +170,20 @@ class ValueSetObjects extends ValueSetDefinition
|
||||
return new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs);
|
||||
}
|
||||
|
||||
public function GetValues($aArgs, $sContains = '')
|
||||
/**
|
||||
* @param $aArgs
|
||||
* @param string $sContains
|
||||
* @param string $sOperation for the values @see self::LoadValues()
|
||||
*
|
||||
* @return array
|
||||
* @throws CoreException
|
||||
* @throws OQLException
|
||||
*/
|
||||
public function GetValues($aArgs, $sContains = '', $sOperation = 'contains')
|
||||
{
|
||||
if (!$this->m_bIsLoaded || ($sContains != $this->m_sContains))
|
||||
if (!$this->m_bIsLoaded || ($sContains != $this->m_sContains) || ($sOperation != $this->m_sOperation))
|
||||
{
|
||||
$this->LoadValues($aArgs, $sContains);
|
||||
$this->LoadValues($aArgs, $sContains, $sOperation);
|
||||
$this->m_bIsLoaded = true;
|
||||
}
|
||||
// The results are already filtered and sorted (on friendly name)
|
||||
@@ -175,9 +191,19 @@ class ValueSetObjects extends ValueSetDefinition
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
protected function LoadValues($aArgs, $sContains = '')
|
||||
/**
|
||||
* @param $aArgs
|
||||
* @param string $sContains
|
||||
* @param string $sOperation 'contains' or 'equals_start_with'
|
||||
*
|
||||
* @return bool
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
protected function LoadValues($aArgs, $sContains = '', $sOperation = 'contains')
|
||||
{
|
||||
$this->m_sContains = $sContains;
|
||||
$this->m_sOperation = $sOperation;
|
||||
|
||||
$this->m_aValues = array();
|
||||
|
||||
@@ -202,12 +228,59 @@ class ValueSetObjects extends ValueSetDefinition
|
||||
}
|
||||
}
|
||||
|
||||
$oValueExpr = new ScalarExpression('%'.$sContains.'%');
|
||||
$oNameExpr = new FieldExpression('friendlyname', $oFilter->GetClassAlias());
|
||||
$oNewCondition = new BinaryExpression($oNameExpr, 'LIKE', $oValueExpr);
|
||||
$oFilter->AddConditionExpression($oNewCondition);
|
||||
switch ($sOperation)
|
||||
{
|
||||
case 'equals':
|
||||
$aAttributes = MetaModel::GetFriendlyNameAttributeCodeList($oFilter->GetClass());
|
||||
$sClassAlias = $oFilter->GetClassAlias();
|
||||
$aFilters = array();
|
||||
$oValueExpr = new ScalarExpression($sContains);
|
||||
foreach($aAttributes as $sAttribute)
|
||||
{
|
||||
$oNewFilter = $oFilter->DeepClone();
|
||||
$oNameExpr = new FieldExpression($sAttribute, $sClassAlias);
|
||||
$oCondition = new BinaryExpression($oNameExpr, '=', $oValueExpr);
|
||||
$oNewFilter->AddConditionExpression($oCondition);
|
||||
$aFilters[] = $oNewFilter;
|
||||
}
|
||||
// Unions are much faster than OR conditions
|
||||
$oFilter = new DBUnionSearch($aFilters);
|
||||
break;
|
||||
case 'start_with':
|
||||
$aAttributes = MetaModel::GetFriendlyNameAttributeCodeList($oFilter->GetClass());
|
||||
$sClassAlias = $oFilter->GetClassAlias();
|
||||
$aFilters = array();
|
||||
$oValueExpr = new ScalarExpression($sContains.'%');
|
||||
foreach($aAttributes as $sAttribute)
|
||||
{
|
||||
$oNewFilter = $oFilter->DeepClone();
|
||||
$oNameExpr = new FieldExpression($sAttribute, $sClassAlias);
|
||||
$oCondition = new BinaryExpression($oNameExpr, 'LIKE', $oValueExpr);
|
||||
$oNewFilter->AddConditionExpression($oCondition);
|
||||
$aFilters[] = $oNewFilter;
|
||||
}
|
||||
// Unions are much faster than OR conditions
|
||||
$oFilter = new DBUnionSearch($aFilters);
|
||||
break;
|
||||
|
||||
$oObjects = new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs);
|
||||
default:
|
||||
$oValueExpr = new ScalarExpression('%'.$sContains.'%');
|
||||
$oNameExpr = new FieldExpression('friendlyname', $oFilter->GetClassAlias());
|
||||
$oNewCondition = new BinaryExpression($oNameExpr, 'LIKE', $oValueExpr);
|
||||
$oFilter->AddConditionExpression($oNewCondition);
|
||||
break;
|
||||
}
|
||||
|
||||
$oObjects = new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs, null, $this->m_iLimit, 0, $this->m_bSort);
|
||||
if (empty($this->m_sValueAttCode))
|
||||
{
|
||||
$aAttToLoad = array($oFilter->GetClassAlias() => array('friendlyname'));
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAttToLoad = array($oFilter->GetClassAlias() => array($this->m_sValueAttCode));
|
||||
}
|
||||
$oObjects->OptimizeColumnLoad($aAttToLoad);
|
||||
while ($oObject = $oObjects->Fetch())
|
||||
{
|
||||
if (empty($this->m_sValueAttCode))
|
||||
@@ -231,6 +304,22 @@ class ValueSetObjects extends ValueSetDefinition
|
||||
{
|
||||
return $this->m_sFilterExpr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $iLimit
|
||||
*/
|
||||
public function SetLimit($iLimit)
|
||||
{
|
||||
$this->m_iLimit = $iLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $bSort
|
||||
*/
|
||||
public function SetSort($bSort)
|
||||
{
|
||||
$this->m_bSort = $bSort;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -746,12 +746,11 @@ input.dp-applied {
|
||||
transition: transform 0.5s linear;
|
||||
}
|
||||
.search_form_handler.closed {
|
||||
margin-top: -0.25em;
|
||||
/* To remove top padding from the parent .display_block */
|
||||
margin-bottom: 0.5em;
|
||||
width: 150px;
|
||||
overflow: hidden;
|
||||
border-radius: 0 0 4px 4px;
|
||||
border-radius: 4px;
|
||||
background-color: #1c94c4;
|
||||
}
|
||||
.search_form_handler.closed .sf_criterion_area {
|
||||
height: 0;
|
||||
@@ -2093,6 +2092,7 @@ td.layout_cell {
|
||||
vertical-align: top;
|
||||
}
|
||||
.dashlet-content {
|
||||
position: relative;
|
||||
background: #fff;
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
@@ -2142,12 +2142,12 @@ td.prop_icon {
|
||||
.dashlet-content .display_block {
|
||||
text-align: left;
|
||||
}
|
||||
.dashlet-unknown .dashlet-content {
|
||||
.dashlet-unknown .dashlet-content, .dashlet-proxy .dashlet-content {
|
||||
padding: 8px;
|
||||
background-color: #f2f2f2;
|
||||
text-align: center;
|
||||
}
|
||||
.dashlet-unknown .dashlet-content .dashlet-ukn-text {
|
||||
.dashlet-unknown .dashlet-content .dashlet-ukn-text, .dashlet-proxy .dashlet-content .dashlet-ukn-text, .dashlet-unknown .dashlet-content .dashlet-pxy-text, .dashlet-proxy .dashlet-content .dashlet-pxy-text {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.prop_apply .ui-icon-alert {
|
||||
@@ -2838,3 +2838,6 @@ table.listResults .originColor {
|
||||
#dataModelSplitPane .ui-layout-center {
|
||||
margin-left: 30px !important;
|
||||
}
|
||||
#ds_flash {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
@@ -839,11 +839,11 @@ input.dp-applied {
|
||||
}
|
||||
}
|
||||
&.closed{
|
||||
margin-top: -0.25em; /* To remove top padding from the parent .display_block */
|
||||
margin-bottom: 0.5em;
|
||||
width: 150px;
|
||||
overflow: hidden;
|
||||
border-radius: 0 0 4px 4px;
|
||||
border-radius: 4px;
|
||||
background-color: $complement-color;
|
||||
|
||||
.sf_criterion_area{
|
||||
height: 0;
|
||||
@@ -2414,6 +2414,7 @@ td.layout_cell {
|
||||
vertical-align: top;
|
||||
}
|
||||
.dashlet-content {
|
||||
position: relative;
|
||||
background: #fff;
|
||||
margin:0;
|
||||
overflow: auto;
|
||||
@@ -2464,13 +2465,13 @@ td.prop_icon {
|
||||
.dashlet-content .display_block {
|
||||
text-align:left;
|
||||
}
|
||||
.dashlet-unknown {
|
||||
.dashlet-unknown, .dashlet-proxy {
|
||||
.dashlet-content {
|
||||
padding: 8px;
|
||||
background-color: #F2F2F2;
|
||||
text-align: center;
|
||||
|
||||
.dashlet-ukn-text {
|
||||
.dashlet-ukn-text, .dashlet-pxy-text {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
@@ -3226,4 +3227,8 @@ table.listResults .originColor{
|
||||
#dataModelSplitPane .ui-layout-center{
|
||||
margin-left :30px !important;
|
||||
}
|
||||
///////////////////
|
||||
///////////////////
|
||||
//Impact analysis filter
|
||||
#ds_flash{
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
@@ -67,7 +67,7 @@ try
|
||||
{
|
||||
$oDoc = utils::ReadPostedDocument('file');
|
||||
$oAttachment = MetaModel::NewObject('Attachment');
|
||||
$oAttachment->Set('expire', time() + 3600); // one hour...
|
||||
$oAttachment->Set('expire', time() + MetaModel::GetConfig()->Get('draft_attachments_lifetime'));
|
||||
$oAttachment->Set('temp_id', $sTempId);
|
||||
$oAttachment->Set('item_class', $sObjClass);
|
||||
$oAttachment->SetDefaultOrgId();
|
||||
|
||||
@@ -79,18 +79,18 @@ class DBRestore extends DBBackup
|
||||
$aOutput = array();
|
||||
$iRetCode = 0;
|
||||
exec($sCommand, $aOutput, $iRetCode);
|
||||
foreach ($aOutput as $sLine)
|
||||
foreach($aOutput as $sLine)
|
||||
{
|
||||
$this->LogInfo("mysql said: $sLine");
|
||||
}
|
||||
if ($iRetCode != 0)
|
||||
{
|
||||
$this->LogError("Failed to execute: $sCommandDisplay. The command returned:$iRetCode");
|
||||
foreach ($aOutput as $sLine)
|
||||
foreach($aOutput as $sLine)
|
||||
{
|
||||
$this->LogError("mysql said: $sLine");
|
||||
}
|
||||
if (count($aOutput) == 1)
|
||||
if (count($aOutput) == 1)
|
||||
{
|
||||
$sMoreInfo = trim($aOutput[0]);
|
||||
}
|
||||
@@ -146,28 +146,22 @@ class DBRestore extends DBBackup
|
||||
|
||||
// Load the database
|
||||
//
|
||||
$sDataDir = tempnam(SetupUtils::GetTmpDir(), 'itop-');
|
||||
unlink($sDataDir); // I need a directory, not a file...
|
||||
$sDataDir = APPROOT.'data/tmp-backup-'.rand(10000, getrandmax());
|
||||
|
||||
SetupUtils::builddir($sDataDir); // Here is the directory
|
||||
$oArchive->extractFileTo($sDataDir, 'itop-dump.sql');
|
||||
$oArchive->extractTo($sDataDir);
|
||||
|
||||
$sDataFile = $sDataDir.'/itop-dump.sql';
|
||||
$this->LoadDatabase($sDataFile);
|
||||
try
|
||||
{
|
||||
SetupUtils::rrmdir($sDataDir);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
throw new BackupException("Can't remove data dir", 0, $e);
|
||||
}
|
||||
|
||||
// Update the code
|
||||
//
|
||||
$sDeltaFile = APPROOT.'data/'.$sEnvironment.'.delta.xml';
|
||||
if ($oArchive->hasFile('delta.xml') !== false)
|
||||
|
||||
if (is_file($sDataDir.'/delta.xml'))
|
||||
{
|
||||
// Extract and rename delta.xml => <env>.delta.xml;
|
||||
file_put_contents($sDeltaFile, $oArchive->getFromName('delta.xml'));
|
||||
rename($sDataDir.'/delta.xml', $sDeltaFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -184,16 +178,25 @@ class DBRestore extends DBBackup
|
||||
throw new BackupException("Can't remove production-modules dir", 0, $e);
|
||||
}
|
||||
}
|
||||
if ($oArchive->hasDir('production-modules/') !== false)
|
||||
if (is_dir($sDataDir.'/production-modules'))
|
||||
{
|
||||
$oArchive->extractDirTo(APPROOT.'data/', 'production-modules/');
|
||||
rename($sDataDir.'/production-modules', APPROOT.'data/production-modules/');
|
||||
}
|
||||
|
||||
$sConfigFile = APPROOT.'conf/'.$sEnvironment.'/config-itop.php';
|
||||
@chmod($sConfigFile, 0770); // Allow overwriting the file
|
||||
$oArchive->extractFileTo(APPROOT.'conf/'.$sEnvironment, 'config-itop.php');
|
||||
rename($sDataDir.'/config-itop.php', $sConfigFile);
|
||||
@chmod($sConfigFile, 0444); // Read-only
|
||||
|
||||
try
|
||||
{
|
||||
SetupUtils::rrmdir($sDataDir);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
throw new BackupException("Can't remove data dir", 0, $e);
|
||||
}
|
||||
|
||||
$oEnvironment = new RunTimeEnvironment($sEnvironment);
|
||||
$oEnvironment->CompileFrom($sEnvironment);
|
||||
}
|
||||
|
||||
45
datamodels/2.x/itop-backup/pt_br.dict.itop-backup.php
Executable file
45
datamodels/2.x/itop-backup/pt_br.dict.itop-backup.php
Executable file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2018 Combodo
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
||||
|
||||
'bkp-backup-running' => 'Um backup está sendo executado. Por favor, espere...',
|
||||
'bkp-restore-running' => 'Uma restauração está sendo executada. Por favor, espere...',
|
||||
|
||||
'Menu:BackupStatus' => 'Backups agendados',
|
||||
'bkp-status-title' => 'Backups agendados',
|
||||
'bkp-status-checks' => 'Configurações e verificações',
|
||||
'bkp-mysqldump-ok' => 'mysqldump está presente: %1$s',
|
||||
'bkp-mysqldump-notfound' => 'mysqldump não pode ser encontrado: %1$s - Por favor, verifique se ele está instalado e no caminho, ou edite o arquivo de configuração para ajustar mysql_bindir.',
|
||||
'bkp-mysqldump-issue' => 'mysqldump não pode ser executado (retcode=%1$d): Por favor, certifique-se de que está instalado e no caminho, ou edite o arquivo de configuração para ajustar mysql_bindir.',
|
||||
'bkp-missing-dir' => 'O diretório de destino %1$s não foi encontrado',
|
||||
'bkp-free-disk-space' => '<b>%1$s livre</b> in %2$s',
|
||||
'bkp-dir-not-writeable' => '%1$s não é gravável',
|
||||
'bkp-wrong-format-spec' => 'A especificação atual para formatar os nomes dos arquivos está errada. (%1$s). A especificação padrão foi aplicada: %2$s',
|
||||
'bkp-name-sample' => 'Os arquivos de backup são nomeados dependendo dos identificadores do banco de dados, data e hora. Exemplo: %1$s',
|
||||
'bkp-week-days' => 'Backups ocorrerão <b>cada %1$s a %2$s</b>',
|
||||
'bkp-retention' => 'No máximo <b>%1$d arquivos de backup serão mantidos</b> no diretório destino.',
|
||||
'bkp-next-to-delete' => 'Será deletado quando ocorrer o próximo backup (veja a configuração de "retention_count")',
|
||||
'bkp-table-file' => 'Arquivo',
|
||||
'bkp-table-file+' => 'Apenas arquivos com a extensão .zip são considerados arquivos de backup',
|
||||
'bkp-table-size' => 'Tamanho',
|
||||
'bkp-table-size+' => '',
|
||||
'bkp-table-actions' => 'Ações',
|
||||
'bkp-table-actions+' => '',
|
||||
'bkp-status-backups-auto' => 'Backups agendados',
|
||||
'bkp-status-backups-manual' => 'Backups manuais',
|
||||
'bkp-status-backups-none' => 'Nenhum backup ainda',
|
||||
'bkp-next-backup' => 'O próximo backup ocorrerá em <b>%1$s</b> (%2$s) at %3$s',
|
||||
'bkp-button-backup-now' => 'Backup Agora!',
|
||||
'bkp-button-restore-now' => 'Restaurar!',
|
||||
'bkp-confirm-backup' => 'Por favor, confirme que você solicitou que o backup ocorra agora.',
|
||||
'bkp-confirm-restore' => 'Por favor, confirme que você deseja restaurar o backup %1$s.',
|
||||
'bkp-wait-backup' => 'Por favor, aguarde o backup concluir...',
|
||||
'bkp-wait-restore' => 'Por favor, aguarde a restauração concluir...',
|
||||
'bkp-success-restore' => 'Restauração concluída com sucesso.',
|
||||
));
|
||||
@@ -1659,6 +1659,7 @@
|
||||
$aSearches[$sSubClass] = $oSearch;
|
||||
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$oSet->SetShowObsoleteData(utils::ShowObsoleteData());
|
||||
$iTotal += $oSet->Count();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Class:Location/Attribute:country+' => '',
|
||||
'Class:Location/Attribute:physicaldevice_list' => 'Geräte',
|
||||
'Class:Location/Attribute:physicaldevice_list+' => '',
|
||||
'Class:Location/Attribute:person_list' => 'kontakte',
|
||||
'Class:Location/Attribute:person_list' => 'Kontakte',
|
||||
'Class:Location/Attribute:person_list+' => '',
|
||||
'Class:Person' => 'Person',
|
||||
'Class:Person+' => '',
|
||||
@@ -293,7 +293,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Class:Model/Attribute:type/Value:Phone+' => '',
|
||||
'Class:Model/Attribute:physicaldevices_list' => 'Phyische Geräte',
|
||||
'Class:Model/Attribute:physicaldevices_list+' => '',
|
||||
'Class:NetworkDeviceType' => 'Netzerkgerätetyp',
|
||||
'Class:NetworkDeviceType' => 'Netzwerkgerätetyp',
|
||||
'Class:NetworkDeviceType+' => '',
|
||||
'Class:NetworkDeviceType/Attribute:networkdevicesdevices_list' => 'Netzwerkgeräte',
|
||||
'Class:NetworkDeviceType/Attribute:networkdevicesdevices_list+' => '',
|
||||
@@ -1039,7 +1039,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Menu:ConfigManagementOverview+' => 'Übersicht',
|
||||
'Menu:Contact' => 'Kontakte',
|
||||
'Menu:Contact+' => 'Kontakte',
|
||||
'Menu:Contact:Count' => '%1$d kontakten',
|
||||
'Menu:Contact:Count' => '%1$d Kontakte',
|
||||
'Menu:Person' => 'Personen',
|
||||
'Menu:Person+' => 'Alle Personen',
|
||||
'Menu:Team' => 'Teams',
|
||||
|
||||
@@ -46,7 +46,7 @@ function TestConfig($sContents, $oP)
|
||||
catch (Error $e)
|
||||
{
|
||||
// ParseError only thrown in PHP7
|
||||
throw new Exception('Error in configuration: '.$e->getMessage());
|
||||
throw new Exception('Error in configuration: '.$e->getMessage().' at line '.$e->getLine());
|
||||
}
|
||||
if (strlen($sNoise) > 0)
|
||||
{
|
||||
|
||||
@@ -31,10 +31,10 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'config-apply' => 'Anwenden',
|
||||
'config-apply' => 'Anwenden (Ctrl+S)',
|
||||
'config-cancel' => 'Zurücksetzen',
|
||||
'config-saved' => 'Successfully recorded.~~',
|
||||
'config-saved' => 'Erfolgreich gesprichert',
|
||||
'config-confirm-cancel' => 'Ihre Änderungen werden nicht gespeichert.',
|
||||
'config-no-change' => 'Keine Änderungen: Die Datei wurde nicht verändert.',
|
||||
'config-reverted' => 'The configuration has been reverted.~~',
|
||||
'config-reverted' => 'Die Konfiguration wurde zurückgesetzt',
|
||||
'config-parse-error' => 'Zeile %2$d: %1$s.<br/>Die Datei wurde nicht aktualisiert.',
|
||||
'config-current-line' => 'Editiere Zeile: %1$s',
|
||||
));
|
||||
|
||||
@@ -158,8 +158,17 @@ function collect_configuration()
|
||||
}
|
||||
else
|
||||
{
|
||||
// The format of the variable $_SERVER["SERVER_SOFTWARE"] seems to be the following:
|
||||
// PHP 7 FPM with Apache on Ubuntu: "Apache/2.4.18 (Ubuntu)"
|
||||
// IIS 7.5 on Windows 7: "Microsoft-IIS/7.5"
|
||||
// Nginx with PHP FPM on Ubuntu: "nginx/1.10.0"
|
||||
$aConfiguration['web_server_name'] = substr($_SERVER["SERVER_SOFTWARE"], 0, strpos($_SERVER["SERVER_SOFTWARE"], '/'));
|
||||
$aConfiguration['web_server_version'] = substr($_SERVER["SERVER_SOFTWARE"], strpos($_SERVER["SERVER_SOFTWARE"], '/'), strpos($_SERVER["SERVER_SOFTWARE"], 'PHP'));
|
||||
$sWebServerVersion = trim(substr($_SERVER["SERVER_SOFTWARE"], 1+strpos($_SERVER["SERVER_SOFTWARE"], '/')));
|
||||
if ($sWebServerVersion == '')
|
||||
{
|
||||
$sWebServerVersion = 'Unknown';
|
||||
}
|
||||
$aConfiguration['web_server_version'] = $sWebServerVersion;
|
||||
}
|
||||
|
||||
// PHP extensions
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// 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.
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -132,7 +132,7 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
||||
'Class:lnkDocumentToError/Attribute:error_id+' => '',
|
||||
'Class:lnkDocumentToError/Attribute:error_name' => 'Nome erro',
|
||||
'Class:lnkDocumentToError/Attribute:error_name+' => '',
|
||||
'Class:lnkDocumentToError/Attribute:link_type' => 'link_type',
|
||||
'Class:lnkDocumentToError/Attribute:link_type' => 'Tipo de link',
|
||||
'Class:lnkDocumentToError/Attribute:link_type+' => '',
|
||||
));
|
||||
|
||||
@@ -180,14 +180,15 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
||||
'Menu:NewError+' => 'Criar um erro conhecido',
|
||||
'Menu:SearchError' => 'Pesquisar por um erro conhecido',
|
||||
'Menu:SearchError+' => 'Pesquisar por erros conhecidos',
|
||||
'Menu:Problem:KnownErrors' => 'Todos erros conhecidos',
|
||||
'Menu:Problem:KnownErrors+' => 'Todos erros conhecidos',
|
||||
'Menu:Problem:KnownErrors' => 'Todos erros conhecidos',
|
||||
'Menu:Problem:KnownErrors+' => 'Todos erros conhecidos',
|
||||
'Menu:FAQCategory' => 'Categorias FAQ',
|
||||
'Menu:FAQCategory+' => 'Todas categorias FAQ',
|
||||
'Menu:FAQ' => 'FAQs',
|
||||
'Menu:FAQ+' => 'Todas FAQs',
|
||||
|
||||
|
||||
'Brick:Portal:FAQ:Menu' => 'FAQ',
|
||||
'Brick:Portal:FAQ:Title' => 'Dúvidas frequentes',
|
||||
'Brick:Portal:FAQ:Title+' => '<p>Já conferiu nossa lista de perguntas frequentes?</p><p>Talvez encontre a resposta esperada imediatamente.</p>',
|
||||
));
|
||||
'Brick:Portal:FAQ:Title' => 'Perguntes frequentes (FAQ)',
|
||||
'Brick:Portal:FAQ:Title+' => '<p>Com pressa?</p><p>Confira a lista de perguntas mais comuns e (talvez) encontre a resposta esperada imediatamente.</p>',
|
||||
|
||||
));
|
||||
@@ -107,6 +107,17 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
|
||||
'Brick:Portal:Manage:Name' => 'Spravovat položky',
|
||||
'Brick:Portal:Manage:Table:NoData' => 'Žádná položka',
|
||||
'Brick:Portal:Manage:Table:ItemActions' => 'Actions~~',
|
||||
'Brick:Portal:Manage:DisplayMode:list' => 'List~~',
|
||||
'Brick:Portal:Manage:DisplayMode:pie-chart' => 'Pie Chart~~',
|
||||
'Brick:Portal:Manage:DisplayMode:bar-chart' => 'Bar Chart',
|
||||
'Brick:Portal:Manage:Others' => 'Others~~',
|
||||
'Brick:Portal:Manage:All' => 'All~~',
|
||||
'Brick:Portal:Manage:Group' => 'Group~~',
|
||||
'Brick:Portal:Manage:fct:count' => 'Total~~',
|
||||
'Brick:Portal:Manage:fct:sum' => 'Sum~~',
|
||||
'Brick:Portal:Manage:fct:avg' => 'Average~~',
|
||||
'Brick:Portal:Manage:fct:min' => 'Min~~',
|
||||
'Brick:Portal:Manage:fct:max' => 'Max~~',
|
||||
));
|
||||
|
||||
// ObjectBrick brick
|
||||
|
||||
@@ -31,9 +31,9 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Page:Home' => 'Start',
|
||||
'Page:GoPortalHome' => 'Startseite',
|
||||
'Page:GoPreviousPage' => 'vorherige Seite',
|
||||
'Page:ReloadPage' => 'Reload page~~',
|
||||
'Page:ReloadPage' => 'Seite neu laden',
|
||||
'Portal:Button:Submit' => 'Abschicken',
|
||||
'Portal:Button:Apply' => 'Update~~',
|
||||
'Portal:Button:Apply' => 'Anwenden',
|
||||
'Portal:Button:Cancel' => 'Zurück',
|
||||
'Portal:Button:Close' => 'Schließen',
|
||||
'Portal:Button:Add' => 'Hinzu',
|
||||
@@ -41,12 +41,12 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Portal:Button:Delete' => 'Löschen',
|
||||
'Portal:EnvironmentBanner:Title' => 'Sie sind im Moment im <strong>%1$s</strong> Modus',
|
||||
'Portal:EnvironmentBanner:GoToProduction' => 'Zurück zum PRODUCTION Modus',
|
||||
'Error:HTTP:401' => 'Authentication~~',
|
||||
'Error:HTTP:401' => 'Authentifizierung',
|
||||
'Error:HTTP:404' => 'Seite nicht gefunden.',
|
||||
'Error:HTTP:500' => 'Oops! Es ist ein Fehler aufgetreten.',
|
||||
'Error:HTTP:GetHelp' => 'Bitte kontaktieren Sie Ihren %1$s administrator falls das Problem öfter auftaucht.',
|
||||
'Error:XHR:Fail' => 'Konnte Daten nicht laden, bitte kontaktieren Sie Ihren %1$s administrator',
|
||||
'Portal:ErrorUserLoggedOut' => 'You are logged out and need to log in again in order to continue.~~',
|
||||
'Portal:ErrorUserLoggedOut' => 'Sie sind ausgeloggt und müssen sich erneut einloggen, um fortfahren zu können.',
|
||||
'Portal:Datatables:Language:Processing' => 'Bitte warten...',
|
||||
'Portal:Datatables:Language:Search' => 'Filter :',
|
||||
'Portal:Datatables:Language:LengthMenu' => 'Anzahl _MENU_ Einträge pro Seite',
|
||||
@@ -87,7 +87,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Brick:Portal:Browse:Name' => 'List durchgehen',
|
||||
'Brick:Portal:Browse:Mode:List' => 'Liste',
|
||||
'Brick:Portal:Browse:Mode:Tree' => 'Baum',
|
||||
'Brick:Portal:Browse:Mode:Mosaic' => 'Mosaic~~',
|
||||
'Brick:Portal:Browse:Mode:Mosaic' => 'Kachel',
|
||||
'Brick:Portal:Browse:Action:Drilldown' => 'Drilldown',
|
||||
'Brick:Portal:Browse:Action:View' => 'Details',
|
||||
'Brick:Portal:Browse:Action:Edit' => 'Editieren',
|
||||
@@ -102,7 +102,18 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Brick:Portal:Manage:Name' => 'Einträge managen',
|
||||
'Brick:Portal:Manage:Table:NoData' => 'Kein Eintrag.',
|
||||
'Brick:Portal:Manage:Table:ItemActions' => 'Actions~~',
|
||||
'Brick:Portal:Manage:Table:ItemActions' => 'Aktionen',
|
||||
'Brick:Portal:Manage:DisplayMode:list' => 'Liste',
|
||||
'Brick:Portal:Manage:DisplayMode:pie-chart' => 'Kuchendiagramm',
|
||||
'Brick:Portal:Manage:DisplayMode:bar-chart' => 'Balkendiagramm',
|
||||
'Brick:Portal:Manage:Others' => 'Andere',
|
||||
'Brick:Portal:Manage:All' => 'Alle',
|
||||
'Brick:Portal:Manage:Group' => 'Gruppe',
|
||||
'Brick:Portal:Manage:fct:count' => 'Anzahl',
|
||||
'Brick:Portal:Manage:fct:sum' => 'Summe',
|
||||
'Brick:Portal:Manage:fct:avg' => 'Durchschnitt',
|
||||
'Brick:Portal:Manage:fct:min' => 'Minimum',
|
||||
'Brick:Portal:Manage:fct:max' => 'Maximum',
|
||||
));
|
||||
|
||||
// ObjectBrick brick
|
||||
@@ -120,12 +131,12 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
// CreateBrick brick
|
||||
Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Brick:Portal:Create:Name' => 'Schnelles Erstellen',
|
||||
'Brick:Portal:Create:ChooseType' => 'Please, choose a type~~',
|
||||
'Brick:Portal:Create:ChooseType' => 'Bitte wählen Sie einen Typ',
|
||||
));
|
||||
|
||||
// Filter brick
|
||||
Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Brick:Portal:Filter:Name' => 'Prefilter a brick~~',
|
||||
'Brick:Portal:Filter:SearchInput:Placeholder' => 'eg. connect wifi~~',
|
||||
'Brick:Portal:Filter:SearchInput:Submit' => 'Search~~',
|
||||
'Brick:Portal:Filter:Name' => 'Brick vorfiltern',
|
||||
'Brick:Portal:Filter:SearchInput:Placeholder' => 'z.B. connect wifi',
|
||||
'Brick:Portal:Filter:SearchInput:Submit' => 'Suchen',
|
||||
));
|
||||
|
||||
@@ -108,9 +108,9 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'Brick:Portal:Manage:Name' => 'Manage items',
|
||||
'Brick:Portal:Manage:Table:NoData' => 'No item.',
|
||||
'Brick:Portal:Manage:Table:ItemActions' => 'Actions',
|
||||
'Brick:Portal:Manage:DisplayType:pie-chart' => 'Pie Chart',
|
||||
'Brick:Portal:Manage:DisplayType:bar-chart' => 'Bar Chart',
|
||||
'Brick:Portal:Manage:DisplayType:default' => 'List',
|
||||
'Brick:Portal:Manage:DisplayMode:list' => 'List',
|
||||
'Brick:Portal:Manage:DisplayMode:pie-chart' => 'Pie Chart',
|
||||
'Brick:Portal:Manage:DisplayMode:bar-chart' => 'Bar Chart',
|
||||
'Brick:Portal:Manage:Others' => 'Others',
|
||||
'Brick:Portal:Manage:All' => 'All',
|
||||
'Brick:Portal:Manage:Group' => 'Group',
|
||||
|
||||
@@ -103,6 +103,17 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Brick:Portal:Manage:Name' => 'Administrar elementos',
|
||||
'Brick:Portal:Manage:Table:NoData' => 'Sin objeto.',
|
||||
'Brick:Portal:Manage:Table:ItemActions' => 'Acciones',
|
||||
'Brick:Portal:Manage:DisplayMode:list' => 'List~~',
|
||||
'Brick:Portal:Manage:DisplayMode:pie-chart' => 'Pie Chart~~',
|
||||
'Brick:Portal:Manage:DisplayMode:bar-chart' => 'Bar Chart',
|
||||
'Brick:Portal:Manage:Others' => 'Others~~',
|
||||
'Brick:Portal:Manage:All' => 'All~~',
|
||||
'Brick:Portal:Manage:Group' => 'Group~~',
|
||||
'Brick:Portal:Manage:fct:count' => 'Total~~',
|
||||
'Brick:Portal:Manage:fct:sum' => 'Sum~~',
|
||||
'Brick:Portal:Manage:fct:avg' => 'Average~~',
|
||||
'Brick:Portal:Manage:fct:min' => 'Min~~',
|
||||
'Brick:Portal:Manage:fct:max' => 'Max~~',
|
||||
));
|
||||
|
||||
// ObjectBrick brick
|
||||
|
||||
@@ -108,9 +108,9 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'Brick:Portal:Manage:Name' => 'Gestion d\'éléments',
|
||||
'Brick:Portal:Manage:Table:NoData' => 'Aucun élément',
|
||||
'Brick:Portal:Manage:Table:ItemActions' => 'Actions',
|
||||
'Brick:Portal:Manage:DisplayType:pie-chart' => 'Secteur',
|
||||
'Brick:Portal:Manage:DisplayType:bar-chart' => 'Histogramme',
|
||||
'Brick:Portal:Manage:DisplayType:default' => 'Liste',
|
||||
'Brick:Portal:Manage:DisplayMode:list' => 'Liste',
|
||||
'Brick:Portal:Manage:DisplayMode:pie-chart' => 'Secteur',
|
||||
'Brick:Portal:Manage:DisplayMode:bar-chart' => 'Histogramme',
|
||||
'Brick:Portal:Manage:Others' => 'Autres',
|
||||
'Brick:Portal:Manage:All' => 'Total',
|
||||
'Brick:Portal:Manage:Group' => 'Groupe',
|
||||
|
||||
@@ -13,7 +13,6 @@ SetupWebPage::AddModule(
|
||||
'visible' => true,
|
||||
// Components
|
||||
'datamodel' => array(
|
||||
'portal/src/apis/extensions/d3portaluiextension.class.inc.php',
|
||||
'portal/src/controllers/abstractcontroller.class.inc.php',
|
||||
'portal/src/controllers/brickcontroller.class.inc.php',
|
||||
'portal/src/entities/abstractbrick.class.inc.php',
|
||||
|
||||
@@ -97,6 +97,17 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'Brick:Portal:Manage:Name' => 'Beheer items',
|
||||
'Brick:Portal:Manage:Table:NoData' => 'Geen gegevens',
|
||||
'Brick:Portal:Manage:Table:ItemActions' => 'Actions~~',
|
||||
'Brick:Portal:Manage:DisplayMode:list' => 'List~~',
|
||||
'Brick:Portal:Manage:DisplayMode:pie-chart' => 'Pie Chart~~',
|
||||
'Brick:Portal:Manage:DisplayMode:bar-chart' => 'Bar Chart',
|
||||
'Brick:Portal:Manage:Others' => 'Others~~',
|
||||
'Brick:Portal:Manage:All' => 'All~~',
|
||||
'Brick:Portal:Manage:Group' => 'Group~~',
|
||||
'Brick:Portal:Manage:fct:count' => 'Total~~',
|
||||
'Brick:Portal:Manage:fct:sum' => 'Sum~~',
|
||||
'Brick:Portal:Manage:fct:avg' => 'Average~~',
|
||||
'Brick:Portal:Manage:fct:min' => 'Min~~',
|
||||
'Brick:Portal:Manage:fct:max' => 'Max~~',
|
||||
));
|
||||
|
||||
// ObjectBrick brick
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
// Copyright (c) 2010-2017 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/>
|
||||
//
|
||||
namespace Combodo\iTop\Portal\API\Extension;
|
||||
|
||||
use AbstractPortalUIExtension;
|
||||
use Silex\Application;
|
||||
use utils;
|
||||
|
||||
class D3PortalUIExtension extends AbstractPortalUIExtension
|
||||
{
|
||||
public function GetCSSFiles(Application $oApp)
|
||||
{
|
||||
$aCSSFiles = array(
|
||||
utils::GetAbsoluteUrlAppRoot().'css/c3.min.css?v='.ITOP_VERSION,
|
||||
);
|
||||
return $aCSSFiles;
|
||||
}
|
||||
|
||||
public function GetJSFiles(Application $oApp)
|
||||
{
|
||||
$aJSFiles = array(
|
||||
utils::GetAbsoluteUrlAppRoot().'js/d3.min.js?v='.ITOP_VERSION,
|
||||
utils::GetAbsoluteUrlAppRoot().'js/c3.min.js?v='.ITOP_VERSION,
|
||||
utils::GetCurrentModuleUrl().'/portal/web/js/export.js?v='.ITOP_VERSION,
|
||||
);
|
||||
return $aJSFiles;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
namespace Combodo\iTop\Portal\Controller;
|
||||
|
||||
use Exception;
|
||||
use AttributeDate;
|
||||
use AttributeDateTime;
|
||||
use AttributeDefinition;
|
||||
@@ -53,7 +54,7 @@ class ManageBrickController extends BrickController
|
||||
* @param \Symfony\Component\HttpFoundation\Request $oRequest
|
||||
* @param \Silex\Application $oApp
|
||||
* @param string $sBrickId
|
||||
* @param string $sDisplayType
|
||||
* @param string $sDisplayMode
|
||||
* @param string $sGroupingTab
|
||||
* @param string $sDataLoading
|
||||
*
|
||||
@@ -64,22 +65,21 @@ class ManageBrickController extends BrickController
|
||||
* @throws \MySQLException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function DisplayAction(
|
||||
Request $oRequest, Application $oApp, $sBrickId, $sGroupingTab, $sDisplayType = null, $sDataLoading = null
|
||||
) {
|
||||
public function DisplayAction(Request $oRequest, Application $oApp, $sBrickId, $sGroupingTab, $sDisplayMode = null, $sDataLoading = null)
|
||||
{
|
||||
/** @var ManageBrick $oBrick */
|
||||
$oBrick = ApplicationHelper::GetLoadedBrickFromId($oApp, $sBrickId);
|
||||
|
||||
if (is_null($sDisplayType))
|
||||
if (is_null($sDisplayMode))
|
||||
{
|
||||
$sDisplayType = $oBrick->GetDisplayType();
|
||||
$sDisplayMode = $oBrick->GetDefaultDisplayMode();
|
||||
}
|
||||
$aDisplayParams = $oBrick->GetPresentationDataForDisplayType($sDisplayType);
|
||||
$aData = $this->GetData($oRequest, $oApp, $sBrickId, $sGroupingTab, $aDisplayParams['need_details']);
|
||||
$aDisplayParams = $oBrick->GetPresentationDataForTileMode($sDisplayMode);
|
||||
$aData = $this->GetData($oRequest, $oApp, $sBrickId, $sGroupingTab, $oBrick::AreDetailsNeededForDisplayMode($sDisplayMode));
|
||||
|
||||
$aExportFields = $oBrick->GetExportFields();
|
||||
$aData = $aData + array(
|
||||
'sDisplayType' => $sDisplayType,
|
||||
'sDisplayMode' => $sDisplayMode,
|
||||
'bCanExport' => !empty($aExportFields),
|
||||
);
|
||||
// Preparing response
|
||||
@@ -89,7 +89,7 @@ class ManageBrickController extends BrickController
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLayoutTemplate = $aDisplayParams['layoutTemplate'];
|
||||
$sLayoutTemplate = $oBrick::GetPageTemplateFromDisplayMode($sDisplayMode);
|
||||
$oResponse = $oApp['twig']->render($sLayoutTemplate, $aData);
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ class ManageBrickController extends BrickController
|
||||
{
|
||||
$aData = $this->GetData($oRequest, $oApp, $sBrickId, null);
|
||||
}
|
||||
catch (\Exception $e)
|
||||
catch (Exception $e)
|
||||
{
|
||||
// TODO Default values
|
||||
$aData = array();
|
||||
@@ -156,7 +156,9 @@ class ManageBrickController extends BrickController
|
||||
{
|
||||
$oQuery = DBSearch::FromOQL($oBrick->GetOql());
|
||||
$sClass = $oQuery->GetClass();
|
||||
$this->AddScopeToQuery($oQuery, $oApp, $oBrick, $sClass);
|
||||
/** @var \Combodo\iTop\Portal\Helper\ScopeValidatorHelper $oScopeHelper */
|
||||
$oScopeHelper = $oApp['scope_validator'];
|
||||
$oScopeHelper->AddScopeToQuery($oQuery, $sClass);
|
||||
$aData = array();
|
||||
$this->ManageSearchValue($oRequest, $aData, $oQuery, $sClass);
|
||||
|
||||
@@ -275,10 +277,7 @@ class ManageBrickController extends BrickController
|
||||
// Starting to build query
|
||||
$oQuery = DBSearch::FromOQL($oBrick->GetOql());
|
||||
$sClass = $oQuery->GetClass();
|
||||
$sIconURL = \MetaModel::GetClassIcon($sClass, false);
|
||||
|
||||
// - Adding search clause if necessary
|
||||
$this->ManageSearchValue($oRequest, $aData, $oQuery, $sClass);
|
||||
$sIconURL = MetaModel::GetClassIcon($sClass, false);
|
||||
|
||||
// Preparing tabs
|
||||
// - We need to retrieve distinct values for the grouping attribute
|
||||
@@ -304,7 +303,11 @@ class ManageBrickController extends BrickController
|
||||
{
|
||||
$oConditionQuery = $oQuery->Intersect(DBSearch::FromOQL($aGroup['condition']));
|
||||
// - Restricting query to scope
|
||||
if ($this->AddScopeToQuery($oConditionQuery, $oApp, $oBrick, $oConditionQuery->GetClass()))
|
||||
|
||||
/** @var \Combodo\iTop\Portal\Helper\ScopeValidatorHelper $oScopeHelper */
|
||||
$oScopeHelper = $oApp['scope_validator'];
|
||||
$bHasScope = $oScopeHelper->AddScopeToQuery($oConditionQuery, $oConditionQuery->GetClass());
|
||||
if ($bHasScope)
|
||||
{
|
||||
// - Building ObjectSet
|
||||
$oConditionSet = new DBObjectSet($oConditionQuery);
|
||||
@@ -336,7 +339,7 @@ class ManageBrickController extends BrickController
|
||||
}
|
||||
}
|
||||
|
||||
// - Retrieving the current grouping tab to display and altering the query to do so
|
||||
// - Retrieving the current grouping tab to display if necessary and altering the query to do so
|
||||
if ($sGroupingTab === null)
|
||||
{
|
||||
if ($oBrick->HasGroupingTabs())
|
||||
@@ -345,7 +348,7 @@ class ManageBrickController extends BrickController
|
||||
$sGroupingTab = key($aGroupingTabsValues);
|
||||
if ($aGroupingTabsValues[$sGroupingTab]['condition'] !== null)
|
||||
{
|
||||
$oQuery = $oQuery->Intersect($aGroupingTabsValues[$sGroupingTab]['condition']);
|
||||
$oQuery = $aGroupingTabsValues[$sGroupingTab]['condition']->DeepClone();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,10 +356,13 @@ class ManageBrickController extends BrickController
|
||||
{
|
||||
if ($aGroupingTabsValues[$sGroupingTab]['condition'] !== null)
|
||||
{
|
||||
$oQuery = $oQuery->Intersect($aGroupingTabsValues[$sGroupingTab]['condition']);
|
||||
$oQuery = $aGroupingTabsValues[$sGroupingTab]['condition']->DeepClone();
|
||||
}
|
||||
}
|
||||
|
||||
// - Adding search clause if necessary
|
||||
$this->ManageSearchValue($oRequest, $aData, $oQuery, $sClass, $aColumnsAttrs);
|
||||
|
||||
// Preparing areas
|
||||
// - We need to retrieve distinct values for the grouping attribute
|
||||
// Note : Will have to be changed when we consider grouping on something else than the finalclass
|
||||
@@ -370,7 +376,7 @@ class ManageBrickController extends BrickController
|
||||
{
|
||||
$oDistinctQuery = $this->GetScopedQuery($oApp, $oBrick, $sClass);
|
||||
// Adding grouping conditions
|
||||
$oFieldExp = new FieldExpression($sGroupingAreaAttCode, $sParentAlias);
|
||||
$oFieldExp = new FieldExpression($sGroupingAreaAttCode, $oDistinctQuery->GetClassAlias());
|
||||
$sDistinctSql = $oDistinctQuery->MakeGroupByQuery(array(), array('grouped_by_1' => $oFieldExp), true);
|
||||
$aDistinctResults = CMDBSource::QueryToArray($sDistinctSql);
|
||||
|
||||
@@ -415,14 +421,16 @@ class ManageBrickController extends BrickController
|
||||
$oAreaQuery = DBSearch::CloneWithAlias($oQuery, $sParentAlias);
|
||||
if ($aGroupingAreasValue['condition'] !== null)
|
||||
{
|
||||
//$oAreaQuery->AddConditionExpression($aGroupingAreasValue['condition']);
|
||||
$oAreaQuery = $oAreaQuery->Intersect($aGroupingAreasValue['condition']);
|
||||
$oAreaQuery = $aGroupingAreasValue['condition']->DeepClone();
|
||||
}
|
||||
|
||||
// Restricting query to allowed scope on each classes
|
||||
// Note: Will need to moved the scope restriction on queries elsewhere when we consider grouping on something else than finalclass
|
||||
// Note: We now get view scope instead of edit scope as we allowed users to view/edit objects in the brick regarding their rights
|
||||
if (!$this->AddScopeToQuery($oAreaQuery, $oApp, $oBrick, $aGroupingAreasValue['value']))
|
||||
/** @var \Combodo\iTop\Portal\Helper\ScopeValidatorHelper $oScopeHelper */
|
||||
$oScopeHelper = $oApp['scope_validator'];
|
||||
$bHasScope = $oScopeHelper->AddScopeToQuery($oAreaQuery, $aGroupingAreasValue['value']);
|
||||
if (!$bHasScope)
|
||||
{
|
||||
// if no scope apply does not allow any data
|
||||
$oAreaQuery = null;
|
||||
@@ -693,7 +701,7 @@ class ManageBrickController extends BrickController
|
||||
);
|
||||
$aUrls[] = $oApp['url_generator']->generate('p_manage_brick', array(
|
||||
'sBrickId' => $sBrickId,
|
||||
'sDisplayType' => 'default',
|
||||
'sDisplayMode' => 'default',
|
||||
'sGroupingTab' => $aValues['value']
|
||||
));
|
||||
}
|
||||
@@ -734,7 +742,7 @@ class ManageBrickController extends BrickController
|
||||
* @param DBSearch $oQuery
|
||||
* @param string $sClass
|
||||
*/
|
||||
protected function ManageSearchValue(Request $oRequest, &$aData, DBSearch &$oQuery, $sClass)
|
||||
protected function ManageSearchValue(Request $oRequest, &$aData, DBSearch &$oQuery, $sClass, $aColumnsAttrs)
|
||||
{
|
||||
// Getting search value
|
||||
$sSearchValue = $oRequest->get('sSearchValue', null);
|
||||
@@ -743,7 +751,17 @@ class ManageBrickController extends BrickController
|
||||
// Note : This is a very naive search at the moment
|
||||
if ($sSearchValue !== null)
|
||||
{
|
||||
$aSearchListItems = MetaModel::GetZListItems($sClass, 'standard_search');
|
||||
// Putting only valid attributes as one can define attributes of leaf classes in the brick definition (<fields>), but at this stage we are working on the abstract class.
|
||||
// Note: This won't fix everything as the search will not be looking in all fields.
|
||||
$aSearchListItems = array();
|
||||
foreach($aColumnsAttrs as $sColumnAttr)
|
||||
{
|
||||
if(MetaModel::IsValidAttCode($sClass, $sColumnAttr))
|
||||
{
|
||||
$aSearchListItems[] = $sColumnAttr;
|
||||
}
|
||||
}
|
||||
|
||||
$oFullBinExpr = null;
|
||||
foreach ($aSearchListItems as $sSearchItemAttr)
|
||||
{
|
||||
@@ -797,7 +815,9 @@ class ManageBrickController extends BrickController
|
||||
$aGroupingTabsValues = array();
|
||||
$aDistinctResults = array();
|
||||
$oDistinctQuery = DBSearch::FromOQL($oBrick->GetOql());
|
||||
$bHasScope = $this->AddScopeToQuery($oDistinctQuery, $oApp, $oBrick, $oDistinctQuery->GetClass());
|
||||
/** @var \Combodo\iTop\Portal\Helper\ScopeValidatorHelper $oScopeHelper */
|
||||
$oScopeHelper = $oApp['scope_validator'];
|
||||
$bHasScope = $oScopeHelper->AddScopeToQuery($oDistinctQuery, $oDistinctQuery->GetClass());
|
||||
if ($bHasScope)
|
||||
{
|
||||
// - Adding field condition
|
||||
@@ -831,7 +851,7 @@ class ManageBrickController extends BrickController
|
||||
{
|
||||
$oConditionQuery = DBSearch::CloneWithAlias($oQuery, 'GTAB');
|
||||
$oExpression = new BinaryExpression(new FieldExpression($sGroupingTabAttCode,
|
||||
$oDistinctQuery->GetClassAlias()), '=', new UnaryExpression($aDistinctResult['grouped_by_1']));
|
||||
$oConditionQuery->GetClassAlias()), '=', new UnaryExpression($aDistinctResult['grouped_by_1']));
|
||||
$oConditionQuery->AddConditionExpression($oExpression);
|
||||
|
||||
$sHtmlLabel = $oFieldExp->MakeValueLabel($oDistinctQuery, $aDistinctResult['grouped_by_1'], '');
|
||||
@@ -854,7 +874,7 @@ class ManageBrickController extends BrickController
|
||||
{
|
||||
$iOtherCount += $aResult['_itop_count_'];
|
||||
$oExpr = new BinaryExpression(new FieldExpression($sGroupingTabAttCode,
|
||||
$oDistinctQuery->GetClassAlias()), '=', new UnaryExpression($aResult['grouped_by_1']));
|
||||
$oConditionQuery->GetClassAlias()), '=', new UnaryExpression($aResult['grouped_by_1']));
|
||||
if (is_null($oExpression))
|
||||
{
|
||||
$oExpression = $oExpr;
|
||||
@@ -904,35 +924,10 @@ class ManageBrickController extends BrickController
|
||||
protected function GetScopedQuery(Application $oApp, ManageBrick $oBrick, $sClass)
|
||||
{
|
||||
$oQuery = DBSearch::FromOQL($oBrick->GetOql());
|
||||
$this->AddScopeToQuery($oQuery, $oApp, $oBrick, $sClass);
|
||||
/** @var \Combodo\iTop\Portal\Helper\ScopeValidatorHelper $oScopeHelper */
|
||||
$oScopeHelper = $oApp['scope_validator'];
|
||||
$oScopeHelper->AddScopeToQuery($oQuery, $sClass);
|
||||
|
||||
return $oQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DBSearch $oQuery
|
||||
* @param Application $oApp
|
||||
* @param ManageBrick $oBrick
|
||||
* @param string $sClass
|
||||
*
|
||||
* @return bool true if scope exists, false if scope is null
|
||||
*/
|
||||
protected function AddScopeToQuery(DBSearch &$oQuery, Application $oApp, ManageBrick $oBrick, $sClass)
|
||||
{
|
||||
$oScopeQuery = $oApp['scope_validator']->GetScopeFilterForProfiles(UserRights::ListProfiles(), $sClass,
|
||||
UR_ACTION_READ);
|
||||
if ($oScopeQuery !== null)
|
||||
{
|
||||
$oQuery = $oQuery->Intersect($oScopeQuery);
|
||||
// - Allowing all data if necessary
|
||||
if ($oScopeQuery->IsAllDataAllowed())
|
||||
{
|
||||
$oQuery->AllowAllData();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1455,7 +1455,7 @@ class ObjectController extends AbstractController
|
||||
{
|
||||
$oDocument = utils::ReadPostedDocument($sFieldName);
|
||||
$oAttachment = MetaModel::NewObject('Attachment');
|
||||
$oAttachment->Set('expire', time() + 3600); // one hour...
|
||||
$oAttachment->Set('expire', time() + MetaModel::GetConfig()->Get('draft_attachments_lifetime')); // one hour...
|
||||
$oAttachment->Set('temp_id', $sTempId);
|
||||
$oAttachment->Set('item_class', $sObjectClass);
|
||||
$oAttachment->SetDefaultOrgId();
|
||||
|
||||
@@ -30,29 +30,37 @@ use Combodo\iTop\Portal\Brick\PortalBrick;
|
||||
*/
|
||||
class BrowseBrick extends PortalBrick
|
||||
{
|
||||
const DEFAULT_DECORATION_CLASS_HOME = 'fa fa-map';
|
||||
const DEFAULT_DECORATION_CLASS_NAVIGATION_MENU = 'fa fa-map fa-2x';
|
||||
const ENUM_BROWSE_MODE_LIST = 'list';
|
||||
const ENUM_BROWSE_MODE_TREE = 'tree';
|
||||
const ENUM_BROWSE_MODE_MOSAIC = 'mosaic';
|
||||
|
||||
const ENUM_ACTION_VIEW = 'view';
|
||||
const ENUM_ACTION_EDIT = 'edit';
|
||||
const ENUM_ACTION_DRILLDOWN = 'drilldown';
|
||||
|
||||
const ENUM_ACTION_CREATE_FROM_THIS = 'create_from_this';
|
||||
const ENUM_ACTION_ICON_CLASS_VIEW = 'glyphicon glyphicon-list-alt';
|
||||
const ENUM_ACTION_ICON_CLASS_EDIT = 'glyphicon glyphicon-pencil';
|
||||
const ENUM_ACTION_ICON_CLASS_DRILLDOWN = 'glyphicon glyphicon-menu-down';
|
||||
const ENUM_ACTION_ICON_CLASS_CREATE_FROM_THIS = 'glyphicon glyphicon-edit';
|
||||
|
||||
const ENUM_FACTORY_TYPE_METHOD = 'method';
|
||||
const ENUM_FACTORY_TYPE_CLASS = 'class';
|
||||
const DEFAULT_DATA_LOADING = self::ENUM_DATA_LOADING_FULL;
|
||||
|
||||
const DEFAULT_DECORATION_CLASS_HOME = 'fa fa-map';
|
||||
const DEFAULT_DECORATION_CLASS_NAVIGATION_MENU = 'fa fa-map fa-2x';
|
||||
const DEFAULT_DATA_LOADING = self::ENUM_DATA_LOADING_FULL;
|
||||
const DEFAULT_LEVEL_NAME_ATT = 'name';
|
||||
const DEFAULT_BROWSE_MODE = self::ENUM_BROWSE_MODE_LIST;
|
||||
const DEFAULT_ACTION = self::ENUM_ACTION_DRILLDOWN;
|
||||
const DEFAULT_ACTION_OPENING_TARGET = self::ENUM_OPENING_TARGET_MODAL;
|
||||
const DEFAULT_LIST_LENGTH = 20;
|
||||
|
||||
static $aBrowseModes = array(self::ENUM_BROWSE_MODE_LIST, self::ENUM_BROWSE_MODE_TREE, self::ENUM_BROWSE_MODE_MOSAIC);
|
||||
static $aBrowseModes = array(
|
||||
self::ENUM_BROWSE_MODE_LIST,
|
||||
self::ENUM_BROWSE_MODE_TREE,
|
||||
self::ENUM_BROWSE_MODE_MOSAIC
|
||||
);
|
||||
|
||||
static $sRouteName = 'p_browse_brick';
|
||||
|
||||
|
||||
@@ -32,6 +32,9 @@ class FilterBrick extends PortalBrick
|
||||
{
|
||||
const DEFAULT_VISIBLE_NAVIGATION_MENU = false;
|
||||
const DEFAULT_TILE_TEMPLATE_PATH = 'itop-portal-base/portal/src/views/bricks/filter/tile.html.twig';
|
||||
const DEFAULT_DECORATION_CLASS_HOME = 'fa fa-search';
|
||||
const DEFAULT_DECORATION_CLASS_NAVIGATION_MENU = 'fa fa-search fa-2x';
|
||||
|
||||
const DEFAULT_TARGET_BRICK_CLASS = 'Combodo\\iTop\\Portal\\Brick\\BrowseBrick';
|
||||
const DEFAULT_SEARCH_PLACEHOLDER_VALUE = 'Brick:Portal:Filter:SearchInput:Placeholder';
|
||||
const DEFAULT_SEARCH_SUBMIT_LABEL = 'Brick:Portal:Filter:SearchInput:Submit';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
// Copyright (C) 2010-2018 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -25,89 +25,151 @@ use DBSearch;
|
||||
use DOMFormatException;
|
||||
use MetaModel;
|
||||
|
||||
define('MANAGE_BRICK_LAYOUT_PATH', 'itop-portal-base/portal/src/views/bricks/manage/');
|
||||
|
||||
class ManageBrick extends PortalBrick
|
||||
{
|
||||
const ENUM_ACTION_VIEW = 'view';
|
||||
const ENUM_ACTION_EDIT = 'edit';
|
||||
|
||||
const DEFAULT_DECORATION_CLASS_HOME = 'fa fa-pencil-square';
|
||||
const ENUM_TILE_MODE_TEXT = 'text';
|
||||
const ENUM_TILE_MODE_BADGE = 'badge';
|
||||
const ENUM_TILE_MODE_PIE = 'pie-chart';
|
||||
const ENUM_TILE_MODE_BAR = 'bar-chart';
|
||||
const ENUM_TILE_MODE_TOP = 'top-list';
|
||||
|
||||
const ENUM_DISPLAY_MODE_LIST = 'list';
|
||||
const ENUM_DISPLAY_MODE_PIE = 'pie-chart';
|
||||
const ENUM_DISPLAY_MODE_BAR = 'bar-chart';
|
||||
|
||||
const ENUM_PAGE_TEMPLATE_PATH_TABLE = 'itop-portal-base/portal/src/views/bricks/manage/layout-table.html.twig';
|
||||
const ENUM_PAGE_TEMPLATE_PATH_CHART = 'itop-portal-base/portal/src/views/bricks/manage/layout-chart.html.twig';
|
||||
|
||||
const DEFAULT_DECORATION_CLASS_HOME = 'fa fa-pencil-square';
|
||||
const DEFAULT_DECORATION_CLASS_NAVIGATION_MENU = 'fa fa-pencil-square fa-2x';
|
||||
const DEFAULT_PAGE_TEMPLATE_PATH = 'itop-portal-base/portal/src/views/bricks/manage/layout-table.html.twig';
|
||||
const CHART_PAGE_TEMPLATE_PATH = 'itop-portal-base/portal/src/views/bricks/manage/layout-chart.html.twig';
|
||||
const DEFAULT_PAGE_TEMPLATE_PATH = self::ENUM_PAGE_TEMPLATE_PATH_TABLE;
|
||||
const DEFAULT_OQL = '';
|
||||
const DEFAULT_OPENING_MODE = self::ENUM_ACTION_EDIT;
|
||||
const DEFAULT_DATA_LOADING = self::ENUM_DATA_LOADING_LAZY;
|
||||
const DEFAULT_LIST_LENGTH = 20;
|
||||
const DEFAULT_ZLIST_FIELDS = 'list';
|
||||
const DEFAULT_SHOW_TAB_COUNTS = false;
|
||||
const DEFAULT_DISPLAY_MODE = self::ENUM_DISPLAY_MODE_LIST;
|
||||
const DEFAULT_TILE_MODE = self::ENUM_TILE_MODE_TEXT;
|
||||
const DEFAULT_GROUP_LIMIT = 0;
|
||||
const DEFAULT_GROUP_SHOW_OTHERS = true;
|
||||
|
||||
const DEFAULT_TILE_TEMPLATE_PATH = 'itop-portal-base/portal/src/views/bricks/manage/tile-default.html.twig';
|
||||
const DEFAULT_TILE_CONTROLLER_ACTION = 'Combodo\\iTop\\Portal\\Controller\\ManageBrickController::TileAction';
|
||||
|
||||
static $aDisplayModes = array(
|
||||
self::ENUM_DISPLAY_MODE_LIST,
|
||||
self::ENUM_DISPLAY_MODE_PIE,
|
||||
self::ENUM_DISPLAY_MODE_BAR,
|
||||
);
|
||||
static $aTileModes = array(
|
||||
self::ENUM_TILE_MODE_TEXT,
|
||||
self::ENUM_TILE_MODE_BADGE,
|
||||
self::ENUM_TILE_MODE_PIE,
|
||||
self::ENUM_TILE_MODE_BAR,
|
||||
self::ENUM_TILE_MODE_TOP,
|
||||
);
|
||||
static $aPresentationData = array(
|
||||
self::ENUM_TILE_MODE_BADGE => array(
|
||||
'decorationCssClass' => 'fa fa-id-card-o fa-2x',
|
||||
'tileTemplate' => 'itop-portal-base/portal/src/views/bricks/manage/tile-badge.html.twig',
|
||||
'layoutTemplate' => self::ENUM_PAGE_TEMPLATE_PATH_TABLE,
|
||||
'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_LIST,
|
||||
'need_details' => true,
|
||||
),
|
||||
self::ENUM_TILE_MODE_TOP => array(
|
||||
'decorationCssClass' => 'fa fa-signal fa-rotate-270 fa-2x',
|
||||
'tileTemplate' => 'itop-portal-base/portal/src/views/bricks/manage/tile-top-list.html.twig',
|
||||
'layoutTemplate' => self::ENUM_PAGE_TEMPLATE_PATH_TABLE,
|
||||
'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_LIST,
|
||||
'need_details' => true,
|
||||
),
|
||||
self::ENUM_TILE_MODE_PIE => array(
|
||||
'decorationCssClass' => 'fa fa-pie-chart fa-2x',
|
||||
'tileTemplate' => 'itop-portal-base/portal/src/views/bricks/manage/tile-chart.html.twig',
|
||||
'layoutTemplate' => self::ENUM_PAGE_TEMPLATE_PATH_CHART,
|
||||
'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_PIE,
|
||||
'need_details' => false,
|
||||
),
|
||||
self::ENUM_TILE_MODE_BAR => array(
|
||||
'decorationCssClass' => 'fa fa-bar-chart fa-2x',
|
||||
'tileTemplate' => 'itop-portal-base/portal/src/views/bricks/manage/tile-chart.html.twig',
|
||||
'layoutTemplate' => self::ENUM_PAGE_TEMPLATE_PATH_CHART,
|
||||
'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_BAR,
|
||||
'need_details' => false,
|
||||
),
|
||||
self::ENUM_TILE_MODE_TEXT => array(
|
||||
'decorationCssClass' => 'fa fa-pencil-square fa-2x',
|
||||
'tileTemplate' => self::DEFAULT_TILE_TEMPLATE_PATH,
|
||||
'layoutTemplate' => self::ENUM_PAGE_TEMPLATE_PATH_TABLE,
|
||||
'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_LIST,
|
||||
'need_details' => true,
|
||||
),
|
||||
);
|
||||
|
||||
static $sRouteName = 'p_manage_brick';
|
||||
|
||||
protected $sOql;
|
||||
protected $sOpeningMode;
|
||||
protected $aGrouping;
|
||||
protected $aFields;
|
||||
protected $aExportFields;
|
||||
protected $bShowTabCounts;
|
||||
/**
|
||||
* @var string default display mode for the brick's tile
|
||||
*/
|
||||
protected $sDisplayType;
|
||||
protected $aAvailableDisplayModes = array();
|
||||
protected $sDefaultDisplayMode;
|
||||
|
||||
protected $sTileMode;
|
||||
protected $iGroupLimit;
|
||||
protected $bGroupShowOthers;
|
||||
|
||||
protected $aPresentationData = array(
|
||||
'badge' => array(
|
||||
'decorationCssClass' => 'fa fa-id-card-o fa-2x',
|
||||
'tileTemplate' => 'itop-portal-base/portal/src/views/bricks/manage/tile-badge.html.twig',
|
||||
'layoutTemplate' => 'itop-portal-base/portal/src/views/bricks/manage/layout-table.html.twig',
|
||||
'layoutDisplayType' => ManageBrick::DISPLAY_MODE_TABLE,
|
||||
'need_details' => true,
|
||||
),
|
||||
'top-list' => array(
|
||||
'decorationCssClass' => 'fa fa-signal fa-rotate-270 fa-2x',
|
||||
'tileTemplate' => 'itop-portal-base/portal/src/views/bricks/manage/tile-top-list.html.twig',
|
||||
'layoutTemplate' => 'itop-portal-base/portal/src/views/bricks/manage/layout-table.html.twig',
|
||||
'layoutDisplayType' => ManageBrick::DISPLAY_MODE_TABLE,
|
||||
'need_details' => true,
|
||||
),
|
||||
'pie-chart' => array(
|
||||
'decorationCssClass' => 'fa fa-pie-chart fa-2x',
|
||||
'tileTemplate' => 'itop-portal-base/portal/src/views/bricks/manage/tile-chart.html.twig',
|
||||
'layoutTemplate' => 'itop-portal-base/portal/src/views/bricks/manage/layout-chart.html.twig',
|
||||
'layoutDisplayType' => ManageBrick::DISPLAY_MODE_PIE,
|
||||
'need_details' => false,
|
||||
),
|
||||
'bar-chart' => array(
|
||||
'decorationCssClass' => 'fa fa-bar-chart fa-2x',
|
||||
'tileTemplate' => 'itop-portal-base/portal/src/views/bricks/manage/tile-chart.html.twig',
|
||||
'layoutTemplate' => 'itop-portal-base/portal/src/views/bricks/manage/layout-chart.html.twig',
|
||||
'layoutDisplayType' => ManageBrick::DISPLAY_MODE_BAR,
|
||||
'need_details' => false,
|
||||
),
|
||||
'default' => array(
|
||||
'decorationCssClass' => 'fa fa-pencil-square fa-2x',
|
||||
'tileTemplate' => self::DEFAULT_TILE_TEMPLATE_PATH,
|
||||
'layoutTemplate' => 'itop-portal-base/portal/src/views/bricks/manage/layout-table.html.twig',
|
||||
'layoutDisplayType' => ManageBrick::DISPLAY_MODE_TABLE,
|
||||
'need_details' => true,
|
||||
),
|
||||
);
|
||||
protected $aAvailableDisplayModes = array();
|
||||
const DISPLAY_MODE_TABLE = 'default';
|
||||
const DISPLAY_MODE_PIE = 'pie-chart';
|
||||
const DISPLAY_MODE_BAR = 'bar-chart';
|
||||
const DISPLAY_MODES_ALLOWED = array(
|
||||
ManageBrick::DISPLAY_MODE_TABLE,
|
||||
ManageBrick::DISPLAY_MODE_PIE,
|
||||
ManageBrick::DISPLAY_MODE_BAR
|
||||
);
|
||||
/**
|
||||
* Returns true if the $sDisplayMode need objects details for rendering.
|
||||
*
|
||||
* @param string $sDisplayMode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
static public function AreDetailsNeededForDisplayMode($sDisplayMode)
|
||||
{
|
||||
$bNeedDetails = false;
|
||||
foreach(static::$aPresentationData as $aData)
|
||||
{
|
||||
if($aData['layoutDisplayMode'] === $sDisplayMode)
|
||||
{
|
||||
$bNeedDetails = $aData['need_details'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
return $bNeedDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the page template path for the $sDisplayMode
|
||||
*
|
||||
* @param string $sDisplayMode
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
static public function GetPageTemplateFromDisplayMode($sDisplayMode)
|
||||
{
|
||||
$sTemplate = static::DEFAULT_PAGE_TEMPLATE_PATH;
|
||||
foreach(static::$aPresentationData as $aData)
|
||||
{
|
||||
if($aData['layoutDisplayMode'] === $sDisplayMode)
|
||||
{
|
||||
$sTemplate = $aData['layoutTemplate'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $sTemplate;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
@@ -117,6 +179,11 @@ class ManageBrick extends PortalBrick
|
||||
$this->aFields = array();
|
||||
$this->aExportFields = array();
|
||||
$this->bShowTabCounts = static::DEFAULT_SHOW_TAB_COUNTS;
|
||||
$this->sDefaultDisplayMode = static::DEFAULT_DISPLAY_MODE;
|
||||
|
||||
$this->sTileMode = static::DEFAULT_TILE_MODE;
|
||||
$this->iGroupLimit = static::DEFAULT_GROUP_LIMIT;
|
||||
$this->bGroupShowOthers = static::DEFAULT_GROUP_SHOW_OTHERS;
|
||||
|
||||
// This is hardcoded for now, we might allow area grouping on another attribute in the future
|
||||
$this->AddGrouping('areas', array('attribute' => 'finalclass'));
|
||||
@@ -182,35 +249,66 @@ class ManageBrick extends PortalBrick
|
||||
return $this->bShowTabCounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the brick default display mode
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetDefaultDisplayMode()
|
||||
{
|
||||
return $this->sDefaultDisplayMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default display mode of the brick
|
||||
*
|
||||
* @param string $sDefaultDisplayMode
|
||||
*
|
||||
* @return \Combodo\iTop\Portal\Brick\ManageBrick
|
||||
*/
|
||||
public function SetDefaultDisplayMode($sDefaultDisplayMode)
|
||||
{
|
||||
$this->sDefaultDisplayMode = $sDefaultDisplayMode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tile mode (display)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetDisplayType()
|
||||
public function GetTileMode()
|
||||
{
|
||||
return $this->sDisplayType;
|
||||
return $this->sTileMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sDisplayType
|
||||
* Sets the tile mode (display)
|
||||
*
|
||||
* @param string $sTileMode
|
||||
* @return \Combodo\iTop\Portal\Brick\ManageBrick
|
||||
*/
|
||||
public function SetDisplayType($sDisplayType)
|
||||
public function SetTileMode($sTileMode)
|
||||
{
|
||||
$this->sDisplayType = $sDisplayType;
|
||||
$this->sTileMode = $sTileMode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sDisplayType
|
||||
* @param string $sTileMode
|
||||
*
|
||||
* @return string[] parameters for specified type, default parameters if type is invalid
|
||||
*/
|
||||
public function GetPresentationDataForDisplayType($sDisplayType)
|
||||
public function GetPresentationDataForTileMode($sTileMode)
|
||||
{
|
||||
if (isset($this->aPresentationData[$sDisplayType]))
|
||||
if (isset(static::$aPresentationData[$sTileMode]))
|
||||
{
|
||||
return $this->aPresentationData[$sDisplayType];
|
||||
return static::$aPresentationData[$sTileMode];
|
||||
}
|
||||
|
||||
return $this->aPresentationData['default'];
|
||||
return static::$aPresentationData[static::DEFAULT_TILE_MODE];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -391,7 +489,6 @@ class ManageBrick extends PortalBrick
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns if the brick has grouping tabs or not.
|
||||
*
|
||||
@@ -432,12 +529,41 @@ class ManageBrick extends PortalBrick
|
||||
return (isset($this->aGrouping['areas'])) ? $this->aGrouping['areas'] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sModeId
|
||||
*
|
||||
* @return \Combodo\iTop\Portal\Brick\ManageBrick
|
||||
*/
|
||||
public function AddAvailableDisplayMode($sModeId)
|
||||
{
|
||||
if(!in_array($sModeId, static::$aDisplayModes))
|
||||
{
|
||||
throw new Exception('ManageBrick: Display mode "' . $sModeId. '" must be one of the allowed display modes (' . implode(', ', static::$aDisplayModes) . ')');
|
||||
}
|
||||
|
||||
$this->aAvailableDisplayModes[] = $sModeId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes $sModeId from the list of availables display modes
|
||||
*
|
||||
* @param string $sModeId
|
||||
* @return \Combodo\iTop\Portal\Brick\ManageBrick
|
||||
*/
|
||||
public function RemoveAvailableDisplayMode($sModeId)
|
||||
{
|
||||
if (isset($this->aAvailableDisplayModes[$sModeId]))
|
||||
{
|
||||
unset($this->aAvailableDisplayModes[$sModeId]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the available display modes for the brick (page, not tile)
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function GetAvailablesDisplayModes()
|
||||
@@ -495,9 +621,6 @@ class ManageBrick extends PortalBrick
|
||||
public function LoadFromXml(DesignElement $oMDElement)
|
||||
{
|
||||
parent::LoadFromXml($oMDElement);
|
||||
$this->sDisplayType = 'default';
|
||||
$this->iGroupLimit = 0;
|
||||
$this->bGroupShowOthers = true;
|
||||
$bUseListFieldsForExport = false;
|
||||
|
||||
// Checking specific elements
|
||||
@@ -509,7 +632,7 @@ class ManageBrick extends PortalBrick
|
||||
$sClass = $oBrickSubNode->GetText();
|
||||
if ($sClass === '')
|
||||
{
|
||||
throw new DOMFormatException('ManageBrick : class tag is empty. Must contain Classname', null,
|
||||
throw new DOMFormatException('ManageBrick: class tag is empty. Must contain Classname', null,
|
||||
null, $oBrickSubNode);
|
||||
}
|
||||
|
||||
@@ -520,7 +643,7 @@ class ManageBrick extends PortalBrick
|
||||
$sOql = $oBrickSubNode->GetText();
|
||||
if ($sOql === '')
|
||||
{
|
||||
throw new DOMFormatException('ManageBrick : oql tag is empty. Must contain OQL statement', null,
|
||||
throw new DOMFormatException('ManageBrick: oql tag is empty. Must contain OQL statement', null,
|
||||
null, $oBrickSubNode);
|
||||
}
|
||||
|
||||
@@ -531,7 +654,7 @@ class ManageBrick extends PortalBrick
|
||||
$sOpeningMode = $oBrickSubNode->GetText(static::DEFAULT_OPENING_MODE);
|
||||
if (!in_array($sOpeningMode, array(static::ENUM_ACTION_VIEW, static::ENUM_ACTION_EDIT)))
|
||||
{
|
||||
throw new DOMFormatException('ManageBrick : opening_mode tag value must be edit|view ("'.$sOpeningMode.'" given)',
|
||||
throw new DOMFormatException('ManageBrick: opening_mode tag value must be edit|view ("'.$sOpeningMode.'" given)',
|
||||
null, null, $oBrickSubNode);
|
||||
}
|
||||
|
||||
@@ -548,14 +671,14 @@ class ManageBrick extends PortalBrick
|
||||
{
|
||||
if (!$oModeNode->hasAttribute('id'))
|
||||
{
|
||||
throw new DOMFormatException('ManageBrick : display mode must have a unique ID attribute',
|
||||
throw new DOMFormatException('ManageBrick: Display mode must have a unique ID attribute',
|
||||
null, null, $oModeNode);
|
||||
}
|
||||
|
||||
$sModeId = $oModeNode->getAttribute('id');
|
||||
if (!in_array($sModeId, ManageBrick::DISPLAY_MODES_ALLOWED))
|
||||
if (!in_array($sModeId, static::$aDisplayModes))
|
||||
{
|
||||
throw new DOMFormatException('ManageBrick : display mode has an invalid value',
|
||||
throw new DOMFormatException('ManageBrick: Display mode has an invalid value. Expected '.implode('/', static::$aDisplayModes.', "'.$sModeId.'" given.'),
|
||||
null, null, $oModeNode);
|
||||
}
|
||||
|
||||
@@ -563,11 +686,16 @@ class ManageBrick extends PortalBrick
|
||||
}
|
||||
break;
|
||||
|
||||
case 'default';
|
||||
$this->sDisplayType = $oDisplayNode->nodeValue;
|
||||
$aDisplayParameterForType = $this->GetPresentationDataForDisplayType($this->sDisplayType);
|
||||
$this->SetTileTemplatePath($aDisplayParameterForType['tileTemplate']);
|
||||
$this->SetPageTemplatePath($aDisplayParameterForType['layoutTemplate']);
|
||||
case 'default':
|
||||
$this->SetDefaultDisplayMode($oDisplayNode->GetText(static::DEFAULT_DISPLAY_MODE));
|
||||
break;
|
||||
|
||||
case 'tile';
|
||||
$this->SetTileMode($oDisplayNode->GetText(static::DEFAULT_TILE_MODE));
|
||||
|
||||
$aTileParametersForType = $this->GetPresentationDataForTileMode($this->sTileMode);
|
||||
$this->SetTileTemplatePath($aTileParametersForType['tileTemplate']);
|
||||
$this->SetPageTemplatePath($aTileParametersForType['layoutTemplate']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -689,21 +817,24 @@ class ManageBrick extends PortalBrick
|
||||
// Checking if has an oql
|
||||
if ($this->GetOql() === '')
|
||||
{
|
||||
throw new DOMFormatException('BrowseBrick : must have a valid <class|oql> tag', null, null, $oMDElement);
|
||||
throw new DOMFormatException('ManageBrick: must have a valid <class|oql> tag', null, null, $oMDElement);
|
||||
}
|
||||
|
||||
// Display modes : at least one selected
|
||||
$sDefaultDetailDisplayMode = (isset($this->sDisplayType))
|
||||
? $this->aPresentationData[$this->sDisplayType]['layoutDisplayType']
|
||||
: 'default';
|
||||
$bHasAvailableDisplayModes = (count($this->GetAvailablesDisplayModes()) > 0);
|
||||
$bIsDefaultDisplayModeInAvailableModes = in_array($sDefaultDetailDisplayMode,
|
||||
$this->GetAvailablesDisplayModes());
|
||||
if (!$bHasAvailableDisplayModes || (!$bIsDefaultDisplayModeInAvailableModes))
|
||||
{
|
||||
// legacy : setting to default
|
||||
$this->AddAvailableDisplayMode($sDefaultDetailDisplayMode);
|
||||
}
|
||||
// Checking that the brick has at least a display mode
|
||||
if (count($this->GetAvailablesDisplayModes()) === 0)
|
||||
{
|
||||
$this->AddAvailableDisplayMode(static::DEFAULT_DISPLAY_MODE);
|
||||
}
|
||||
// Checking that default display mode in among the availables
|
||||
if (!in_array($this->sDefaultDisplayMode, $this->aAvailableDisplayModes))
|
||||
{
|
||||
throw new DOMFormatException('ManageBrick: Default display mode "' . $this->sDefaultDisplayMode . '" must be one of the available display modes (' . implode(', ', $this->aAvailableDisplayModes) . ')', null, null, $oMDElement);
|
||||
}
|
||||
// Checking that tile mode in among the availables
|
||||
if (!in_array($this->sTileMode, static::$aTileModes))
|
||||
{
|
||||
throw new DOMFormatException('ManageBrick: Tile mode "' . $this->sTileMode. '" must be one of the allowed tile modes (' . implode(', ', static::$aTileModes) . ')', null, null, $oMDElement);
|
||||
}
|
||||
|
||||
// Checking if specified fields, if not we put those from the details zlist
|
||||
if (empty($this->aFields))
|
||||
@@ -725,9 +856,9 @@ class ManageBrick extends PortalBrick
|
||||
|
||||
// Checking the navigation icon
|
||||
$sDecorationClassNavigationMenu = $this->GetDecorationClassNavigationMenu();
|
||||
if (empty($sDecorationClassNavigationMenu) && isset($this->aPresentationData[$this->sDisplayType]))
|
||||
if (empty($sDecorationClassNavigationMenu) && isset(static::$aPresentationData[$this->sTileMode]))
|
||||
{
|
||||
$sDecorationClassNavigationMenu = $this->aPresentationData[$this->sDisplayType]['decorationCssClass'];
|
||||
$sDecorationClassNavigationMenu = static::$aPresentationData[$this->sTileMode]['decorationCssClass'];
|
||||
if (!empty($sDecorationClassNavigationMenu))
|
||||
{
|
||||
$this->SetDecorationClassNavigationMenu($sDecorationClassNavigationMenu);
|
||||
@@ -738,8 +869,8 @@ class ManageBrick extends PortalBrick
|
||||
if (empty($sTitle))
|
||||
{
|
||||
$sOql = $this->GetOql();
|
||||
$oSeach = DBSearch::FromOQL($sOql);
|
||||
$sClassName = MetaModel::GetName($oSeach->GetClass());
|
||||
$oSearch = DBSearch::FromOQL($sOql);
|
||||
$sClassName = MetaModel::GetName($oSearch->GetClass());
|
||||
$this->SetTitleHome($sClassName);
|
||||
$this->SetTitleNavigationMenu($sClassName);
|
||||
$this->SetTitle($sClassName);
|
||||
|
||||
@@ -35,7 +35,7 @@ abstract class PortalBrick extends AbstractBrick
|
||||
const ENUM_OPENING_TARGET_SELF = 'self';
|
||||
const ENUM_OPENING_TARGET_NEW = 'new';
|
||||
|
||||
const DEFAULT_WIDTH = 1;
|
||||
const DEFAULT_WIDTH = 6;
|
||||
const DEFAULT_HEIGHT = 1;
|
||||
const DEFAULT_MODAL = false;
|
||||
const DEFAULT_VISIBLE_HOME = true;
|
||||
|
||||
@@ -29,6 +29,7 @@ use Exception;
|
||||
use iPortalUIExtension;
|
||||
use IssueLog;
|
||||
use MetaModel;
|
||||
use cmdbAbstractObject;
|
||||
use ModuleDesign;
|
||||
use Silex\Application;
|
||||
use Symfony\Component\Debug\ErrorHandler;
|
||||
@@ -751,12 +752,7 @@ class ApplicationHelper
|
||||
// If we have still not found one, we return a default form
|
||||
if (!$bFound)
|
||||
{
|
||||
$aForm = array(
|
||||
'id' => 'default',
|
||||
'type' => 'zlist',
|
||||
'fields' => 'details',
|
||||
'layout' => null
|
||||
);
|
||||
$aForm = static::GenerateDefaultFormForClass($sClass);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1346,4 +1342,113 @@ class ApplicationHelper
|
||||
return $aUIExtensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the form data for the $sClass.
|
||||
* Form will look like the "Properties" tab of a $sClass object in the console.
|
||||
*
|
||||
* @param string $sClass
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function GenerateDefaultFormForClass($sClass, $bAddLinksets = false)
|
||||
{
|
||||
$aForm = array(
|
||||
'id' => strtolower($sClass)."-default-".uniqid(),
|
||||
'type' => 'custom_list',
|
||||
'properties' => array(
|
||||
'display_mode' => static::FORM_DEFAULT_DISPLAY_MODE,
|
||||
'always_show_submit' => static::FORM_DEFAULT_ALWAYS_SHOW_SUBMIT,
|
||||
),
|
||||
'fields' => array(),
|
||||
'layout' => array(
|
||||
'type' => 'xhtml',
|
||||
'content' => '',
|
||||
),
|
||||
);
|
||||
|
||||
// Generate layout
|
||||
$sContent = "";
|
||||
|
||||
// - Retrieve zlist details
|
||||
$aDetailsList = MetaModel::GetZListItems($sClass, 'details');
|
||||
$aDetailsStruct = cmdbAbstractObject::ProcessZlist($aDetailsList, array(), 'UI:PropertiesTab', 'col1', '');
|
||||
$aPropertiesStruct = $aDetailsStruct['UI:PropertiesTab'];
|
||||
|
||||
// Count cols (not linksets)
|
||||
$iColCount = 0;
|
||||
foreach($aPropertiesStruct as $sColId => $aColFieldsets)
|
||||
{
|
||||
if(substr($sColId, 0, 1) !== '_')
|
||||
{
|
||||
foreach($aColFieldsets as $sFieldsetName => $aAttCodes)
|
||||
{
|
||||
if(substr($sFieldsetName, 0, 1) !== '_')
|
||||
{
|
||||
$iColCount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If no cols, return a default form with all fields one after another
|
||||
if($iColCount === 0)
|
||||
{
|
||||
return array(
|
||||
'id' => 'default',
|
||||
'type' => 'zlist',
|
||||
'fields' => 'details',
|
||||
'layout' => null
|
||||
);
|
||||
}
|
||||
// Warning, this might not be great when 12 modulo $iColCount is greater than 0.
|
||||
$sColCSSClass = 'col-sm-'.floor(12/$iColCount);
|
||||
|
||||
$sLinksetsHTML = "";
|
||||
$sRowHTML = "<div class=\"row\">\n";
|
||||
foreach($aPropertiesStruct as $sColId => $aColFieldsets)
|
||||
{
|
||||
$sColsHTML = "\t<div class=\"".$sColCSSClass."\">\n";
|
||||
foreach($aColFieldsets as $sFieldsetName => $aAttCodes)
|
||||
{
|
||||
// Add fieldset, not linkset
|
||||
if(substr($sFieldsetName, 0, 1) !== '_')
|
||||
{
|
||||
$sFieldsetHTML = "\t\t<fieldset>\n";
|
||||
$sFieldsetHTML .= "\t\t\t<legend>".htmlentities(Dict::S($sFieldsetName), ENT_QUOTES, 'UTF-8')."</legend>\n";
|
||||
|
||||
foreach($aAttCodes as $sAttCode)
|
||||
{
|
||||
$sFieldsetHTML .= "\t\t\t<div class=\"form_field\" data-field-id=\"".$sAttCode."\"></div>\n";
|
||||
}
|
||||
|
||||
$sFieldsetHTML .= "\t\t</fieldset>\n";
|
||||
|
||||
// Add to col
|
||||
$sColsHTML .= $sFieldsetHTML;
|
||||
}
|
||||
elseif($bAddLinksets)
|
||||
{
|
||||
foreach($aAttCodes as $sAttCode)
|
||||
{
|
||||
$sLinksetsHTML .= "<div class=\"form_field\" data-field-id=\"".$sAttCode."\"></div>\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
$sColsHTML .= "\t</div>\n";
|
||||
|
||||
// Add col to row
|
||||
$sRowHTML .= $sColsHTML;
|
||||
}
|
||||
$sRowHTML .= "</div>\n";
|
||||
|
||||
// Add row to twig
|
||||
$sContent .= $sRowHTML;
|
||||
// Add linksets to twig
|
||||
$sContent .= $sLinksetsHTML;
|
||||
|
||||
$aForm['layout']['content'] = $sContent;
|
||||
|
||||
return $aForm;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,15 +19,23 @@
|
||||
|
||||
namespace Combodo\iTop\Portal\Helper;
|
||||
|
||||
use Exception;
|
||||
use DOMNodeList;
|
||||
use DOMFormatException;
|
||||
use utils;
|
||||
use ProfilesConfig;
|
||||
use MetaModel;
|
||||
use DBSearch;
|
||||
use DBUnionSearch;
|
||||
use DOMFormatException;
|
||||
use DOMNodeList;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
use ProfilesConfig;
|
||||
use UserRights;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Class ScopeValidatorHelper
|
||||
*
|
||||
* Inside the portal this service is injected, get the instance using $oApp['scope_validator']
|
||||
*
|
||||
* @package Combodo\iTop\Portal\Helper
|
||||
*/
|
||||
class ScopeValidatorHelper
|
||||
{
|
||||
const ENUM_MODE_READ = 'r';
|
||||
@@ -113,6 +121,7 @@ class ScopeValidatorHelper
|
||||
|
||||
$this->sInstancePrefix = $sInstancePrefix;
|
||||
$this->sGeneratedClass = $this->sInstancePrefix . static::DEFAULT_GENERATED_CLASS;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -150,7 +159,7 @@ class ScopeValidatorHelper
|
||||
{
|
||||
throw new DOMFormatException('Class tag must have an id attribute.', null, null, $oClassNode);
|
||||
}
|
||||
|
||||
|
||||
// Iterating over scope nodes of the class
|
||||
$oScopesNode = $oClassNode->GetOptionalElement('scopes');
|
||||
if ($oScopesNode !== null)
|
||||
@@ -213,7 +222,7 @@ class ScopeValidatorHelper
|
||||
{
|
||||
// Scope profile id
|
||||
$iProfileId = $this->GetProfileIdFromProfileName($sProfileName);
|
||||
|
||||
|
||||
// Now that we have the queries infos, we are going to build the queries for that profile / class
|
||||
$sMatrixPrefix = $iProfileId . '_' . $sClass . '_';
|
||||
// - View query
|
||||
@@ -284,7 +293,7 @@ class ScopeValidatorHelper
|
||||
$aProfileClasses[] = $sClass;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Filling the array with missing classes from MetaModel, so we can have an inheritance principle on the scope
|
||||
// For each class explicitly given in the scopes, we check if its child classes were also in the scope :
|
||||
// If not, we add them with the same OQL
|
||||
@@ -493,7 +502,7 @@ class ScopeValidatorHelper
|
||||
{
|
||||
$iAction = UR_ACTION_READ;
|
||||
}
|
||||
|
||||
|
||||
// Iterating on profiles to retrieving the different OQLs parts
|
||||
foreach ($aProfiles as $sProfile)
|
||||
{
|
||||
@@ -539,10 +548,37 @@ class ScopeValidatorHelper
|
||||
{
|
||||
$oSearch->AllowAllData();
|
||||
}
|
||||
|
||||
|
||||
return $oSearch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the scope query (view or edit depending on $sAction) for $sClass to the $oQuery.
|
||||
*
|
||||
* @param DBSearch $oQuery
|
||||
* @param string $sClass
|
||||
* @param string $sAction
|
||||
*
|
||||
* @return bool true if scope exists, false if scope is null
|
||||
*/
|
||||
public function AddScopeToQuery(DBSearch &$oQuery, $sClass, $sAction = UR_ACTION_READ)
|
||||
{
|
||||
$oScopeQuery = $this->GetScopeFilterForProfiles(UserRights::ListProfiles(), $sClass, $sAction);
|
||||
if ($oScopeQuery !== null)
|
||||
{
|
||||
$oQuery = $oQuery->Intersect($oScopeQuery);
|
||||
// - Allowing all data if necessary
|
||||
if ($oScopeQuery->IsAllDataAllowed())
|
||||
{
|
||||
$oQuery->AllowAllData();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if at least one of the $aProfiles has the ignore_silos flag set to true for the $sClass.
|
||||
*
|
||||
@@ -665,6 +701,7 @@ class $sClassName
|
||||
}
|
||||
|
||||
EOF;
|
||||
|
||||
return $sPHP;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,30 +22,38 @@ namespace Combodo\iTop\Portal\Router;
|
||||
class ManageBrickRouter extends AbstractRouter
|
||||
{
|
||||
static $aRoutes = array(
|
||||
array(
|
||||
'pattern' => '/manage/{sBrickId}/{sDisplayType}/{sGroupingTab}',
|
||||
'callback' => 'Combodo\\iTop\\Portal\\Controller\\ManageBrickController::DisplayAction',
|
||||
'bind' => 'p_manage_brick',
|
||||
'asserts' => array(
|
||||
'sDisplayType' => 'badge|pie-chart|bar-chart|top-list|default'
|
||||
),
|
||||
'values' => array(
|
||||
'sDisplayType' => null, // will be set using brick's XML config
|
||||
'sGroupingTab' => null
|
||||
)
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'pattern' => '/manage/{sBrickId}/{sGroupingTab}',
|
||||
'callback' => 'Combodo\\iTop\\Portal\\Controller\\ManageBrickController::DisplayAction',
|
||||
'bind' => 'p_manage_brick',
|
||||
'asserts' => array(),
|
||||
'values' => array(
|
||||
'sGroupingTab' => null,
|
||||
)
|
||||
),
|
||||
array(
|
||||
'pattern' => '/manage/{sBrickId}/display-as/{sDisplayMode}/{sGroupingTab}',
|
||||
'callback' => 'Combodo\\iTop\\Portal\\Controller\\ManageBrickController::DisplayAction',
|
||||
'bind' => 'p_manage_brick_display_as',
|
||||
'asserts' => array(
|
||||
'sDisplayMode' => 'list|pie-chart|bar-chart'
|
||||
),
|
||||
'values' => array(
|
||||
'sGroupingTab' => null,
|
||||
)
|
||||
),
|
||||
array(
|
||||
'pattern' => '/manage/{sBrickId}/{sGroupingTab}/{sGroupingArea}/page/{iPageNumber}/show/{iListLength}',
|
||||
'callback' => 'Combodo\\iTop\\Portal\\Controller\\ManageBrickController::DisplayAction',
|
||||
'bind' => 'p_manage_brick_lazy',
|
||||
'asserts' => array(
|
||||
'iPageNumber' => '\d+',
|
||||
'iListLength' => '\d+'
|
||||
'iListLength' => '\d+',
|
||||
),
|
||||
'values' => array(
|
||||
'sDataLoading' => 'lazy',
|
||||
'iPageNumber' => '1',
|
||||
'iListLength' => '20'
|
||||
'iListLength' => '20',
|
||||
)
|
||||
),
|
||||
array(
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{% block pMainContentHolder %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
{% include 'itop-portal-base/portal/src/views/bricks/manage/mode-' ~ sDisplayType ~ '.html.twig' with {'oBrick': oBrick, 'aColumns': aColumns, 'aNames': aNames, 'aUrls': aUrls, 'aDisplayValues': aDisplayValues} %}
|
||||
{% include 'itop-portal-base/portal/src/views/bricks/manage/mode-' ~ sDisplayMode ~ '.html.twig' with {'oBrick': oBrick, 'aColumns': aColumns, 'aNames': aNames, 'aUrls': aUrls, 'aDisplayValues': aDisplayValues} %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<ul class="nav nav-pills grouping_tabs">
|
||||
{% for aGroupingTab in aGroupingTabsValues %}
|
||||
<li{% if sGroupingTab is defined and sGroupingTab == aGroupingTab.value %} class="active"{% endif %}>
|
||||
<a href="{{ app.url_generator.generate('p_manage_brick', {'sBrickId': sBrickId, 'sDisplayType': 'default', 'sGroupingTab': aGroupingTab.value}) }}"
|
||||
<a href="{{ app.url_generator.generate('p_manage_brick', {'sBrickId': sBrickId, 'sDisplayMode': 'default', 'sGroupingTab': aGroupingTab.value}) }}"
|
||||
id="btn_tab_for_{{ aGroupingTab.value }}">
|
||||
{{ aGroupingTab.label|raw }}
|
||||
{% if oBrick.GetShowTabCounts() %}
|
||||
@@ -253,7 +253,7 @@
|
||||
"displayLength": {{ constant('Combodo\\iTop\\Portal\\Brick\\ManageBrick::DEFAULT_LIST_LENGTH') }},
|
||||
"dom": '<"row"<"col-sm-6"l><"col-sm-6"<f><"visible-xs"p>>>t<"row"<"col-sm-6"i><"col-sm-6"p>>',
|
||||
"columns": getColumnsDefinition('{{ sAreaId }}'),
|
||||
"order": [[0, "desc"]],
|
||||
"order": [],
|
||||
"rowCallback": function (oRow, oData) {
|
||||
if (oData.highlight_class !== undefined) {
|
||||
var sHighlightClass = oData.highlight_class;
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
{% if oBrick.GetAvailablesDisplayModes|length > 1 %}
|
||||
<div class="btn-group btn_group_explicit">
|
||||
{% for sDisplay in oBrick.GetAvailablesDisplayModes %}
|
||||
<a href="{{ app.url_generator.generate('p_manage_brick', {'sBrickId': sBrickId, 'sDisplayType': sDisplay}) }}{% if app['combodo.portal.instance.routes'][oBrick.GetRouteName]['hash'] is defined %}#{{ app['combodo.portal.instance.routes'][oBrick.GetRouteName]['hash'] }}{% endif %}"
|
||||
<a href="{{ app.url_generator.generate('p_manage_brick_display_as', {'sBrickId': sBrickId, 'sDisplayMode': sDisplay}) }}{% if app['combodo.portal.instance.routes'][oBrick.GetRouteName]['hash'] is defined %}#{{ app['combodo.portal.instance.routes'][oBrick.GetRouteName]['hash'] }}{% endif %}"
|
||||
id="btn_tab_for_{{ sDisplay }}"
|
||||
class="btn btn-default {% if sDisplay == oBrick.GetPresentationDataForDisplayType(sDisplayType).layoutDisplayType %}active{% endif %}">
|
||||
{{ ('Brick:Portal:Manage:DisplayType:' ~ sDisplay)|dict_s }}
|
||||
class="btn btn-default {% if sDisplay == oBrick.GetPresentationDataForTileMode(sDisplayMode).layoutDisplayMode %}active{% endif %}">
|
||||
{{ ('Brick:Portal:Manage:DisplayMode:' ~ sDisplay)|dict_s }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
@@ -1,28 +1,32 @@
|
||||
{# itop-portal-dashlet-basic/tile.html.twig #}
|
||||
{# Image brick tile layout #}
|
||||
|
||||
<div class="col-xs-12 col-sm-{{ oBrick.GetWidth }} tile-badge">
|
||||
<div class="col-xs-12 col-sm-{{ oBrick.GetWidth }}">
|
||||
{% block pTileWrapper %}
|
||||
<a href="{{ app.url_generator.generate('p_manage_brick', {'sBrickId': sBrickId, 'sDisplayType': oBrick.GetDisplayType}) }}{% if app['combodo.portal.instance.routes'][oBrick.GetRouteName]['hash'] is defined %}#{{ app['combodo.portal.instance.routes'][oBrick.GetRouteName]['hash'] }}{% endif %}"
|
||||
<a href="{{ app.url_generator.generate('p_manage_brick_display_as', {'sBrickId': sBrickId, 'sDisplayMode': oBrick.GetDefaultDisplayMode}) }}{% if app['combodo.portal.instance.routes'][oBrick.GetRouteName]['hash'] is defined %}#{{ app['combodo.portal.instance.routes'][oBrick.GetRouteName]['hash'] }}{% endif %}"
|
||||
{% if app['combodo.portal.instance.routes'][oBrick.GetRouteName]['navigation_menu_attr'] is defined %}{% for key, value in app['combodo.portal.instance.routes'][oBrick.GetRouteName]['navigation_menu_attr'] %} {{ key }}="{{ value }}"{% endfor %}{% endif %}
|
||||
{% if oBrick.GetModal %}data-toggle="modal" data-target="#modal-for-all"{% endif %}
|
||||
class="tile{# vertical-center#}"
|
||||
id="brick-{{ oBrick.GetId }}"
|
||||
data-brick-id="{{ oBrick.GetId }}">
|
||||
<div>
|
||||
<div class="tile_decoration">{% if oBrick.GetDecorationClassHome %}<span
|
||||
class="icon {{ oBrick.GetDecorationClassHome }}"></span>{% else %}<img src="{{ sIconURL }}"
|
||||
style="vertical-align:middle;"
|
||||
alt="Class Icon">{% endif %}
|
||||
</div>
|
||||
<div class="tile_body">
|
||||
class="tile tile_badge{# vertical-center#}"
|
||||
id="brick-{{ oBrick.GetId }}"
|
||||
data-brick-id="{{ oBrick.GetId }}"
|
||||
{% if oBrick.GetDescription is not empty %} data-toggle="tooltip" data-original-title="{{ oBrick.GetDescription|dict_s|raw }}"{% endif %}
|
||||
>
|
||||
<div>
|
||||
<div>
|
||||
<div class="tile_description">{{ oBrick.GetTitle()|dict_s }}</div>
|
||||
<div class="tile_title">{{ iCount }}</div>
|
||||
<div class="tile_decoration">{% if oBrick.GetDecorationClassHome %}<span
|
||||
class="icon {{ oBrick.GetDecorationClassHome }}"></span>{% else %}<img src="{{ sIconURL }}"
|
||||
style="vertical-align:middle;"
|
||||
alt="Class Icon">{% endif %}
|
||||
</div>
|
||||
<div class="tile_body">
|
||||
<div>
|
||||
<div class="tile_title">{{ oBrick.GetTitle()|dict_s }}</div>
|
||||
<div class="tile_count">{{ iCount }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile_description"><p>{{ oBrick.GetDescription|dict_s|raw }}</p></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile_description">{{ oBrick.GetDescription|dict_s|raw }}</div>
|
||||
</a>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
{# itop-portal-dashlet-basic/tile.html.twig #}
|
||||
{# Image brick tile layout #}
|
||||
|
||||
<div class="col-xs-12 col-sm-{{ oBrick.GetWidth }} tile-chart">
|
||||
<div class="col-xs-12 col-sm-{{ oBrick.GetWidth }}">
|
||||
{% block pTileWrapper %}
|
||||
<a href="{{ app.url_generator.generate('p_manage_brick', {'sBrickId': sBrickId, 'sDisplayType': oBrick.GetDisplayType}) }}{% if app['combodo.portal.instance.routes'][oBrick.GetRouteName]['hash'] is defined %}#{{ app['combodo.portal.instance.routes'][oBrick.GetRouteName]['hash'] }}{% endif %}"
|
||||
<a href="{{ app.url_generator.generate('p_manage_brick_display_as', {'sBrickId': sBrickId, 'sDisplayMode': oBrick.GetDefaultDisplayMode}) }}{% if app['combodo.portal.instance.routes'][oBrick.GetRouteName]['hash'] is defined %}#{{ app['combodo.portal.instance.routes'][oBrick.GetRouteName]['hash'] }}{% endif %}"
|
||||
{% if app['combodo.portal.instance.routes'][oBrick.GetRouteName]['navigation_menu_attr'] is defined %}{% for key, value in app['combodo.portal.instance.routes'][oBrick.GetRouteName]['navigation_menu_attr'] %} {{ key }}="{{ value }}"{% endfor %}{% endif %}
|
||||
{% if oBrick.GetModal %}data-toggle="modal" data-target="#modal-for-all"{% endif %}
|
||||
class="tile{# vertical-center#}"
|
||||
class="tile tile_chart{# vertical-center#}"
|
||||
id="brick-{{ oBrick.GetId }}"
|
||||
data-brick-id="{{ oBrick.GetId }}">
|
||||
<div>
|
||||
<div class="tile_title"><span class="icon fa fa-{{ oBrick.GetDisplayType }}"></span> {{ oBrick.GetTitle()|dict_s }}
|
||||
<div class="tile_title"><span class="icon fa fa-{{ oBrick.GetTileMode }}"></span> {{ oBrick.GetTitle()|dict_s }}
|
||||
({{ iCount }})</span> </div>
|
||||
{% include 'itop-portal-base/portal/src/views/bricks/manage/mode-' ~ oBrick.GetDisplayType ~ '.html.twig' with {'oBrick': oBrick, 'aColumns': aColumns, 'aNames': aNames, 'aUrls': aUrls, 'aDisplayValues': aDisplayValues} %}
|
||||
{% include 'itop-portal-base/portal/src/views/bricks/manage/mode-' ~ oBrick.GetTileMode ~ '.html.twig' with {'oBrick': oBrick, 'aColumns': aColumns, 'aNames': aNames, 'aUrls': aUrls, 'aDisplayValues': aDisplayValues} %}
|
||||
</div>
|
||||
</a>
|
||||
{% endblock %}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<div class="col-xs-12 col-sm-{{ oBrick.GetWidth }}">
|
||||
{% block pTileWrapper %}
|
||||
<a href="{{ app.url_generator.generate('p_manage_brick', {'sBrickId': sBrickId, 'sDisplayType': oBrick.GetDisplayType}) }}{% if app['combodo.portal.instance.routes'][oBrick.GetRouteName]['hash'] is defined %}#{{ app['combodo.portal.instance.routes'][oBrick.GetRouteName]['hash'] }}{% endif %}"
|
||||
<a href="{{ app.url_generator.generate('p_manage_brick_display_as', {'sBrickId': sBrickId, 'sDisplayMode': oBrick.GetDefaultDisplayMode}) }}{% if app['combodo.portal.instance.routes'][oBrick.GetRouteName]['hash'] is defined %}#{{ app['combodo.portal.instance.routes'][oBrick.GetRouteName]['hash'] }}{% endif %}"
|
||||
{% if app['combodo.portal.instance.routes'][oBrick.GetRouteName]['navigation_menu_attr'] is defined %}{% for key, value in app['combodo.portal.instance.routes'][oBrick.GetRouteName]['navigation_menu_attr'] %} {{ key }}="{{ value }}"{% endfor %}{% endif %}
|
||||
{% if oBrick.GetModal %}data-toggle="modal" data-target="#modal-for-all"{% endif %}
|
||||
class="tile{# vertical-center#}"
|
||||
|
||||
@@ -4,21 +4,6 @@
|
||||
|
||||
{% block pPageBodyClass %}home{% endblock %}
|
||||
|
||||
{# Showing only bricks that are not visible on the main content as well as a welcome message #}
|
||||
{#{% block pNavigationSideMenu %}
|
||||
<ul class="nav">
|
||||
{% for brick in app['combodo.portal.instance.conf'].bricks %}
|
||||
{% if brick.GetActive and brick.GetVisibleNavigationMenu and not brick.GetVisibleHome and brick.GetRouteName is not null %}
|
||||
<li class="{% if oBrick is defined and brick.id == oBrick.id %}active{% endif %}">
|
||||
<a href="{{ app.url_generator.generate(brick.GetRouteName, {sBrickId: brick.GetId}) }}{% if app['combodo.portal.instance.routes'][brick.GetRouteName]['hash'] is defined %}#{{ app['combodo.portal.instance.routes'][brick.GetRouteName]['hash'] }}{% endif %}" {% if app['combodo.portal.instance.routes'][brick.GetRouteName]['navigation_menu_attr'] is defined %}{% for key, value in app['combodo.portal.instance.routes'][brick.GetRouteName]['navigation_menu_attr'] %} {{ key }}="{{ value }}"{% endfor %}{% endif %} {% if brick.GetModal %}data-toggle="modal" data-target="#modal-for-all"{% endif %}>
|
||||
{{ brick.GetTitle|dict_s }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}#}
|
||||
|
||||
{% block pMainWrapper %}
|
||||
{% set iCurrentTileIndex = 0 %}
|
||||
<div class="container-fluid" id="main-wrapper">
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
{# - Misc libs #}
|
||||
<link href="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/typeahead/css/typeaheadjs.bootstrap.css'|add_itop_version }}" rel="stylesheet">
|
||||
<link href="{{ app['combodo.absolute_url'] ~ 'css/magnific-popup.css'|add_itop_version }}" rel="stylesheet">
|
||||
<link href="{{ app['combodo.absolute_url'] ~ 'css/c3.min.css'|add_itop_version }}" rel="stylesheet">
|
||||
{# - Bootstrap theme #}
|
||||
<link href="{{ app['combodo.portal.instance.conf'].properties.themes.bootstrap|add_itop_version }}" rel="stylesheet" id="css_bootstrap_theme">
|
||||
{# - Portal adjustments for BS theme #}
|
||||
@@ -84,6 +85,8 @@
|
||||
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/jquery.magnific-popup.min.js'|add_itop_version }}"></script>
|
||||
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/jquery.iframe-transport.js'|add_itop_version }}"></script>
|
||||
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/jquery.fileupload.js'|add_itop_version }}"></script>
|
||||
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/d3.min.js'|add_itop_version }}"></script>
|
||||
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/c3.min.js'|add_itop_version }}"></script>
|
||||
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/bootstrap/js/bootstrap.min.js'|add_itop_version }}"></script>
|
||||
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/latinise/latinise.min.js'|add_itop_version }}"></script>
|
||||
{# Visible.js to check if an element is visible on screen #}
|
||||
@@ -101,6 +104,8 @@
|
||||
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/datatables/js/dataTables.select.min.js'|add_itop_version }}"></script>
|
||||
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/datatables/js/datetime-moment.js'|add_itop_version }}"></script>
|
||||
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'js/dataTables.accentNeutraliseForFilter.js'|add_itop_version }}"></script>
|
||||
{# Export for Datatables #}
|
||||
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'js/export.js'|add_itop_version }}"></script>
|
||||
{# CKEditor files for HTML WYSIWYG #}
|
||||
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/ckeditor/ckeditor.js'|add_itop_version }}"></script>
|
||||
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/ckeditor/adapters/jquery.js'|add_itop_version }}"></script>
|
||||
@@ -124,6 +129,11 @@
|
||||
<script type="text/javascript" src="{{ js_file|add_itop_version }}"></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('.tile.tile_badge[data-toggle="tooltip"]').tooltip({'html': true});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body class="{% block pPageBodyClass %}{% endblock %}">
|
||||
@@ -316,7 +326,7 @@
|
||||
|
||||
<footer id="footer-wrapper">
|
||||
{% block pPageFooter%}
|
||||
<a href="http://www.combodo.com">iTop © {{ "now"|date('Y') }}</a>
|
||||
{#<a href="http://www.combodo.com">iTop © {{ "now"|date('Y') }}</a>#}
|
||||
{% endblock %}
|
||||
</footer>
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ footer {
|
||||
}
|
||||
/* Sidebar */
|
||||
@media (min-width: 768px) {
|
||||
/* Main content */
|
||||
#sidebar {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
@@ -141,6 +142,9 @@ footer {
|
||||
width: 40%;
|
||||
max-width: 100%;
|
||||
}
|
||||
#main-wrapper {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
/* Warning : Not a offical BS breakpoint */
|
||||
@media (min-width: 1600px) {
|
||||
@@ -152,12 +156,6 @@ footer {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
/* Main content */
|
||||
@media (min-width: 768px) {
|
||||
#main-wrapper {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
/* Overlays*/
|
||||
.global_overlay {
|
||||
z-index: 9999;
|
||||
@@ -334,7 +332,7 @@ footer {
|
||||
}
|
||||
.home .tile .tile_title {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
color: #777;
|
||||
}
|
||||
.home .tile .tile_title > span.icon {
|
||||
color: #ea7d1e;
|
||||
@@ -343,46 +341,30 @@ footer {
|
||||
display: none;
|
||||
color: #555;
|
||||
}
|
||||
.home div.tile-badge > a.tile {
|
||||
/**********************************/
|
||||
/* ManageBrick badge tile display */
|
||||
/**********************************/
|
||||
.home a.tile.tile_badge > div {
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
.home div.tile-badge > a.tile > div {
|
||||
.home a.tile.tile_badge > div > div {
|
||||
display: table-row;
|
||||
}
|
||||
.home div.tile-badge > a.tile > div > div {
|
||||
.home a.tile.tile_badge > div > div > div {
|
||||
display: table-cell;
|
||||
}
|
||||
.home div.tile-badge > a.tile > div > div.tile_decoration {
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
position: inherit;
|
||||
float: inherit;
|
||||
}
|
||||
.home div.tile-badge > a.tile > div > div.tile_body {
|
||||
text-align: right;
|
||||
padding-left: 0;
|
||||
}
|
||||
.home div.tile-badge > a.tile > div > div.tile_body > div:first-child {
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
margin-left: 10%;
|
||||
}
|
||||
.home div.tile-badge > a.tile > div > div.tile_body > div:first-child > div {
|
||||
text-align: center;
|
||||
}
|
||||
.home div.tile-badge > a.tile > div > div.tile_body > div:first-child > div.tile_description {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
.home div.tile-badge > a.tile > div > div.tile_body > div:first-child > div.tile_title {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
.home div.tile-badge > a.tile > div > div.tile_body > div.tile_description {
|
||||
text-align: right;
|
||||
}
|
||||
.home div.tile-chart > a > div {
|
||||
padding: 0.25em;
|
||||
@media (max-width: 768px) {
|
||||
.home a.tile.tile_badge > div > div > div.tile_body div.tile_count {
|
||||
position: absolute;
|
||||
top: 0em;
|
||||
right: 2em;
|
||||
text-align: right;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.home a.tile.tile_badge > div.tile_description {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.home .tile {
|
||||
@@ -418,6 +400,41 @@ footer {
|
||||
display: block;
|
||||
text-align: left;
|
||||
}
|
||||
.home a.tile.tile_badge {
|
||||
height: 10em;
|
||||
overflow: hidden;
|
||||
}
|
||||
.home a.tile.tile_badge > div > div > div.tile_decoration {
|
||||
top: unset;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.home a.tile.tile_badge > div > div > div.tile_decoration > span.icon {
|
||||
font-size: 4em;
|
||||
}
|
||||
.home a.tile.tile_badge > div > div > div.tile_body {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
vertical-align: middle;
|
||||
text-align: right;
|
||||
}
|
||||
.home a.tile.tile_badge > div > div > div.tile_body .tile_title {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 0;
|
||||
font-size: 1em;
|
||||
font-weight: normal;
|
||||
}
|
||||
.home a.tile.tile_badge > div > div > div.tile_body .tile_count {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
color: #777;
|
||||
}
|
||||
.home a.tile.tile_badge .tile_description {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.home .tile {
|
||||
@@ -436,6 +453,20 @@ footer {
|
||||
.home .tile .tile_description {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.home a.tile.tile_badge > div > div > div.tile_body .tile_title {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.home a.tile.tile_badge > div > div > div.tile_body .tile_count {
|
||||
font-size: 3em;
|
||||
}
|
||||
.home a.tile.tile_badge > div > div > div.tile_decoration > span.icon {
|
||||
font-size: 4em;
|
||||
}
|
||||
.home a.tile.tile_badge .tile_description {
|
||||
display: block;
|
||||
font-size: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
.home .tile {
|
||||
@@ -459,6 +490,9 @@ footer {
|
||||
.home .tile .tile_description {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.home a.tile.tile_badge > div > div > div.tile_decoration > span.icon {
|
||||
font-size: 6em;
|
||||
}
|
||||
}
|
||||
/********************/
|
||||
/* Modules settings */
|
||||
@@ -798,6 +832,7 @@ table .group-actions {
|
||||
}*/
|
||||
/* Layout 1 */
|
||||
/* Layout 7 */
|
||||
/* Layout 5 & 7 */
|
||||
.mosaic-group-item {
|
||||
display: inline-block;
|
||||
width: 32%;
|
||||
@@ -826,6 +861,7 @@ table .group-actions {
|
||||
}
|
||||
.mosaic-item-description {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.mosaic-item-layout-1 .mosaic-item-name {
|
||||
font-weight: inherit;
|
||||
@@ -839,6 +875,18 @@ table .group-actions {
|
||||
max-height: 40px;
|
||||
font-size: 10px;
|
||||
}
|
||||
.mosaic-item-layout-5 .mosaic-item-image, .mosaic-item-layout-7 .mosaic-item-image {
|
||||
display: table-cell;
|
||||
width: 65px;
|
||||
padding-left: 5px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.mosaic-item-layout-5 .mosaic-item-image > img, .mosaic-item-layout-7 .mosaic-item-image > img {
|
||||
max-width: 65px;
|
||||
/* Equals parent element width */
|
||||
max-height: 100px;
|
||||
/* Equals parent element height */
|
||||
}
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
/* Layout 5 & 7 */
|
||||
@@ -849,14 +897,14 @@ table .group-actions {
|
||||
font-size: 40px;
|
||||
}
|
||||
.mosaic-item-layout-5 .mosaic-item-image, .mosaic-item-layout-7 .mosaic-item-image {
|
||||
display: table-cell;
|
||||
width: 105px;
|
||||
padding-left: 5px;
|
||||
padding-right: 18px;
|
||||
}
|
||||
.mosaic-item-layout-5 .mosaic-item-image > img, .mosaic-item-layout-7 .mosaic-item-image > img {
|
||||
max-width: 105px;
|
||||
/* Equals parent element width */
|
||||
max-height: 100px;
|
||||
/* Equals parent element height */
|
||||
}
|
||||
.mosaic-item-layout-5 .mosaic-item-name, .mosaic-item-layout-7 .mosaic-item-name {
|
||||
font-size: 12px;
|
||||
@@ -874,6 +922,10 @@ table .group-actions {
|
||||
/* Must be .mosaic-item absolute height (in px) */
|
||||
/* overflow hidden inherited */
|
||||
}
|
||||
.mosaic-item-layout-5 .mosaic-item-image > img, .mosaic-item-layout-7 .mosaic-item-image > img {
|
||||
max-height: 120px;
|
||||
/* Equals parent element height */
|
||||
}
|
||||
}
|
||||
/* Helper classes to remove margin depending on the screen size */
|
||||
@media (min-width: 768px) and (max-width: 992px) {
|
||||
|
||||
@@ -144,12 +144,17 @@ footer{
|
||||
text-align: center;
|
||||
}
|
||||
#sidebar .logo img{
|
||||
width: 40%;
|
||||
max-width: 100%;
|
||||
}
|
||||
width: 40%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Main content */
|
||||
#main-wrapper{
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 992px){
|
||||
|
||||
|
||||
}
|
||||
/* Warning : Not a offical BS breakpoint */
|
||||
@media (min-width: 1600px){
|
||||
@@ -162,24 +167,18 @@ footer{
|
||||
}
|
||||
}
|
||||
|
||||
/* Main content */
|
||||
@media (min-width: 768px) {
|
||||
#main-wrapper{
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Overlays*/
|
||||
.global_overlay{
|
||||
z-index: 9999;
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: black;
|
||||
opacity: 0.5;
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: black;
|
||||
opacity: 0.5;
|
||||
}
|
||||
#page_overlay .overlay_content{
|
||||
margin-top: 20em;
|
||||
@@ -220,15 +219,15 @@ footer{
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
|
||||
display: flex;
|
||||
|
||||
/* Align the bootstrap's container vertically */
|
||||
-webkit-box-align : center;
|
||||
-webkit-align-items : center;
|
||||
-moz-box-align : center;
|
||||
-ms-flex-align : center;
|
||||
align-items : center;
|
||||
|
||||
|
||||
/* Also 'margin: 0 auto' doesn't have any effect on flex items in such web browsers
|
||||
hence the bootstrap's container won't be aligned to the center anymore.
|
||||
|
||||
@@ -337,10 +336,10 @@ footer{
|
||||
padding: 0em 1em;
|
||||
min-height: 4em;
|
||||
background-color: $white;
|
||||
background-image: none;
|
||||
border: none;
|
||||
background-image: none;
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
white-space: normal;
|
||||
line-height: 4em;
|
||||
@@ -353,7 +352,7 @@ footer{
|
||||
}
|
||||
.home .tile .tile_title{
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
color: $gray;
|
||||
}
|
||||
|
||||
.home .tile .tile_title > span.icon {
|
||||
@@ -365,59 +364,35 @@ footer{
|
||||
color: #555555;
|
||||
}
|
||||
|
||||
.home div.tile-badge > a.tile {
|
||||
|
||||
/**********************************/
|
||||
/* ManageBrick badge tile display */
|
||||
/**********************************/
|
||||
.home a.tile.tile_badge > div {
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.home div.tile-badge > a.tile > div {
|
||||
.home a.tile.tile_badge > div > div {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.home div.tile-badge > a.tile > div > div {
|
||||
.home a.tile.tile_badge > div > div > div {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.home div.tile-badge > a.tile > div > div.tile_decoration {
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
position: inherit;
|
||||
float: inherit;
|
||||
@media (max-width: 768px) {
|
||||
.home a.tile.tile_badge > div > div > div.tile_body div.tile_count {
|
||||
position: absolute;
|
||||
top: 0em;
|
||||
right: 2em;
|
||||
text-align: right;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.home a.tile.tile_badge > div.tile_description {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.home div.tile-badge > a.tile > div > div.tile_body {
|
||||
text-align: right;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.home div.tile-badge > a.tile > div > div.tile_body > div:first-child {
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
margin-left: 10%;
|
||||
}
|
||||
|
||||
.home div.tile-badge > a.tile > div > div.tile_body > div:first-child > div {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.home div.tile-badge > a.tile > div > div.tile_body > div:first-child > div.tile_description {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.home div.tile-badge > a.tile > div > div.tile_body > div:first-child > div.tile_title {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.home div.tile-badge > a.tile > div > div.tile_body > div.tile_description {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.home div.tile-chart > a > div {
|
||||
padding: 0.25em;
|
||||
}
|
||||
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.home .tile{
|
||||
display: block;
|
||||
@@ -452,6 +427,42 @@ footer{
|
||||
display: block;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.home a.tile.tile_badge{
|
||||
height: 10em;
|
||||
overflow: hidden;
|
||||
}
|
||||
.home a.tile.tile_badge > div > div > div.tile_decoration{
|
||||
top: unset;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.home a.tile.tile_badge > div > div > div.tile_decoration > span.icon {
|
||||
font-size: 4em;
|
||||
}
|
||||
.home a.tile.tile_badge > div > div > div.tile_body{
|
||||
position: relative;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
vertical-align: middle;
|
||||
text-align: right;
|
||||
}
|
||||
.home a.tile.tile_badge > div > div > div.tile_body .tile_title{
|
||||
margin-top: 1em;
|
||||
margin-bottom: 0;
|
||||
font-size: 1em;
|
||||
font-weight: normal;
|
||||
}
|
||||
.home a.tile.tile_badge > div > div > div.tile_body .tile_count{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
color: $gray;
|
||||
}
|
||||
.home a.tile.tile_badge .tile_description{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.home .tile{
|
||||
@@ -470,6 +481,21 @@ footer{
|
||||
.home .tile .tile_description{
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.home a.tile.tile_badge > div > div > div.tile_body .tile_title{
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.home a.tile.tile_badge > div > div > div.tile_body .tile_count{
|
||||
font-size: 3em;
|
||||
}
|
||||
.home a.tile.tile_badge > div > div > div.tile_decoration > span.icon {
|
||||
font-size: 4em;
|
||||
}
|
||||
.home a.tile.tile_badge .tile_description{
|
||||
display: block;
|
||||
font-size: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
.home .tile{
|
||||
@@ -493,6 +519,10 @@ footer{
|
||||
.home .tile .tile_description{
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.home a.tile.tile_badge > div > div > div.tile_decoration > span.icon {
|
||||
font-size: 6em;
|
||||
}
|
||||
}
|
||||
|
||||
/********************/
|
||||
@@ -559,7 +589,7 @@ footer{
|
||||
/***********************/
|
||||
/* Home tile */
|
||||
.home .tile.tile_communication{
|
||||
padding: 20px;
|
||||
padding: 20px;
|
||||
background-color: #EDEDED;
|
||||
border: none;
|
||||
font-weight: initial;
|
||||
@@ -576,15 +606,15 @@ footer{
|
||||
#user-profile-wrapper .user_profile_picture .content_loader{
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding-top: 4em;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding-top: 4em;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
color: white;
|
||||
background-color: black;
|
||||
opacity: 0.5;
|
||||
background-color: black;
|
||||
opacity: 0.5;
|
||||
}
|
||||
#user-profile-wrapper .user_profile_picture .preview{
|
||||
display: inline-block;
|
||||
@@ -722,9 +752,9 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
|
||||
|
||||
#brick_content_empty{
|
||||
display: none;
|
||||
padding: 40px;
|
||||
padding: 40px;
|
||||
font-size: 1.3em;
|
||||
font-style: italic;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Loader */
|
||||
@@ -750,7 +780,7 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
|
||||
/* eg. .mosaic-item-layout-5 when just name and image */
|
||||
/****************************************************************/
|
||||
#brick_content_mosaic{
|
||||
position: relative;
|
||||
position: relative;
|
||||
padding: 10px 10px 1px 10px;
|
||||
}
|
||||
/* Breadcrumb */
|
||||
@@ -820,12 +850,12 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
|
||||
right: 5px;
|
||||
}
|
||||
.mosaic-group-item-actions > a{
|
||||
color: $white;
|
||||
text-decoration: none;
|
||||
color: $white;
|
||||
text-decoration: none;
|
||||
}
|
||||
.mosaic-group-item-actions > a:hover,
|
||||
.mosaic-group-item-actions > a:focus{
|
||||
color: #EEEEEE;
|
||||
color: #EEEEEE;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
@@ -878,7 +908,7 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
|
||||
}
|
||||
.mosaic-item:hover,
|
||||
.mosaic-item:focus{
|
||||
background-color: $combodo-orange;
|
||||
background-color: $brand-primary;
|
||||
box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.20);
|
||||
}
|
||||
|
||||
@@ -897,6 +927,7 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
|
||||
}
|
||||
.mosaic-item-description{
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
/* Layout 1 */
|
||||
.mosaic-item-layout-1 .mosaic-item-name{
|
||||
@@ -913,6 +944,19 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
|
||||
max-height: 40px;
|
||||
font-size: 10px;
|
||||
}
|
||||
/* Layout 5 & 7 */
|
||||
.mosaic-item-layout-5 .mosaic-item-image,
|
||||
.mosaic-item-layout-7 .mosaic-item-image{
|
||||
display: table-cell;
|
||||
width: 65px;
|
||||
padding-left: 5px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.mosaic-item-layout-5 .mosaic-item-image > img,
|
||||
.mosaic-item-layout-7 .mosaic-item-image > img{
|
||||
max-width: 65px; /* Equals parent element width */
|
||||
max-height: 100px; /* Equals parent element height */
|
||||
}
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.mosaic-item{
|
||||
@@ -925,14 +969,13 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
|
||||
/* Layout 5 & 7 */
|
||||
.mosaic-item-layout-5 .mosaic-item-image,
|
||||
.mosaic-item-layout-7 .mosaic-item-image{
|
||||
display: table-cell;
|
||||
width: 105px;
|
||||
padding-left: 5px;
|
||||
padding-right: 18px;
|
||||
}
|
||||
.mosaic-item-layout-5 .mosaic-item-image > img,
|
||||
.mosaic-item-layout-7 .mosaic-item-image > img{
|
||||
max-width: 105px; /* Equals parent element width */
|
||||
max-height: 100px; /* Equals parent element height */
|
||||
}
|
||||
.mosaic-item-layout-5 .mosaic-item-name,
|
||||
.mosaic-item-layout-7 .mosaic-item-name{
|
||||
@@ -951,6 +994,11 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
|
||||
max-height: 120px; /* Must be .mosaic-item absolute height (in px) */
|
||||
/* overflow hidden inherited */
|
||||
}
|
||||
|
||||
.mosaic-item-layout-5 .mosaic-item-image > img,
|
||||
.mosaic-item-layout-7 .mosaic-item-image > img{
|
||||
max-height: 120px; /* Equals parent element height */
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper classes to remove margin depending on the screen size */
|
||||
@@ -1044,12 +1092,12 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
|
||||
.caselog_field_entry_button{
|
||||
display: block;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
text-align: center;
|
||||
height: 15px;
|
||||
text-align: center;
|
||||
line-height: 15px;
|
||||
font-size: 16px;
|
||||
border: 1px solid #a6a6a6;
|
||||
border-bottom-color: #979797;
|
||||
border: 1px solid #a6a6a6;
|
||||
border-bottom-color: #979797;
|
||||
cursor: pointer;
|
||||
}
|
||||
.caselog_field_entry_button:hover{
|
||||
@@ -1153,8 +1201,8 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
|
||||
}
|
||||
#drag_overlay .overlay_content{
|
||||
margin-top: 5em;
|
||||
width: 100%;
|
||||
color: white;
|
||||
width: 100%;
|
||||
color: white;
|
||||
}
|
||||
#drag_overlay .overlay_content .icon{
|
||||
font-size: 3em;
|
||||
@@ -1294,7 +1342,7 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
|
||||
|
||||
/* CKEditor : Adding BS error feedback */
|
||||
.form_field div.cke{
|
||||
border: 1px solid $gray-lighter;
|
||||
border: 1px solid $gray-lighter;
|
||||
}
|
||||
.form_field.has-error div.cke{
|
||||
border: 1px solid $state-danger-text;
|
||||
@@ -1303,11 +1351,11 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
|
||||
}
|
||||
/* CKEditor : Styling notifications based on BS alerts */
|
||||
.cke_notification{
|
||||
position: relative;
|
||||
position: relative;
|
||||
padding: 15px;
|
||||
margin-bottom: 18px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 18px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
background-color: $white
|
||||
}
|
||||
.cke_notification_close{
|
||||
@@ -1323,13 +1371,13 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
|
||||
.cke_notification_success{
|
||||
display: none;
|
||||
background-color: $alert-success-bg;
|
||||
border-color: $alert-success-border;
|
||||
color: $alert-success-text;
|
||||
border-color: $alert-success-border;
|
||||
color: $alert-success-text;
|
||||
}
|
||||
.cke_notification_warning{
|
||||
background-color: $alert-warning-bg;
|
||||
border-color: $alert-warning-border;
|
||||
color: $alert-warning-text;
|
||||
background-color: $alert-warning-bg;
|
||||
border-color: $alert-warning-border;
|
||||
color: $alert-warning-text;
|
||||
}
|
||||
/* CKEditor : Misc */
|
||||
.cke_toolbox_collapser,
|
||||
|
||||
@@ -103,6 +103,17 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
||||
'Brick:Portal:Manage:Name' => 'Gerenciar itens',
|
||||
'Brick:Portal:Manage:Table:NoData' => 'Sem dados.',
|
||||
'Brick:Portal:Manage:Table:ItemActions' => 'Ações',
|
||||
'Brick:Portal:Manage:DisplayMode:list' => 'List~~',
|
||||
'Brick:Portal:Manage:DisplayMode:pie-chart' => 'Pie Chart~~',
|
||||
'Brick:Portal:Manage:DisplayMode:bar-chart' => 'Bar Chart',
|
||||
'Brick:Portal:Manage:Others' => 'Others~~',
|
||||
'Brick:Portal:Manage:All' => 'All~~',
|
||||
'Brick:Portal:Manage:Group' => 'Group~~',
|
||||
'Brick:Portal:Manage:fct:count' => 'Total~~',
|
||||
'Brick:Portal:Manage:fct:sum' => 'Sum~~',
|
||||
'Brick:Portal:Manage:fct:avg' => 'Average~~',
|
||||
'Brick:Portal:Manage:fct:min' => 'Min~~',
|
||||
'Brick:Portal:Manage:fct:max' => 'Max~~',
|
||||
));
|
||||
|
||||
// ObjectBrick brick
|
||||
|
||||
@@ -85,6 +85,17 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
|
||||
'Brick:Portal:Manage:Name' => 'Управление элементами',
|
||||
'Brick:Portal:Manage:Table:NoData' => 'Нет элементов',
|
||||
'Brick:Portal:Manage:Table:ItemActions' => 'Actions~~',
|
||||
'Brick:Portal:Manage:DisplayMode:list' => 'List~~',
|
||||
'Brick:Portal:Manage:DisplayMode:pie-chart' => 'Pie Chart~~',
|
||||
'Brick:Portal:Manage:DisplayMode:bar-chart' => 'Bar Chart',
|
||||
'Brick:Portal:Manage:Others' => 'Others~~',
|
||||
'Brick:Portal:Manage:All' => 'All~~',
|
||||
'Brick:Portal:Manage:Group' => 'Group~~',
|
||||
'Brick:Portal:Manage:fct:count' => 'Total~~',
|
||||
'Brick:Portal:Manage:fct:sum' => 'Sum~~',
|
||||
'Brick:Portal:Manage:fct:avg' => 'Average~~',
|
||||
'Brick:Portal:Manage:fct:min' => 'Min~~',
|
||||
'Brick:Portal:Manage:fct:max' => 'Max~~',
|
||||
));
|
||||
|
||||
// ObjectBrick brick
|
||||
|
||||
@@ -211,6 +211,7 @@
|
||||
<states>
|
||||
<state id="new"/>
|
||||
<state id="escalated_tto"/>
|
||||
<state id="approved"/>
|
||||
</states>
|
||||
<working_time/>
|
||||
<goal>ResponseTicketTTO</goal>
|
||||
|
||||
@@ -216,6 +216,7 @@
|
||||
<states>
|
||||
<state id="new"/>
|
||||
<state id="escalated_tto"/>
|
||||
<state id="approved"/>
|
||||
</states>
|
||||
<working_time/>
|
||||
<goal>ResponseTicketTTO</goal>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2018 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -280,7 +280,7 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
||||
'Portal:TitleDetailsFor_Service' => 'Detalhes dos serviços',
|
||||
'Portal:Button:CreateRequestFromService' => 'Criar uma solicitação para esse serviço',
|
||||
'Portal:ListOpenRequests' => 'Lista solicitações abertas',
|
||||
'Portal:UserRequest:MoreInfo' => 'Mais informações',
|
||||
'Portal:UserRequest:MoreInfo' => 'Mais informações',
|
||||
'Portal:Details-Service-Element' => 'Elementos do Serviço',
|
||||
'Portal:NoClosedTicket' => 'Nenhuma solicitação fechada',
|
||||
'Portal:NoService' => '',
|
||||
@@ -295,5 +295,3 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
||||
'Class:UserRequest/Method:ResolveChildTickets' => 'ResolveChildTickets~~',
|
||||
'Class:UserRequest/Method:ResolveChildTickets+' => 'Cascade the resolution to child requests (ev_autoresolve), and align the following characteristics of the request: service, team, agent, resolution info~~',
|
||||
));
|
||||
|
||||
?>
|
||||
|
||||
@@ -69,7 +69,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Class:ServiceFamily+' => '',
|
||||
'Class:ServiceFamily/Attribute:name' => 'Name',
|
||||
'Class:ServiceFamily/Attribute:name+' => '',
|
||||
'Class:ServiceFamily/Attribute:icon' => 'Icon~~',
|
||||
'Class:ServiceFamily/Attribute:icon' => 'Icon',
|
||||
'Class:ServiceFamily/Attribute:icon+' => '',
|
||||
'Class:Service' => 'Service',
|
||||
'Class:Service+' => '',
|
||||
@@ -93,7 +93,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Class:Service/Attribute:status/Value:obsolete+' => '',
|
||||
'Class:Service/Attribute:status/Value:production' => 'Produktion',
|
||||
'Class:Service/Attribute:status/Value:production+' => '',
|
||||
'Class:Service/Attribute:icon' => 'Icon~~',
|
||||
'Class:Service/Attribute:icon' => 'Icon',
|
||||
'Class:Service/Attribute:icon+' => '',
|
||||
'Class:Service/Attribute:customercontracts_list' => 'Kunden-Verträge',
|
||||
'Class:Service/Attribute:customercontracts_list+' => '',
|
||||
@@ -248,7 +248,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Menu:SLT+' => '',
|
||||
'Menu:DeliveryModel' => 'Delivery-Modelle',
|
||||
'Menu:DeliveryModel+' => '',
|
||||
'Class:Contract' => 'Contract',
|
||||
'Class:Contract' => 'Vertrag',
|
||||
'Class:Contract+' => '',
|
||||
'Class:Contract/Attribute:name' => 'Name',
|
||||
'Class:Contract/Attribute:name+' => '',
|
||||
|
||||
@@ -65,7 +65,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Class:ServiceFamily+' => '',
|
||||
'Class:ServiceFamily/Attribute:name' => 'Name',
|
||||
'Class:ServiceFamily/Attribute:name+' => '',
|
||||
'Class:ServiceFamily/Attribute:icon' => 'Icon~~',
|
||||
'Class:ServiceFamily/Attribute:icon' => 'Icon',
|
||||
'Class:ServiceFamily/Attribute:icon+' => '',
|
||||
'Class:ServiceFamily/Attribute:services_list' => 'Services',
|
||||
'Class:ServiceFamily/Attribute:services_list+' => '',
|
||||
@@ -91,7 +91,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Class:Service/Attribute:status/Value:obsolete+' => '',
|
||||
'Class:Service/Attribute:status/Value:production' => 'Produktion',
|
||||
'Class:Service/Attribute:status/Value:production+' => '',
|
||||
'Class:Service/Attribute:icon' => 'Icon~~',
|
||||
'Class:Service/Attribute:icon' => 'Icon',
|
||||
'Class:Service/Attribute:icon+' => '',
|
||||
'Class:Service/Attribute:customercontracts_list' => 'Kunden-Verträge',
|
||||
'Class:Service/Attribute:customercontracts_list+' => '',
|
||||
|
||||
@@ -53,9 +53,6 @@
|
||||
</attributes>
|
||||
</index>
|
||||
</indexes>
|
||||
<obsolescence>
|
||||
<condition><![CDATA[operational_status='closed' AND (close_date < DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 15 MONTH),'%Y-%m-%d 00:00:00') OR (ISNULL(close_date) AND last_update < DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 15 MONTH),'%Y-%m-%d 00:00:00')))]]></condition>
|
||||
</obsolescence>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="operational_status" xsi:type="AttributeMetaEnum">
|
||||
@@ -1175,7 +1172,6 @@
|
||||
</levels>
|
||||
</level>
|
||||
</levels>
|
||||
|
||||
<browse_modes>
|
||||
<availables>
|
||||
<mode id="list"/>
|
||||
@@ -1196,12 +1192,19 @@
|
||||
<title>
|
||||
<default>Brick:Portal:OngoingRequests:Title</default>
|
||||
</title>
|
||||
<display_modes>
|
||||
<availables>
|
||||
<mode id="default"/>
|
||||
</availables>
|
||||
<default>default</default>
|
||||
</display_modes>
|
||||
<!-- Optional tag to define which display modes can be used in the brick's page and the brick's tile -->
|
||||
<!--<display_modes>-->
|
||||
<!-- Optional tag that must contain at least 1 <mode> tag. -->
|
||||
<!--<availables>-->
|
||||
<!--<mode id="list"/>-->
|
||||
<!--<mode id="pie-chart"/>-->
|
||||
<!--<mode id="bar-chart"/>-->
|
||||
<!--</availables>-->
|
||||
<!-- Optional tag to define which display mode will be used by default when opening the brick -->
|
||||
<!--<default>list</default>-->
|
||||
<!-- Optional tag to define which display mode will be used to render the brick's tile -->
|
||||
<!--<tile>text</tile>-->
|
||||
<!--</display_modes>-->
|
||||
<description>Brick:Portal:OngoingRequests:Title+</description>
|
||||
<decoration_class>
|
||||
<default>fc fc-ongoing-request fc-2x</default>
|
||||
@@ -1248,6 +1251,9 @@
|
||||
<!-- Implicit grouping on y axis by finalclass -->
|
||||
</grouping>
|
||||
<data_loading>full</data_loading>
|
||||
<export>
|
||||
<export_default_fields>true</export_default_fields>
|
||||
</export>
|
||||
</brick>
|
||||
<brick id="closed-tickets-for-portal-user" xsi:type="Combodo\iTop\Portal\Brick\ManageBrick">
|
||||
<active>true</active>
|
||||
@@ -1261,12 +1267,6 @@
|
||||
<title>
|
||||
<default>Brick:Portal:ClosedRequests:Title</default>
|
||||
</title>
|
||||
<display_modes>
|
||||
<availables>
|
||||
<mode id="default"/>
|
||||
</availables>
|
||||
<default>default</default>
|
||||
</display_modes>
|
||||
<description></description>
|
||||
<decoration_class>
|
||||
<default>fc fc-closed-request fc-2x</default>
|
||||
@@ -1298,6 +1298,9 @@
|
||||
<!-- Implicit grouping on y axis by finalclass -->
|
||||
</grouping>
|
||||
<data_loading>auto</data_loading>
|
||||
<export>
|
||||
<export_default_fields>true</export_default_fields>
|
||||
</export>
|
||||
</brick>
|
||||
</bricks>
|
||||
<forms>
|
||||
@@ -1401,7 +1404,7 @@
|
||||
<properties>
|
||||
<!--Display mode of the form fields. "cosy" for a regular labels over values layout; "compact" for a side-by-side layout with input aligned; "dense" for a side-by-side layout with input filling all available space. You can also use a custom css class that will be used on the form as "form_xxx", as well as on the fields as "form_field_xxx".-->
|
||||
<!--<display_mode>cosy</display_mode>-->
|
||||
<!-- When set to false, submit button is hidden when transitions are available on the object (in edit mode only). Default is false. -->
|
||||
<!-- When set to false, submit button is hidden when transitions are available on the object (in creation and edit mode). Default is false. -->
|
||||
<!--<always_show_submit>false</always_show_submit>-->
|
||||
</properties>
|
||||
<fields></fields>
|
||||
@@ -1549,7 +1552,7 @@
|
||||
<scope id="all">
|
||||
<oql_view><![CDATA[SELECT Contact WHERE org_id = :current_contact->org_id]]></oql_view>
|
||||
</scope>
|
||||
<scope id="all">
|
||||
<scope id="admin">
|
||||
<oql_view><![CDATA[SELECT Contact]]></oql_view>
|
||||
<allowed_profiles>
|
||||
<allowed_profile id="Administrator"/>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// 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.
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -180,65 +180,65 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
||||
'WorkOrder:Details' => 'Detalhes',
|
||||
'WorkOrder:Moreinfo' => 'Mais informações',
|
||||
|
||||
'Ticket:ImpactAnalysis' => 'Análise de impacto',
|
||||
'Class:lnkContactToTicket/Attribute:role_code' => 'Função',
|
||||
'Class:lnkContactToTicket/Attribute:role_code/Value:manual' => 'Adicionado manualmente',
|
||||
'Class:lnkContactToTicket/Attribute:role_code/Value:computed' => 'Calculado',
|
||||
'Class:lnkContactToTicket/Attribute:role_code/Value:do_not_notify' => 'Sem notificação',
|
||||
'Ticket:ImpactAnalysis' => 'Análise de Impacto~~',
|
||||
'Class:lnkContactToTicket/Attribute:role_code' => 'Role~~',
|
||||
'Class:lnkContactToTicket/Attribute:role_code/Value:manual' => 'Adicionado manualmente~~',
|
||||
'Class:lnkContactToTicket/Attribute:role_code/Value:computed' => 'Computado~~',
|
||||
'Class:lnkContactToTicket/Attribute:role_code/Value:do_not_notify' => 'Não notificar~~',
|
||||
'Class:lnkFunctionalCIToTicket/Attribute:impact_code' => 'Impact~~',
|
||||
'Class:lnkFunctionalCIToTicket/Attribute:impact_code/Value:manual' => 'Adicionado manualmente',
|
||||
'Class:lnkFunctionalCIToTicket/Attribute:impact_code/Value:computed' => 'Calculado',
|
||||
'Class:lnkFunctionalCIToTicket/Attribute:impact_code/Value:not_impacted' => 'Não calculado',
|
||||
'Tickets:ResolvedFrom' => 'Resolvido automaticamente a partir de %1$s',
|
||||
'Class:cmdbAbstractObject/Method:Set' => 'Conjunto',
|
||||
'Class:cmdbAbstractObject/Method:Set+' => 'Defina um campo com um valor estático',
|
||||
'Class:cmdbAbstractObject/Method:Set/Param:1' => 'Campo',
|
||||
'Class:cmdbAbstractObject/Method:Set/Param:1+' => 'O campo a ser definido, no objeto atual',
|
||||
'Class:cmdbAbstractObject/Method:Set/Param:2' => 'Valor',
|
||||
'Class:cmdbAbstractObject/Method:Set/Param:2+' => 'Valor do conjunto',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentDate' => 'SetCurrentDate',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentDate+' => 'Defina um campo com a data e a hora atuais',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentDate/Param:1' => 'Campo',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentDate/Param:1+' => 'O campo a ser definido, no objeto atual',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentUser' => 'SetCurrentUser',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentUser+' => 'Defina um campo com o usuário atualmente logado~~',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentUser/Param:1' => 'Campo',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentUser/Param:1+' => 'O campo a ser definido, no objeto atual. Se o campo for um texto, o nome será usado, caso contrário o identificador será usado. Esse nome é o nome da pessoa se alguma estiver anexada ao usuário, caso contrário é o login.',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentPerson' => 'SetCurrentPerson',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentPerson+' => 'Defina um campo com a pessoa registrada no momento (the \"person\" anexado e logado \"user\").~~',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentPerson/Param:1' => 'Campo',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentPerson/Param:1+' => 'O campo a ser definido, no objeto atual. Se o campo for uma string, o nome amigável será usado, caso contrário o identificador será usado.',
|
||||
'Class:cmdbAbstractObject/Method:SetElapsedTime' => 'SetElapsedTime',
|
||||
'Class:cmdbAbstractObject/Method:SetElapsedTime+' => 'Defina um campo com o tempo (segundos) decorrido desde uma data dada por outro campo',
|
||||
'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:1' => 'Campo',
|
||||
'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:1+' => 'O campo a ser definido, no objeto atual',
|
||||
'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:2' => 'Campo de referência',
|
||||
'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:2+' => 'O campo a partir do qual obter a data de referência',
|
||||
'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:3' => 'Horas trabalhadas',
|
||||
'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:3+' => 'Deixe vazio para confiar no esquema de horas de trabalho padrão ou \"DefaultWorkingTimeComputer\" para forçar um esquema 24x7',
|
||||
'Class:cmdbAbstractObject/Method:Reset' => 'Redefinir',
|
||||
'Class:cmdbAbstractObject/Method:Reset+' => 'Redefinir campo',
|
||||
'Class:cmdbAbstractObject/Method:Reset/Param:1' => 'Campo',
|
||||
'Class:cmdbAbstractObject/Method:Reset/Param:1+' => 'O campo para redefinir, no objeto atual',
|
||||
'Class:cmdbAbstractObject/Method:Copy' => 'Cópia de',
|
||||
'Class:cmdbAbstractObject/Method:Copy+' => 'Copie o valor de um campo para outro campo',
|
||||
'Class:cmdbAbstractObject/Method:Copy/Param:1' => 'Campo',
|
||||
'Class:cmdbAbstractObject/Method:Copy/Param:1+' => 'O campo a ser definido, no objeto atual',
|
||||
'Class:cmdbAbstractObject/Method:Copy/Param:2' => 'Campo fonte',
|
||||
'Class:cmdbAbstractObject/Method:Copy/Param:2+' => 'O campo para obter o valor, no objeto atual',
|
||||
'Class:ResponseTicketTTO/Interface:iMetricComputer' => 'Tempo para possuir',
|
||||
'Class:ResponseTicketTTO/Interface:iMetricComputer+' => 'Tempo baseado em SLA do tipo TTO',
|
||||
'Class:ResponseTicketTTR/Interface:iMetricComputer' => 'Tempo para resolver',
|
||||
'Class:ResponseTicketTTR/Interface:iMetricComputer+' => 'tempo baseado em SLA do tipo TTR',
|
||||
'Class:lnkFunctionalCIToTicket/Attribute:impact_code/Value:manual' => 'Adicionado manualmente~~',
|
||||
'Class:lnkFunctionalCIToTicket/Attribute:impact_code/Value:computed' => 'Computado~~',
|
||||
'Class:lnkFunctionalCIToTicket/Attribute:impact_code/Value:not_impacted' => 'Não impactado~~',
|
||||
'Tickets:ResolvedFrom' => 'Automatically resolved from %1$s~~',
|
||||
'Class:cmdbAbstractObject/Method:Set' => 'Set~~',
|
||||
'Class:cmdbAbstractObject/Method:Set+' => 'Set a field with a static value~~',
|
||||
'Class:cmdbAbstractObject/Method:Set/Param:1' => 'Target Field~~',
|
||||
'Class:cmdbAbstractObject/Method:Set/Param:1+' => 'The field to set, in the current object~~',
|
||||
'Class:cmdbAbstractObject/Method:Set/Param:2' => 'Value~~',
|
||||
'Class:cmdbAbstractObject/Method:Set/Param:2+' => 'The value to set~~',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentDate' => 'SetCurrentDate~~',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentDate+' => 'Set a field with the current date and time~~',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentDate/Param:1' => 'Target Field~~',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentDate/Param:1+' => 'The field to set, in the current object~~',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentUser' => 'SetCurrentUser~~',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentUser+' => 'Set a field with the currently logged in user~~',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentUser/Param:1' => 'Target Field~~',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentUser/Param:1+' => 'The field to set, in the current object. If the field is a string then the friendly name will be used, otherwise the identifier will be used. That friendly name is the name of the person if any is attached to the user, otherwise it is the login.~~',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentPerson' => 'SetCurrentPerson~~',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentPerson+' => 'Set a field with the currently logged in person (the \"person\" attached to the logged in \"user\").~~',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentPerson/Param:1' => 'Target Field~~',
|
||||
'Class:cmdbAbstractObject/Method:SetCurrentPerson/Param:1+' => 'The field to set, in the current object. If the field is a string then the friendly name will be used, otherwise the identifier will be used.~~',
|
||||
'Class:cmdbAbstractObject/Method:SetElapsedTime' => 'SetElapsedTime~~',
|
||||
'Class:cmdbAbstractObject/Method:SetElapsedTime+' => 'Set a field with the time (seconds) elapsed since a date given by another field~~',
|
||||
'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:1' => 'Target Field~~',
|
||||
'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:1+' => 'The field to set, in the current object~~',
|
||||
'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:2' => 'Reference Field~~',
|
||||
'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:2+' => 'The field from which to get the reference date~~',
|
||||
'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:3' => 'Working Hours~~',
|
||||
'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:3+' => 'Leave empty to rely on the standard working hours scheme, or set to \"DefaultWorkingTimeComputer\" to force a 24x7 scheme~~',
|
||||
'Class:cmdbAbstractObject/Method:Reset' => 'Reset~~',
|
||||
'Class:cmdbAbstractObject/Method:Reset+' => 'Reset a field to its default value~~',
|
||||
'Class:cmdbAbstractObject/Method:Reset/Param:1' => 'Target Field~~',
|
||||
'Class:cmdbAbstractObject/Method:Reset/Param:1+' => 'The field to reset, in the current object~~',
|
||||
'Class:cmdbAbstractObject/Method:Copy' => 'Copy~~',
|
||||
'Class:cmdbAbstractObject/Method:Copy+' => 'Copy the value of a field to another field~~',
|
||||
'Class:cmdbAbstractObject/Method:Copy/Param:1' => 'Target Field~~',
|
||||
'Class:cmdbAbstractObject/Method:Copy/Param:1+' => 'The field to set, in the current object~~',
|
||||
'Class:cmdbAbstractObject/Method:Copy/Param:2' => 'Source Field~~',
|
||||
'Class:cmdbAbstractObject/Method:Copy/Param:2+' => 'The field to get the value from, in the current object~~',
|
||||
'Class:ResponseTicketTTO/Interface:iMetricComputer' => 'Time To Own~~',
|
||||
'Class:ResponseTicketTTO/Interface:iMetricComputer+' => 'Goal based on a SLT of type TTO~~',
|
||||
'Class:ResponseTicketTTR/Interface:iMetricComputer' => 'Time To Resolve~~',
|
||||
'Class:ResponseTicketTTR/Interface:iMetricComputer+' => 'Goal based on a SLT of type TTR~~',
|
||||
|
||||
'portal:itop-portal' => 'Portal do Usuário',
|
||||
'portal:itop-portal' => 'Standard portal', // This is the portal name that will be displayed in portal dispatcher (eg. URL in menus)
|
||||
'Page:DefaultTitle' => 'iTop - Portal do Usuário',
|
||||
'Brick:Portal:UserProfile:Title' => 'Meu perfil',
|
||||
'Brick:Portal:UserProfile:Title' => 'Meu Perfil',
|
||||
'Brick:Portal:NewRequest:Title' => 'Nova Solicitação',
|
||||
'Brick:Portal:NewRequest:Title+' => '<p>Precisa de ajuda?</p><p>Escolha o catálogo de serviços e envie sua solicitação para nossa equipe.</p>',
|
||||
'Brick:Portal:OngoingRequests:Title' => 'Solicitação em andamento',
|
||||
'Brick:Portal:OngoingRequests:Title+' => '<p>Acompanhe seus pedidos em andamento.</p><p>Verifique o progresso, adicione comentários, anexe documentos, reconheça a solução.</p>',
|
||||
'Brick:Portal:OngoingRequests:Tab:OnGoing' => 'Aberto',
|
||||
'Brick:Portal:NewRequest:Title+' => '<p>Precisa de ajuda?</p><p>Escolha no Catálogo de Serviços e envie sua solicitação para nossas equipes de suporte.</p>',
|
||||
'Brick:Portal:OngoingRequests:Title' => 'Em andamento',
|
||||
'Brick:Portal:OngoingRequests:Title+' => '<p>Acompanhar suas solicitações em andamento, adicionar comentários, anexar documentos e confirmar a solução.</p>',
|
||||
'Brick:Portal:OngoingRequests:Tab:OnGoing' => 'Abrir',
|
||||
'Brick:Portal:OngoingRequests:Tab:Resolved' => 'Resolvido',
|
||||
'Brick:Portal:ClosedRequests:Title' => 'Solicitação finalizadas',
|
||||
'Brick:Portal:ClosedRequests:Title' => 'Fechar solicitações',
|
||||
));
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
<rank>2</rank>
|
||||
<parent>AdminTools</parent>
|
||||
<oql><![CDATA[SELECT URP_Profiles]]></oql>
|
||||
<do_search/>
|
||||
<do_search>1</do_search>
|
||||
<enable_class>URP_Profiles</enable_class>
|
||||
<enable_action>UR_ACTION_MODIFY</enable_action>
|
||||
</menu>
|
||||
@@ -115,7 +115,7 @@
|
||||
<rank>4</rank>
|
||||
<parent>AdminTools</parent>
|
||||
<oql><![CDATA[SELECT AuditCategory]]></oql>
|
||||
<do_search/>
|
||||
<do_search>1</do_search>
|
||||
<enable_class>AuditCategory</enable_class>
|
||||
<enable_action>UR_ACTION_MODIFY</enable_action>
|
||||
</menu>
|
||||
@@ -145,7 +145,7 @@
|
||||
<rank>10</rank>
|
||||
<parent>AdminTools</parent>
|
||||
<url>$pages/schema.php</url>
|
||||
<enable_class>ResourceAdminMenu</enable_class>
|
||||
<enable_class>ResourceRunQueriesMenu</enable_class>
|
||||
<enable_action>UR_ACTION_MODIFY</enable_action>
|
||||
</menu>
|
||||
<menu id="UniversalSearchMenu" xsi:type="WebPageMenuNode" _delta="define">
|
||||
|
||||
@@ -181,20 +181,20 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Class:EventWebService/Attribute:log_error+' => 'Ergebnis der Fehler-Protokollierung',
|
||||
'Class:EventWebService/Attribute:data' => 'Daten',
|
||||
'Class:EventWebService/Attribute:data+' => 'Ergebnisdaten',
|
||||
'Class:EventRestService' => 'REST/JSON call~~',
|
||||
'Class:EventRestService+' => 'Trace of a REST/JSON service call~~',
|
||||
'Class:EventRestService/Attribute:operation' => 'Operation~~',
|
||||
'Class:EventRestService/Attribute:operation+' => 'Argument \'operation\'~~',
|
||||
'Class:EventRestService/Attribute:version' => 'Version~~',
|
||||
'Class:EventRestService/Attribute:version+' => 'Argument \'version\'~~',
|
||||
'Class:EventRestService/Attribute:json_input' => 'Input~~',
|
||||
'Class:EventRestService/Attribute:json_input+' => 'Argument \'json_data\'~~',
|
||||
'Class:EventRestService/Attribute:code' => 'Code~~',
|
||||
'Class:EventRestService/Attribute:code+' => 'Result code~~',
|
||||
'Class:EventRestService/Attribute:json_output' => 'Response~~',
|
||||
'Class:EventRestService/Attribute:json_output+' => 'HTTP response (json)~~',
|
||||
'Class:EventRestService/Attribute:provider' => 'Provider~~',
|
||||
'Class:EventRestService/Attribute:provider+' => 'PHP class implementing the expected operation~~',
|
||||
'Class:EventRestService' => 'REST/JSON Call',
|
||||
'Class:EventRestService+' => 'Trace eines REST/JSON-Calls',
|
||||
'Class:EventRestService/Attribute:operation' => 'Operation',
|
||||
'Class:EventRestService/Attribute:operation+' => 'Argument \'operation\'',
|
||||
'Class:EventRestService/Attribute:version' => 'Version',
|
||||
'Class:EventRestService/Attribute:version+' => 'Argument \'version\'',
|
||||
'Class:EventRestService/Attribute:json_input' => 'Eingabe',
|
||||
'Class:EventRestService/Attribute:json_input+' => 'Argument \'json_data\'',
|
||||
'Class:EventRestService/Attribute:code' => 'Ergebniscode',
|
||||
'Class:EventRestService/Attribute:code+' => 'Ergebniscode',
|
||||
'Class:EventRestService/Attribute:json_output' => 'Antwort',
|
||||
'Class:EventRestService/Attribute:json_output+' => 'HTTP Antwort (JSON)',
|
||||
'Class:EventRestService/Attribute:provider' => 'Provider',
|
||||
'Class:EventRestService/Attribute:provider+' => 'PHP-Klasse die die erwartete Operation implementiert',
|
||||
'Class:EventLoginUsage' => 'Login Verwendung',
|
||||
'Class:EventLoginUsage+' => '',
|
||||
'Class:EventLoginUsage/Attribute:user_id' => 'Login',
|
||||
|
||||
@@ -118,7 +118,10 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Class:User/Attribute:allowed_org_list+' => 'Der Endbenutzer ist berechtigt, die Daten der folgenden Organisationen zu sehen. Wenn keine Organisation zu sehen ist, gibt es keine Beschränkung.',
|
||||
'Class:User/Error:LoginMustBeUnique' => 'Login-Namen müssen unterschiedlich sein - "%1s" benutzt diesen Login-Name bereits.',
|
||||
'Class:User/Error:AtLeastOneProfileIsNeeded' => 'Mindestens ein Profil muss diesem Benutzer zugewiesen sein.',
|
||||
'Class:URP_Dimensions' => 'Dimension',
|
||||
'Class:User/Error:AtLeastOneOrganizationIsNeeded' => 'Mindestens eine Organisation muss diesem Benutzer zugewiesen sein.',
|
||||
'Class:User/Error:OrganizationNotAllowed' => 'Diese Organisation ist nicht erlaubt.',
|
||||
'Class:User/Error:UserOrganizationNotAllowed' => 'Das Benutzerkonto gehört nicht zu den für Sie freigegebenen Organisationen',
|
||||
'Class:URP_Dimensions' => 'Dimension',
|
||||
'Class:URP_Dimensions+' => 'Anwendungsdimension (Festlegen von Silos)',
|
||||
'Class:URP_Dimensions/Attribute:name' => 'Name',
|
||||
'Class:URP_Dimensions/Attribute:name+' => 'Label',
|
||||
@@ -267,6 +270,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'UI:Button:Delete' => ' Löschen! ',
|
||||
'UI:Button:ChangePassword' => ' Passwort ändern ',
|
||||
'UI:Button:ResetPassword' => ' Passwort zurücksetzen ',
|
||||
'UI:Button:More' => 'Mehr',
|
||||
'UI:Button:Less' => 'Weniger',
|
||||
'UI:SearchToggle' => 'Suche',
|
||||
'UI:ClickToCreateNew' => 'Klicken Sie hier, um eine neues Objekt vom Typ %1$s zu erstellen',
|
||||
'UI:SearchFor_Class' => 'Suche nach Objekten vom Typ "%1$s"',
|
||||
@@ -559,13 +564,13 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'UI:Schema:LifeCycleAttributeMustChange' => 'Muss ändern',
|
||||
'UI:Schema:LifeCycleAttributeMustPrompt' => 'Benutzer wird zur Änderung des Wertes aufgefordert werden',
|
||||
'UI:Schema:LifeCycleEmptyList' => 'Leere Liste',
|
||||
'UI:Schema:ClassFilter' => 'Class:~~',
|
||||
'UI:Schema:DisplayLabel' => 'Display:~~',
|
||||
'UI:Schema:DisplaySelector/LabelAndCode' => 'Label and code~~',
|
||||
'UI:Schema:DisplaySelector/Label' => 'Label~~',
|
||||
'UI:Schema:DisplaySelector/Code' => 'Code~~',
|
||||
'UI:Schema:Attribute/Filter' => 'Filter~~',
|
||||
'UI:Schema:DefaultNullValue' => 'Default null : "%1$s"~~',
|
||||
'UI:Schema:ClassFilter' => 'Klasse:',
|
||||
'UI:Schema:DisplayLabel' => 'Label:',
|
||||
'UI:Schema:DisplaySelector/LabelAndCode' => 'Label und Code',
|
||||
'UI:Schema:DisplaySelector/Label' => 'Label',
|
||||
'UI:Schema:DisplaySelector/Code' => 'Code',
|
||||
'UI:Schema:Attribute/Filter' => 'Filter',
|
||||
'UI:Schema:DefaultNullValue' => 'Default null : "%1$s"',
|
||||
'UI:LinksWidget:Autocomplete+' => 'Geben Sie die ersten 3 Buchstaben ein...',
|
||||
'UI:Edit:TestQuery' => 'Query testen',
|
||||
'UI:Combo:SelectValue' => '--- wählen Sie einen Wert ---',
|
||||
@@ -952,6 +957,17 @@ Wenn Aktionen mit Trigger verknüpft sind, bekommt jede Aktion eine Auftragsnumm
|
||||
'UI:DashboardEdit:DashletProperties' => 'Dashlet-Einstellungen',
|
||||
'UI:Form:Property' => 'Einstellung',
|
||||
'UI:Form:Value' => 'Wert',
|
||||
'UI:DashletUnknown:Label' => 'Unbekannt',
|
||||
'UI:DashletUnknown:Description' => 'Unbekanntes Dashlet (ggf. wurde es deinstalliert)',
|
||||
'UI:DashletUnknown:RenderText:View' => 'Dieses Dashlet kann nicht dargestellt werden.',
|
||||
'UI:DashletUnknown:RenderText:Edit' => 'Dieses Dashlet kann nicht dargestellt werden (Klasse "%1$s"). Bitte kontaktieren Sie Ihren Administrator, ob es noch verfügbar ist.',
|
||||
'UI:DashletUnknown:RenderNoDataText:Edit' => 'Keine Vorschau für dieses Dashlet verfügbar (Klasse "%1$s").',
|
||||
'UI:DashletUnknown:Prop-XMLConfiguration' => 'Konfiguration (Anzeige des XML als einfacher Text)',
|
||||
|
||||
'UI:DashletProxy:Label' => 'Proxy',
|
||||
'UI:DashletProxy:Description' => 'Proxy Dashlet',
|
||||
'UI:DashletProxy:RenderNoDataText:Edit' => 'Keine Vorschau für dieses externe Dashlet verfügbar (Klasse "%1$s").',
|
||||
'UI:DashletProxy:Prop-XMLConfiguration' => 'Konfiguration (Anzeige des XML als einfacher Text)',
|
||||
'UI:DashletPlainText:Label' => 'Text',
|
||||
'UI:DashletPlainText:Description' => 'Reiner Text (ohne Formatierung)',
|
||||
'UI:DashletPlainText:Prop-Text' => 'Text',
|
||||
@@ -980,6 +996,29 @@ Wenn Aktionen mit Trigger verknüpft sind, bekommt jede Aktion eine Auftragsnumm
|
||||
'UI:DashletGroupByBars:Description' => 'Balkendiagramm',
|
||||
'UI:DashletGroupByTable:Label' => 'Gruppieren nach (Tabelle)',
|
||||
'UI:DashletGroupByTable:Description' => 'Liste (gruppiert nach einem Feld)',
|
||||
|
||||
// New in 2.5
|
||||
'UI:DashletGroupBy:Prop-Function' => 'Aggregatfunktion',
|
||||
'UI:DashletGroupBy:Prop-FunctionAttribute' => 'Funktionsattribut',
|
||||
'UI:DashletGroupBy:Prop-OrderDirection' => 'Richtung',
|
||||
'UI:DashletGroupBy:Prop-OrderField' => 'Sortieren nach',
|
||||
'UI:DashletGroupBy:Prop-Limit' => 'Limit',
|
||||
|
||||
'UI:DashletGroupBy:Order:asc' => 'Aufsteigend',
|
||||
'UI:DashletGroupBy:Order:desc' => 'Absteigend',
|
||||
|
||||
'UI:GroupBy:count' => 'Anzahl',
|
||||
'UI:GroupBy:count+' => 'Anzahl der Elemente',
|
||||
'UI:GroupBy:sum' => 'Suumme',
|
||||
'UI:GroupBy:sum+' => 'Suumme von %1$s',
|
||||
'UI:GroupBy:avg' => 'Durchschnitt',
|
||||
'UI:GroupBy:avg+' => 'Durchschnitt von %1$s',
|
||||
'UI:GroupBy:min' => 'Minimum',
|
||||
'UI:GroupBy:min+' => 'Minimum von %1$s',
|
||||
'UI:GroupBy:max' => 'Maximum',
|
||||
'UI:GroupBy:max+' => 'Maximum von %1$s',
|
||||
// ---
|
||||
|
||||
'UI:DashletHeaderStatic:Label' => 'Header',
|
||||
'UI:DashletHeaderStatic:Description' => 'Zeigt einen horizontalen Trenner',
|
||||
'UI:DashletHeaderStatic:Prop-Title' => 'Titel',
|
||||
@@ -1134,5 +1173,125 @@ Wenn Aktionen mit Trigger verknüpft sind, bekommt jede Aktion eine Auftragsnumm
|
||||
'portal:legacy_portal' => 'Endbenutzer-Portal',
|
||||
'portal:backoffice' => 'iTop Backend',
|
||||
'UI:UploadNotSupportedInThisMode' => 'Die Modifizierung von Bildern oder Dateien wird in diesem Modus nicht unterstützt.',
|
||||
|
||||
// Search form
|
||||
'UI:Search:Toggle' => 'Ein-/Ausklappen',
|
||||
'UI:Search:AutoSubmit:DisabledHint' => 'Automatische Eingabe für diese Klasse deaktiviert',
|
||||
'UI:Search:Criterion:MoreMenu:AddCriteria' => 'Kriterium hinzufügen',
|
||||
// - Add new criteria button
|
||||
'UI:Search:AddCriteria:List:RecentlyUsed:Title' => 'Kürzlich verwendet',
|
||||
'UI:Search:AddCriteria:List:MostPopular:Title' => 'Am beliebtesten',
|
||||
'UI:Search:AddCriteria:List:Others:Title' => 'Andere',
|
||||
'UI:Search:AddCriteria:List:RecentlyUsed:Placeholder' => 'Noch keine',
|
||||
|
||||
// - Criteria titles
|
||||
// - Default widget
|
||||
'UI:Search:Criteria:Title:Default:Any' => '%1$s: Beliebig',
|
||||
'UI:Search:Criteria:Title:Default:Empty' => '%1$s ist leer',
|
||||
'UI:Search:Criteria:Title:Default:NotEmpty' => '%1$s ist nicht leer',
|
||||
'UI:Search:Criteria:Title:Default:Equals' => '%1$s is gleich %2$s',
|
||||
'UI:Search:Criteria:Title:Default:Contains' => '%1$s enthält %2$s',
|
||||
'UI:Search:Criteria:Title:Default:StartsWith' => '%1$s startet mit %2$s',
|
||||
'UI:Search:Criteria:Title:Default:EndsWith' => '%1$s endet mit %2$s',
|
||||
'UI:Search:Criteria:Title:Default:RegExp' => '%1$s passt zu %2$s',
|
||||
'UI:Search:Criteria:Title:Default:GreaterThan' => '%1$s > %2$s',
|
||||
'UI:Search:Criteria:Title:Default:GreaterThanOrEquals' => '%1$s >= %2$s',
|
||||
'UI:Search:Criteria:Title:Default:LessThan' => '%1$s < %2$s',
|
||||
'UI:Search:Criteria:Title:Default:LessThanOrEquals' => '%1$s <= %2$s',
|
||||
'UI:Search:Criteria:Title:Default:Different' => '%1$s ≠ %2$s',
|
||||
'UI:Search:Criteria:Title:Default:Between' => '%1$s zwischen [%2$s]',
|
||||
'UI:Search:Criteria:Title:Default:BetweenDates' => '%1$s [%2$s]',
|
||||
'UI:Search:Criteria:Title:Default:BetweenDates:All' => '%1$s: Beliebig',
|
||||
'UI:Search:Criteria:Title:Default:BetweenDates:From' => '%1$s von %2$s',
|
||||
'UI:Search:Criteria:Title:Default:BetweenDates:Until' => '%1$s bis %2$s',
|
||||
'UI:Search:Criteria:Title:Default:Between:All' => '%1$s: Beliebig',
|
||||
'UI:Search:Criteria:Title:Default:Between:From' => '%1$s von %2$s',
|
||||
'UI:Search:Criteria:Title:Default:Between:Until' => '%1$s bis %2$s',
|
||||
// - Numeric widget
|
||||
// None yet
|
||||
// - DateTime widget
|
||||
'UI:Search:Criteria:Title:DateTime:Between' => '%2$s <= 1$s <= %3$s',
|
||||
// - Enum widget
|
||||
'UI:Search:Criteria:Title:Enum:In' => '%1$s: %2$s',
|
||||
'UI:Search:Criteria:Title:Enum:In:Many' => '%1$s: %2$s und %3$s andere',
|
||||
'UI:Search:Criteria:Title:Enum:In:All' => '%1$s: Beliebig',
|
||||
// - External key widget
|
||||
'UI:Search:Criteria:Title:ExternalKey:Empty' => '%1$s ist definiert',
|
||||
'UI:Search:Criteria:Title:ExternalKey:NotEmpty' => '%1$s ist nicht definiert',
|
||||
'UI:Search:Criteria:Title:ExternalKey:Equals' => '%1$s %2$s',
|
||||
'UI:Search:Criteria:Title:ExternalKey:In' => '%1$s: %2$s',
|
||||
'UI:Search:Criteria:Title:ExternalKey:In:Many' => '%1$s: %2$s und %3$s andere',
|
||||
'UI:Search:Criteria:Title:ExternalKey:In:All' => '%1$s: Beliebig',
|
||||
// - Hierarchical key widget
|
||||
'UI:Search:Criteria:Title:HierarchicalKey:Empty' => '%1$s ist definiert',
|
||||
'UI:Search:Criteria:Title:HierarchicalKey:NotEmpty' => '%1$s ist nicht definiert',
|
||||
'UI:Search:Criteria:Title:HierarchicalKey:Equals' => '%1$s %2$s',
|
||||
'UI:Search:Criteria:Title:HierarchicalKey:In' => '%1$s: %2$s',
|
||||
'UI:Search:Criteria:Title:HierarchicalKey:In:Many' => '%1$s: %2$s und %3$s andere',
|
||||
'UI:Search:Criteria:Title:HierarchicalKey:In:All' => '%1$s: Beliebig',
|
||||
|
||||
// - Criteria operators
|
||||
// - Default widget
|
||||
'UI:Search:Criteria:Operator:Default:Empty' => 'Ist leer',
|
||||
'UI:Search:Criteria:Operator:Default:NotEmpty' => 'Ist nicht leer',
|
||||
'UI:Search:Criteria:Operator:Default:Equals' => 'Ist gleich',
|
||||
'UI:Search:Criteria:Operator:Default:Between' => 'Ist zwischen',
|
||||
// - String widget
|
||||
'UI:Search:Criteria:Operator:String:Contains' => 'Enthält',
|
||||
'UI:Search:Criteria:Operator:String:StartsWith' => 'Startet mit',
|
||||
'UI:Search:Criteria:Operator:String:EndsWith' => 'Endet mit',
|
||||
'UI:Search:Criteria:Operator:String:RegExp' => 'Reg. Ausdruck',
|
||||
// - Numeric widget
|
||||
'UI:Search:Criteria:Operator:Numeric:Equals' => 'Ist gleich',
|
||||
'UI:Search:Criteria:Operator:Numeric:GreaterThan' => 'Ist größer',
|
||||
'UI:Search:Criteria:Operator:Numeric:GreaterThanOrEquals' => 'Ist größer / gleich',
|
||||
'UI:Search:Criteria:Operator:Numeric:LessThan' => 'Ist kleiner',
|
||||
'UI:Search:Criteria:Operator:Numeric:LessThanOrEquals' => 'Ist kleiner / gleich',
|
||||
'UI:Search:Criteria:Operator:Numeric:Different' => 'Ist ungleich',
|
||||
|
||||
// - Other translations
|
||||
'UI:Search:Value:Filter:Placeholder' => 'Filter...',
|
||||
'UI:Search:Value:Search:Placeholder' => 'Suche...',
|
||||
'UI:Search:Value:Autocomplete:StartTyping' => 'Geben Sie mögliche Werte ein.',
|
||||
'UI:Search:Value:Autocomplete:Wait' => 'Bitte warten...',
|
||||
'UI:Search:Value:Autocomplete:NoResult' => 'Kein Ergebnis',
|
||||
'UI:Search:Value:Toggler:CheckAllNone' => 'Alles aus- / abwählen',
|
||||
'UI:Search:Value:Toggler:CheckAllNoneFiltered' => 'Alle Sichtbaren aus- / abwählen',
|
||||
|
||||
// - Widget other translations
|
||||
'UI:Search:Criteria:Numeric:From' => 'Von',
|
||||
'UI:Search:Criteria:Numeric:Until' => 'Bis',
|
||||
'UI:Search:Criteria:Numeric:PlaceholderFrom' => 'Beliebig',
|
||||
'UI:Search:Criteria:Numeric:PlaceholderUntil' => 'Beliebig',
|
||||
'UI:Search:Criteria:DateTime:From' => 'Von',
|
||||
'UI:Search:Criteria:DateTime:FromTime' => 'Von',
|
||||
'UI:Search:Criteria:DateTime:Until' => 'Bis',
|
||||
'UI:Search:Criteria:DateTime:UntilTime' => 'Bis',
|
||||
'UI:Search:Criteria:DateTime:PlaceholderFrom' => 'Beliebig',
|
||||
'UI:Search:Criteria:DateTime:PlaceholderFromTime' => 'Beliebig',
|
||||
'UI:Search:Criteria:DateTime:PlaceholderUntil' => 'Beliebig',
|
||||
'UI:Search:Criteria:DateTime:PlaceholderUntilTime' => 'Beliebig',
|
||||
'UI:Search:Criteria:HierarchicalKey:ChildrenIncluded:Hint' => 'Kinderelemente ausgewählter Objekte werden berücksichtigt.',
|
||||
|
||||
'UI:Search:Criteria:Raw:Filtered' => 'Gefiltert',
|
||||
'UI:Search:Criteria:Raw:FilteredOn' => 'Gefiltert über %1$s',
|
||||
));
|
||||
|
||||
//
|
||||
// Expression to Natural language
|
||||
//
|
||||
Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Expression:Operator:AND' => ' UND ',
|
||||
'Expression:Operator:OR' => ' ODER ',
|
||||
|
||||
'Expression:Unit:Short:DAY' => 't',
|
||||
'Expression:Unit:Short:WEEK' => 'w',
|
||||
'Expression:Unit:Short:MONTH' => 'm',
|
||||
'Expression:Unit:Short:YEAR' => 'j',
|
||||
|
||||
'Expression:Unit:Long:DAY' => 'Tag(e)',
|
||||
'Expression:Unit:Long:HOUR' => 'Stunde(n)',
|
||||
'Expression:Unit:Long:MINUTE' => 'Minute(n)',
|
||||
|
||||
'Expression:Verb:NOW' => 'jetzt',
|
||||
));
|
||||
?>
|
||||
|
||||
@@ -140,6 +140,7 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'Class:User/Error:AtLeastOneProfileIsNeeded' => 'At least one profile must be assigned to this user.',
|
||||
'Class:User/Error:AtLeastOneOrganizationIsNeeded' => 'At least one organization must be assigned to this user.',
|
||||
'Class:User/Error:OrganizationNotAllowed' => 'Organization not allowed.',
|
||||
'Class:User/Error:UserOrganizationNotAllowed' => 'The user account does not belong to your allowed organizations.',
|
||||
'Class:UserInternal' => 'User Internal',
|
||||
'Class:UserInternal+' => 'User defined within iTop',
|
||||
));
|
||||
@@ -475,11 +476,9 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'UI:Menu:CSVExport' => 'CSV Export...',
|
||||
'UI:Menu:Modify' => 'Modify...',
|
||||
'UI:Menu:Delete' => 'Delete...',
|
||||
'UI:Menu:Manage' => 'Manage...',
|
||||
'UI:Menu:BulkDelete' => 'Delete...',
|
||||
'UI:UndefinedObject' => 'undefined',
|
||||
'UI:Document:OpenInNewWindow:Download' => 'Open in new window: %1$s, Download: %2$s',
|
||||
'UI:SelectAllToggle+' => 'Select / Deselect All',
|
||||
'UI:SplitDateTime-Date' => 'date',
|
||||
'UI:SplitDateTime-Time' => 'time',
|
||||
'UI:TruncatedResults' => '%1$d objects displayed out of %2$d',
|
||||
@@ -1184,6 +1183,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
|
||||
|
||||
'UI:DashletProxy:Label' => 'Proxy',
|
||||
'UI:DashletProxy:Description' => 'Proxy dashlet',
|
||||
'UI:DashletProxy:RenderNoDataText:Edit' => 'No preview available for this third-party dashlet (class "%1$s").',
|
||||
'UI:DashletProxy:Prop-XMLConfiguration' => 'Configuration (shown as raw XML)',
|
||||
|
||||
'UI:DashletPlainText:Label' => 'Text',
|
||||
@@ -1222,6 +1222,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
|
||||
'UI:DashletGroupBy:Prop-Function' => 'Aggregation function',
|
||||
'UI:DashletGroupBy:Prop-FunctionAttribute' => 'Function attribute',
|
||||
'UI:DashletGroupBy:Prop-OrderDirection' => 'Direction',
|
||||
'UI:DashletGroupBy:Prop-OrderField' => 'Order by',
|
||||
'UI:DashletGroupBy:Prop-Limit' => 'Limit',
|
||||
|
||||
'UI:DashletGroupBy:Order:asc' => 'Ascending',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user