mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
Dashlet extraction
This commit is contained in:
@@ -6,6 +6,8 @@
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\Dashboard\Layout\DashboardLayoutGrid;
|
||||
use Combodo\iTop\Application\Dashlet\DashletFactory;
|
||||
use Combodo\iTop\Application\Dashlet\Service\DashletService;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableSettings;
|
||||
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu;
|
||||
@@ -19,7 +21,6 @@ use Combodo\iTop\PropertyType\PropertyTypeDesign;
|
||||
use Combodo\iTop\Service\DependencyInjection\ServiceLocator;
|
||||
|
||||
require_once(APPROOT.'application/dashboardlayout.class.inc.php');
|
||||
require_once(APPROOT.'application/dashlet.class.inc.php');
|
||||
require_once(APPROOT.'core/modelreflection.class.inc.php');
|
||||
|
||||
/**
|
||||
@@ -51,6 +52,8 @@ abstract class Dashboard
|
||||
/** @var array Array of dashlets with position */
|
||||
protected array $aGridDashlets = [];
|
||||
|
||||
protected $oDashletFactory;
|
||||
|
||||
/**
|
||||
* Dashboard constructor.
|
||||
*
|
||||
@@ -65,6 +68,7 @@ abstract class Dashboard
|
||||
$this->aCells = [];
|
||||
$this->oDOMNode = null;
|
||||
$this->sId = $sId;
|
||||
$this->oDashletFactory = DashletFactory::GetInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,7 +196,7 @@ abstract class Dashboard
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $oDomNode
|
||||
* @param DesignElement $oDomNode
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
@@ -205,7 +209,7 @@ abstract class Dashboard
|
||||
// Test if dashlet can be instantiated, otherwise (uninstalled, broken, ...) we display a placeholder
|
||||
$sClass = static::GetDashletClassFromType($sDashletType);
|
||||
/** @var \Dashlet $oNewDashlet */
|
||||
$oNewDashlet = new $sClass($this->oMetaModel, $sId);
|
||||
$oNewDashlet = $this->oDashletFactory->CreateDashlet($sClass, $sId);
|
||||
$oNewDashlet->SetDashletType($sDashletType);
|
||||
$oNewDashlet->FromDOMNode($oDomNode);
|
||||
|
||||
@@ -345,7 +349,7 @@ abstract class Dashboard
|
||||
$sDashletClass = $aDashletParams['dashlet_class'];
|
||||
$sId = $aDashletParams['dashlet_id'];
|
||||
/** @var \Dashlet $oNewDashlet */
|
||||
$oNewDashlet = new $sDashletClass($this->oMetaModel, $sId);
|
||||
$oNewDashlet = $this->oDashletFactory->CreateDashlet($sDashletClass, $sId);
|
||||
if (isset($aDashletParams['dashlet_type'])) {
|
||||
$oNewDashlet->SetDashletType($aDashletParams['dashlet_type']);
|
||||
}
|
||||
@@ -671,29 +675,12 @@ JS
|
||||
* Return an array of dashlets available for selection.
|
||||
*
|
||||
* @return array
|
||||
* @throws \ReflectionException
|
||||
* @throws \Combodo\iTop\Application\Dashlet\DashletException
|
||||
* @throws \DOMFormatException
|
||||
*/
|
||||
protected function GetAvailableDashlets()
|
||||
protected function GetAvailableDashlets(): array
|
||||
{
|
||||
$aDashlets = [];
|
||||
|
||||
foreach (get_declared_classes() as $sDashletClass) {
|
||||
// DashletUnknown is not among the selection as it is just a fallback for dashlets that can't instantiated.
|
||||
if (is_subclass_of($sDashletClass, 'Dashlet') && !in_array($sDashletClass, ['DashletUnknown', 'DashletProxy'])) {
|
||||
$oReflection = new ReflectionClass($sDashletClass);
|
||||
if (!$oReflection->isAbstract()) {
|
||||
$aCallSpec = [$sDashletClass, 'IsVisible'];
|
||||
$bVisible = call_user_func($aCallSpec);
|
||||
if ($bVisible) {
|
||||
$aCallSpec = [$sDashletClass, 'GetInfo'];
|
||||
$aInfo = call_user_func($aCallSpec);
|
||||
$aDashlets[$sDashletClass] = $aInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aDashlets;
|
||||
return DashletService::GetInstance()->GetAvailableDashlets();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -796,6 +783,7 @@ class RuntimeDashboard extends Dashboard
|
||||
{
|
||||
parent::__construct($sId);
|
||||
$this->oMetaModel = new ModelReflectionRuntime();
|
||||
$this->oDashletFactory->SetModelReflectionRuntime($this->oMetaModel);
|
||||
ServiceLocator::GetInstance()->RegisterService('ModelReflection', $this->oMetaModel);
|
||||
$this->bCustomized = false;
|
||||
}
|
||||
@@ -1504,21 +1492,13 @@ JS
|
||||
|
||||
// Get the list of possible dashlets that support a creation from
|
||||
// an OQL
|
||||
$aAllDashlets = DashletService::GetInstance()->GetAvailableDashlets();
|
||||
$aDashlets = [];
|
||||
foreach (get_declared_classes() as $sDashletClass) {
|
||||
if (is_subclass_of($sDashletClass, 'Dashlet')) {
|
||||
$oReflection = new ReflectionClass($sDashletClass);
|
||||
if (!$oReflection->isAbstract()) {
|
||||
$aCallSpec = [$sDashletClass, 'CanCreateFromOQL'];
|
||||
$bShorcutMode = call_user_func($aCallSpec);
|
||||
if ($bShorcutMode) {
|
||||
$aCallSpec = [$sDashletClass, 'GetInfo'];
|
||||
$aInfo = call_user_func($aCallSpec);
|
||||
foreach ($aAllDashlets as $sDashletClass => $aInfo) {
|
||||
if ($aInfo['can_create_by_oql']) {
|
||||
$aDashlets[$sDashletClass] = ['label' => $aInfo['label'], 'class' => $sDashletClass, 'icon' => $aInfo['icon']];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$oSelectorField = new DesignerFormSelectorField('dashlet_class', Dict::S('UI:DashletCreation:DashletType'), '');
|
||||
$oForm->AddField($oSelectorField);
|
||||
@@ -1526,7 +1506,7 @@ JS
|
||||
$oSubForm = new DesignerForm();
|
||||
$oMetaModel = new ModelReflectionRuntime();
|
||||
/** @var \Dashlet $oDashlet */
|
||||
$oDashlet = new $sDashletClass($oMetaModel, 0);
|
||||
$oDashlet = DashletFactory::GetInstance()->CreateDashlet($sDashletClass, 0);
|
||||
$oDashlet->GetPropertiesFieldsFromOQL($oSubForm, $sOQL);
|
||||
|
||||
$oSelectorField->AddSubForm($oSubForm, $aDashletInfo['label'], $aDashletInfo['class']);
|
||||
|
||||
@@ -140,16 +140,16 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
|
||||
$sDashletId = $oDashlet->GetID();
|
||||
$sDashletClass = get_class($oDashlet);
|
||||
$aDashletDenormalizedProperties = $oDashlet->GetDenormalizedProperties();
|
||||
$aDashletsInfo = $sDashletClass::GetInfo();
|
||||
|
||||
// TODO 3.3 Gather real position and height/width if any.
|
||||
// Also set minimal height/width
|
||||
$iPositionX = null;
|
||||
$iPositionY = null;
|
||||
$iWidth = array_key_exists('preferred_width', $aDashletsInfo) ? $aDashletsInfo['preferred_width'] : 1;
|
||||
$iHeight = array_key_exists('preferred_height', $aDashletsInfo) ? $aDashletsInfo['preferred_height'] : 1;
|
||||
$oDashboardGrid->AddDashlet($oDashlet->DoRender($oPage, $bEditMode, true /* bEnclosingDiv */, $aExtraParams), $sDashletId, $sDashletClass, $aDashletDenormalizedProperties, $iPositionX, $iPositionY, $iWidth, $iHeight);
|
||||
//$oDashboardColumn->AddUIBlock($oDashlet->DoRender($oPage, $bEditMode, true /* bEnclosingDiv */, $aExtraParams));
|
||||
// $aDashletsInfo = $sDashletClass::GetInfo();
|
||||
//
|
||||
// // TODO 3.3 Gather real position and height/width if any.
|
||||
// // Also set minimal height/width
|
||||
// $iPositionX = null;
|
||||
// $iPositionY = null;
|
||||
// $iWidth = array_key_exists('preferred_width', $aDashletsInfo) ? $aDashletsInfo['preferred_width'] : 1;
|
||||
// $iHeight = array_key_exists('preferred_height', $aDashletsInfo) ? $aDashletsInfo['preferred_height'] : 1;
|
||||
// $oDashboardGrid->AddDashlet($oDashlet->DoRender($oPage, $bEditMode, true /* bEnclosingDiv */, $aExtraParams), $sDashletId, $sDashletClass, $aDashletDenormalizedProperties, $iPositionX, $iPositionY, $iWidth, $iHeight);
|
||||
$oDashboardColumn->AddUIBlock($oDashlet->DoRender($oPage, $bEditMode, true /* bEnclosingDiv */, $aExtraParams));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -856,48 +856,84 @@ Call $this->AddInitialAttributeFlags($sAttCode, $iFlags) for all the initial att
|
||||
<label>UI:DashletGroupByTable:Label</label>
|
||||
<icon>images/dashlets/icons8-transaction-list-48.png</icon>
|
||||
<description>UI:DashletGroupByTable:Description</description>
|
||||
<min_width>2</min_width>
|
||||
<min_height>2</min_height>
|
||||
<preferred_width>3</preferred_width>
|
||||
<preferred_height>3</preferred_height>
|
||||
<can_create_by_oql>true</can_create_by_oql>
|
||||
<configuration/>
|
||||
</dashlet>
|
||||
<dashlet id="DashletGroupByBars" _delta="define">
|
||||
<label>UI:DashletGroupByBars:Label</label>
|
||||
<icon>images/dashlets/icons8-bar-chart-48.png</icon>
|
||||
<description>UI:DashletGroupByBars:Description</description>
|
||||
<min_width>2</min_width>
|
||||
<min_height>2</min_height>
|
||||
<preferred_width>3</preferred_width>
|
||||
<preferred_height>3</preferred_height>
|
||||
<can_create_by_oql>true</can_create_by_oql>
|
||||
<configuration/>
|
||||
</dashlet>
|
||||
<dashlet id="DashletGroupByPie" _delta="define">
|
||||
<label>UI:DashletGroupByPie:Label</label>
|
||||
<icon>images/dashlets/icons8-pie-chart-48.png</icon>
|
||||
<description>UI:DashletGroupByPie:Description</description>
|
||||
<min_width>2</min_width>
|
||||
<min_height>2</min_height>
|
||||
<preferred_width>3</preferred_width>
|
||||
<preferred_height>3</preferred_height>
|
||||
<can_create_by_oql>true</can_create_by_oql>
|
||||
<configuration/>
|
||||
</dashlet>
|
||||
<dashlet id="DashletBadge" _delta="define">
|
||||
<label>UI:DashletBadge:Label</label>
|
||||
<icon>images/dashlets/icons8-badge-48.png</icon>
|
||||
<description>UI:DashletBadge:Description</description>
|
||||
<min_width>2</min_width>
|
||||
<min_height>1</min_height>
|
||||
<preferred_width>2</preferred_width>
|
||||
<preferred_height>1</preferred_height>
|
||||
<configuration/>
|
||||
</dashlet>
|
||||
<dashlet id="DashletHeaderDynamic" _delta="define">
|
||||
<label>UI:DashletHeaderDynamic:Label</label>
|
||||
<icon>images/dashlets/icons8-header-altered-48.png</icon>
|
||||
<description>UI:DashletHeaderDynamic:Description</description>
|
||||
<min_width>2</min_width>
|
||||
<min_height>1</min_height>
|
||||
<preferred_width>4</preferred_width>
|
||||
<preferred_height>3</preferred_height>
|
||||
<configuration/>
|
||||
</dashlet>
|
||||
<dashlet id="DashletHeaderStatic" _delta="define">
|
||||
<label>UI:DashletHeaderStatic:Label</label>
|
||||
<icon>images/dashlets/icons8-header-48.png</icon>
|
||||
<description>UI:DashletHeaderStatic:Description</description>
|
||||
<min_width>4</min_width>
|
||||
<min_height>1</min_height>
|
||||
<preferred_width>4</preferred_width>
|
||||
<preferred_height>1</preferred_height>
|
||||
<configuration/>
|
||||
</dashlet>
|
||||
<dashlet id="DashletObjectList" _delta="define">
|
||||
<label>UI:DashletObjectList:Label</label>
|
||||
<icon>images/dashlets/icons8-list-48.png</icon>
|
||||
<description>UI:DashletObjectList:Description</description>
|
||||
<min_width>2</min_width>
|
||||
<min_height>1</min_height>
|
||||
<preferred_width>4</preferred_width>
|
||||
<preferred_height>3</preferred_height>
|
||||
<can_create_by_oql>true</can_create_by_oql>
|
||||
<configuration/>
|
||||
</dashlet>
|
||||
<dashlet id="DashletPlainText" _delta="define">
|
||||
<label>UI:DashletPlainText:Label</label>
|
||||
<icon>images/dashlets/icons8-text-box-48.png</icon>
|
||||
<description>UI:DashletPlainText:Description</description>
|
||||
<min_width>2</min_width>
|
||||
<min_height>1</min_height>
|
||||
<preferred_width>2</preferred_width>
|
||||
<preferred_height>1</preferred_height>
|
||||
<configuration/>
|
||||
</dashlet>
|
||||
</dashlets>
|
||||
|
||||
17
git-split.md
Normal file
17
git-split.md
Normal file
@@ -0,0 +1,17 @@
|
||||
git copy of one file into two files without loosing history
|
||||
|
||||
```
|
||||
git mv foo bar
|
||||
git commit
|
||||
|
||||
SAVED=`git rev-parse HEAD`
|
||||
git reset --hard HEAD^
|
||||
git mv foo copy
|
||||
git commit
|
||||
|
||||
git merge $SAVED # This will generate conflicts
|
||||
git commit -a # Trivially resolved like this
|
||||
|
||||
git mv copy foo
|
||||
git commit
|
||||
```
|
||||
@@ -130,10 +130,27 @@ return array(
|
||||
'CheckableExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php',
|
||||
'Collator' => $vendorDir . '/symfony/polyfill-intl-icu/Resources/stubs/Collator.php',
|
||||
'Combodo\\iTop\\Application\\Branding' => $baseDir . '/sources/Application/Branding.php',
|
||||
'Combodo\\iTop\\Application\\Dashboard\\Controller\\DashboardController' => $baseDir . '/sources/Application/Dashboard/Controller/DashboardController.php',
|
||||
'Combodo\\iTop\\Application\\Dashboard\\DashboardException' => $baseDir . '/sources/Application/Dashboard/DashboardException.php',
|
||||
'Combodo\\iTop\\Application\\Dashboard\\FormBlock\\DashboardFormBlock' => $baseDir . '/sources/Application/Dashboard/FormBlock/DashboardFormBlock.php',
|
||||
'Combodo\\iTop\\Application\\Dashboard\\FormBlock\\DashletFormBlock' => $baseDir . '/sources/Application/Dashboard/FormBlock/DashletFormBlock.php',
|
||||
'Combodo\\iTop\\Application\\Dashboard\\FormBlock\\DashletPropertiesFormBlock' => $baseDir . '/sources/Application/Dashboard/FormBlock/DashletPropertiesFormBlock.php',
|
||||
'Combodo\\iTop\\Application\\Dashboard\\Layout\\DashboardLayoutGrid' => $baseDir . '/sources/Application/Dashboard/Layout/DashboardLayoutGrid.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletBadge' => $baseDir . '/sources/Application/Dashlet/Core/DashletBadge.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletGroupBy' => $baseDir . '/sources/Application/Dashlet/Core/DashletGroupBy.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletGroupByBars' => $baseDir . '/sources/Application/Dashlet/Core/DashletGroupByBars.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletGroupByPie' => $baseDir . '/sources/Application/Dashlet/Core/DashletGroupByPie.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletGroupByTable' => $baseDir . '/sources/Application/Dashlet/Core/DashletGroupByTable.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletHeaderDynamic' => $baseDir . '/sources/Application/Dashlet/Core/DashletHeaderDynamic.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletHeaderStatic' => $baseDir . '/sources/Application/Dashlet/Core/DashletHeaderStatic.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletObjectList' => $baseDir . '/sources/Application/Dashlet/Core/DashletObjectList.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletPlainText' => $baseDir . '/sources/Application/Dashlet/Core/DashletPlainText.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletProxy' => $baseDir . '/sources/Application/Dashlet/Core/DashletProxy.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletUnknown' => $baseDir . '/sources/Application/Dashlet/Core/DashletUnknown.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Dashlet' => $baseDir . '/sources/Application/Dashlet/Dashlet.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\DashletException' => $baseDir . '/sources/Application/Dashlet/DashletException.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\DashletFactory' => $baseDir . '/sources/Application/Dashlet/DashletFactory.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Service\\DashletService' => $baseDir . '/sources/Application/Dashlet/Service/DashletService.php',
|
||||
'Combodo\\iTop\\Application\\EventRegister\\ApplicationEvents' => $baseDir . '/sources/Application/EventRegister/ApplicationEvents.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\CKEditorHelper' => $baseDir . '/sources/Application/Helper/CKEditorHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\ExportHelper' => $baseDir . '/sources/Application/Helper/ExportHelper.php',
|
||||
@@ -354,7 +371,6 @@ return array(
|
||||
'Combodo\\iTop\\Controller\\AbstractController' => $baseDir . '/sources/Controller/AbstractController.php',
|
||||
'Combodo\\iTop\\Controller\\AjaxRenderController' => $baseDir . '/sources/Controller/AjaxRenderController.php',
|
||||
'Combodo\\iTop\\Controller\\Base\\Layout\\ActivityPanelController' => $baseDir . '/sources/Controller/Base/Layout/ActivityPanelController.php',
|
||||
'Combodo\\iTop\\Controller\\Base\\Layout\\DashboardController' => $baseDir . '/sources/Controller/Base/Layout/DashboardController.php',
|
||||
'Combodo\\iTop\\Controller\\Base\\Layout\\ObjectController' => $baseDir . '/sources/Controller/Base/Layout/ObjectController.php',
|
||||
'Combodo\\iTop\\Controller\\Base\\Layout\\OqlController' => $baseDir . '/sources/Controller/Base/Layout/OqlController.php',
|
||||
'Combodo\\iTop\\Controller\\Links\\LinkSetController' => $baseDir . '/sources/Controller/Links/LinkSetController.php',
|
||||
@@ -624,7 +640,6 @@ return array(
|
||||
'Combodo\\iTop\\Service\\Base\\ObjectRepository' => $baseDir . '/sources/Service/Base/ObjectRepository.php',
|
||||
'Combodo\\iTop\\Service\\Base\\iDataPostProcessor' => $baseDir . '/sources/Service/Base/iDataPostProcessor.php',
|
||||
'Combodo\\iTop\\Service\\Cache\\DataModelDependantCache' => $baseDir . '/sources/Service/Cache/DataModelDependantCache.php',
|
||||
'Combodo\\iTop\\Service\\Dashboard\\DashletService' => $baseDir . '/sources/Service/Dashboard/DashletService.php',
|
||||
'Combodo\\iTop\\Service\\DependencyInjection\\DIException' => $baseDir . '/sources/Service/DependencyInjection/DIException.php',
|
||||
'Combodo\\iTop\\Service\\DependencyInjection\\ServiceLocator' => $baseDir . '/sources/Service/DependencyInjection/ServiceLocator.php',
|
||||
'Combodo\\iTop\\Service\\Events\\Description\\EventDataDescription' => $baseDir . '/sources/Service/Events/Description/EventDataDescription.php',
|
||||
@@ -694,19 +709,6 @@ return array(
|
||||
'DashboardLayoutThreeCols' => $baseDir . '/application/dashboardlayout.class.inc.php',
|
||||
'DashboardLayoutTwoCols' => $baseDir . '/application/dashboardlayout.class.inc.php',
|
||||
'DashboardMenuNode' => $baseDir . '/application/menunode.class.inc.php',
|
||||
'Dashlet' => $baseDir . '/application/dashlet.class.inc.php',
|
||||
'DashletBadge' => $baseDir . '/application/dashlet.class.inc.php',
|
||||
'DashletEmptyCell' => $baseDir . '/application/dashlet.class.inc.php',
|
||||
'DashletGroupBy' => $baseDir . '/application/dashlet.class.inc.php',
|
||||
'DashletGroupByBars' => $baseDir . '/application/dashlet.class.inc.php',
|
||||
'DashletGroupByPie' => $baseDir . '/application/dashlet.class.inc.php',
|
||||
'DashletGroupByTable' => $baseDir . '/application/dashlet.class.inc.php',
|
||||
'DashletHeaderDynamic' => $baseDir . '/application/dashlet.class.inc.php',
|
||||
'DashletHeaderStatic' => $baseDir . '/application/dashlet.class.inc.php',
|
||||
'DashletObjectList' => $baseDir . '/application/dashlet.class.inc.php',
|
||||
'DashletPlainText' => $baseDir . '/application/dashlet.class.inc.php',
|
||||
'DashletProxy' => $baseDir . '/application/dashlet.class.inc.php',
|
||||
'DashletUnknown' => $baseDir . '/application/dashlet.class.inc.php',
|
||||
'Datamatrix' => $vendorDir . '/tecnickcom/tcpdf/include/barcodes/datamatrix.php',
|
||||
'DateError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateError.php',
|
||||
'DateException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateException.php',
|
||||
|
||||
@@ -516,10 +516,27 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'CheckableExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php',
|
||||
'Collator' => __DIR__ . '/..' . '/symfony/polyfill-intl-icu/Resources/stubs/Collator.php',
|
||||
'Combodo\\iTop\\Application\\Branding' => __DIR__ . '/../..' . '/sources/Application/Branding.php',
|
||||
'Combodo\\iTop\\Application\\Dashboard\\Controller\\DashboardController' => __DIR__ . '/../..' . '/sources/Application/Dashboard/Controller/DashboardController.php',
|
||||
'Combodo\\iTop\\Application\\Dashboard\\DashboardException' => __DIR__ . '/../..' . '/sources/Application/Dashboard/DashboardException.php',
|
||||
'Combodo\\iTop\\Application\\Dashboard\\FormBlock\\DashboardFormBlock' => __DIR__ . '/../..' . '/sources/Application/Dashboard/FormBlock/DashboardFormBlock.php',
|
||||
'Combodo\\iTop\\Application\\Dashboard\\FormBlock\\DashletFormBlock' => __DIR__ . '/../..' . '/sources/Application/Dashboard/FormBlock/DashletFormBlock.php',
|
||||
'Combodo\\iTop\\Application\\Dashboard\\FormBlock\\DashletPropertiesFormBlock' => __DIR__ . '/../..' . '/sources/Application/Dashboard/FormBlock/DashletPropertiesFormBlock.php',
|
||||
'Combodo\\iTop\\Application\\Dashboard\\Layout\\DashboardLayoutGrid' => __DIR__ . '/../..' . '/sources/Application/Dashboard/Layout/DashboardLayoutGrid.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletBadge' => __DIR__ . '/../..' . '/sources/Application/Dashlet/Core/DashletBadge.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletGroupBy' => __DIR__ . '/../..' . '/sources/Application/Dashlet/Core/DashletGroupBy.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletGroupByBars' => __DIR__ . '/../..' . '/sources/Application/Dashlet/Core/DashletGroupByBars.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletGroupByPie' => __DIR__ . '/../..' . '/sources/Application/Dashlet/Core/DashletGroupByPie.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletGroupByTable' => __DIR__ . '/../..' . '/sources/Application/Dashlet/Core/DashletGroupByTable.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletHeaderDynamic' => __DIR__ . '/../..' . '/sources/Application/Dashlet/Core/DashletHeaderDynamic.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletHeaderStatic' => __DIR__ . '/../..' . '/sources/Application/Dashlet/Core/DashletHeaderStatic.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletObjectList' => __DIR__ . '/../..' . '/sources/Application/Dashlet/Core/DashletObjectList.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletPlainText' => __DIR__ . '/../..' . '/sources/Application/Dashlet/Core/DashletPlainText.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletProxy' => __DIR__ . '/../..' . '/sources/Application/Dashlet/Core/DashletProxy.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Core\\DashletUnknown' => __DIR__ . '/../..' . '/sources/Application/Dashlet/Core/DashletUnknown.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Dashlet' => __DIR__ . '/../..' . '/sources/Application/Dashlet/Dashlet.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\DashletException' => __DIR__ . '/../..' . '/sources/Application/Dashlet/DashletException.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\DashletFactory' => __DIR__ . '/../..' . '/sources/Application/Dashlet/DashletFactory.php',
|
||||
'Combodo\\iTop\\Application\\Dashlet\\Service\\DashletService' => __DIR__ . '/../..' . '/sources/Application/Dashlet/Service/DashletService.php',
|
||||
'Combodo\\iTop\\Application\\EventRegister\\ApplicationEvents' => __DIR__ . '/../..' . '/sources/Application/EventRegister/ApplicationEvents.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\CKEditorHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/CKEditorHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\ExportHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/ExportHelper.php',
|
||||
@@ -740,7 +757,6 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Controller\\AbstractController' => __DIR__ . '/../..' . '/sources/Controller/AbstractController.php',
|
||||
'Combodo\\iTop\\Controller\\AjaxRenderController' => __DIR__ . '/../..' . '/sources/Controller/AjaxRenderController.php',
|
||||
'Combodo\\iTop\\Controller\\Base\\Layout\\ActivityPanelController' => __DIR__ . '/../..' . '/sources/Controller/Base/Layout/ActivityPanelController.php',
|
||||
'Combodo\\iTop\\Controller\\Base\\Layout\\DashboardController' => __DIR__ . '/../..' . '/sources/Controller/Base/Layout/DashboardController.php',
|
||||
'Combodo\\iTop\\Controller\\Base\\Layout\\ObjectController' => __DIR__ . '/../..' . '/sources/Controller/Base/Layout/ObjectController.php',
|
||||
'Combodo\\iTop\\Controller\\Base\\Layout\\OqlController' => __DIR__ . '/../..' . '/sources/Controller/Base/Layout/OqlController.php',
|
||||
'Combodo\\iTop\\Controller\\Links\\LinkSetController' => __DIR__ . '/../..' . '/sources/Controller/Links/LinkSetController.php',
|
||||
@@ -1010,7 +1026,6 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Service\\Base\\ObjectRepository' => __DIR__ . '/../..' . '/sources/Service/Base/ObjectRepository.php',
|
||||
'Combodo\\iTop\\Service\\Base\\iDataPostProcessor' => __DIR__ . '/../..' . '/sources/Service/Base/iDataPostProcessor.php',
|
||||
'Combodo\\iTop\\Service\\Cache\\DataModelDependantCache' => __DIR__ . '/../..' . '/sources/Service/Cache/DataModelDependantCache.php',
|
||||
'Combodo\\iTop\\Service\\Dashboard\\DashletService' => __DIR__ . '/../..' . '/sources/Service/Dashboard/DashletService.php',
|
||||
'Combodo\\iTop\\Service\\DependencyInjection\\DIException' => __DIR__ . '/../..' . '/sources/Service/DependencyInjection/DIException.php',
|
||||
'Combodo\\iTop\\Service\\DependencyInjection\\ServiceLocator' => __DIR__ . '/../..' . '/sources/Service/DependencyInjection/ServiceLocator.php',
|
||||
'Combodo\\iTop\\Service\\Events\\Description\\EventDataDescription' => __DIR__ . '/../..' . '/sources/Service/Events/Description/EventDataDescription.php',
|
||||
@@ -1080,19 +1095,6 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'DashboardLayoutThreeCols' => __DIR__ . '/../..' . '/application/dashboardlayout.class.inc.php',
|
||||
'DashboardLayoutTwoCols' => __DIR__ . '/../..' . '/application/dashboardlayout.class.inc.php',
|
||||
'DashboardMenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php',
|
||||
'Dashlet' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php',
|
||||
'DashletBadge' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php',
|
||||
'DashletEmptyCell' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php',
|
||||
'DashletGroupBy' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php',
|
||||
'DashletGroupByBars' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php',
|
||||
'DashletGroupByPie' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php',
|
||||
'DashletGroupByTable' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php',
|
||||
'DashletHeaderDynamic' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php',
|
||||
'DashletHeaderStatic' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php',
|
||||
'DashletObjectList' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php',
|
||||
'DashletPlainText' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php',
|
||||
'DashletProxy' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php',
|
||||
'DashletUnknown' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php',
|
||||
'Datamatrix' => __DIR__ . '/..' . '/tecnickcom/tcpdf/include/barcodes/datamatrix.php',
|
||||
'DateError' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateError.php',
|
||||
'DateException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateException.php',
|
||||
|
||||
@@ -704,6 +704,9 @@ PHP;
|
||||
$oPropertyTypesNode = $this->oFactory->GetNodes('/itop_design/meta/property_types')->item(0);
|
||||
$this->CompilePropertyTypes($oPropertyTypesNode, $sTempTargetDir, $sFinalTargetDir);
|
||||
|
||||
$oDashletNode = $this->oFactory->GetNodes('/itop_design/meta/dashlets')->item(0);
|
||||
$this->CompileDashlets($oDashletNode, $sTempTargetDir, $sFinalTargetDir);
|
||||
|
||||
// Compile the XML parameters
|
||||
/** @var \MFElement $oParametersNode */
|
||||
$oParametersNode = $this->oFactory->GetNodes('/itop_design/module_parameters')->item(0);
|
||||
@@ -3592,6 +3595,16 @@ EOF;
|
||||
}
|
||||
}
|
||||
|
||||
protected function CompileDashlets(?DOMNode $oDashlets, string $sTempTargetDir, string $sFinalTargetDir): void
|
||||
{
|
||||
if ($oDashlets) {
|
||||
$oDoc = new DesignDocument();
|
||||
$oClone = $oDoc->importNode($oDashlets->cloneNode(true), true);
|
||||
$oDoc->appendChild($oClone);
|
||||
$oDoc->save($sTempTargetDir.'/core/dashlets.xml');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \DOMFormatException
|
||||
*/
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Controller\Base\Layout;
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Application\Dashboard\Controller;
|
||||
|
||||
use Combodo\iTop\Application\Dashlet\DashletFactory;
|
||||
use Combodo\iTop\Application\TwigBase\Controller\Controller;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Dashlet\DashletWrapper;
|
||||
use Combodo\iTop\Application\UI\Base\Component\TurboForm\TurboFormUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\iUIBlock;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\Dashboard\DashboardGrid;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
|
||||
use Combodo\iTop\Application\WebPage\AjaxPage;
|
||||
use Combodo\iTop\Application\WebPage\JsonPage;
|
||||
use Combodo\iTop\Controller\AbstractController;
|
||||
use Combodo\iTop\Forms\Block\FormBlockService;
|
||||
use Combodo\iTop\ItopSdkFormDemonstrator\Helper\ItopSdkFormDemonstratorLog;
|
||||
use Combodo\iTop\PropertyType\PropertyType;
|
||||
use Combodo\iTop\PropertyType\Serializer\XMLSerializer;
|
||||
use Combodo\iTop\Service\DependencyInjection\ServiceLocator;
|
||||
use Dashboard;
|
||||
use Exception;
|
||||
use IssueLog;
|
||||
use ModelReflectionRuntime;
|
||||
@@ -39,11 +40,11 @@ class DashboardController extends Controller
|
||||
|
||||
$oPage = new AjaxPage('');
|
||||
|
||||
if (is_subclass_of($sDashletClass, 'Dashlet')) {
|
||||
if (is_a($sDashletClass, 'Dashlet', true)) {
|
||||
// TODO 3.3 Make a real unique id if none is provided
|
||||
$sDashletId = !empty($sDashletId) ? $sDashletId : uniqid();
|
||||
|
||||
$oDashlet = new $sDashletClass(new ModelReflectionRuntime(), $sDashletId);
|
||||
$oDashlet = DashletFactory::GetInstance()->CreateDashlet($sDashletClass, $sDashletId);
|
||||
|
||||
// TODO 3.3 This is not the place to register this service, do better please
|
||||
ServiceLocator::GetInstance()->RegisterService('ModelReflection', new ModelReflectionRuntime());
|
||||
@@ -113,8 +114,7 @@ class DashboardController extends Controller
|
||||
$oDashboard->PersistDashboard($sXml);
|
||||
$sStatus = 'ok';
|
||||
$sMessage = 'Dashboard saved';
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$sStatus = 'error';
|
||||
$aFormErrors = $oForm->getErrors(true, true);
|
||||
$sMessage = $aFormErrors->__toString();
|
||||
@@ -128,7 +128,7 @@ class DashboardController extends Controller
|
||||
$oPage = new JsonPage();
|
||||
$oPage->SetData([
|
||||
'status' => $sStatus,
|
||||
'message' => $sMessage
|
||||
'message' => $sMessage,
|
||||
]);
|
||||
$oPage->SetOutputDataOnly(true);
|
||||
return $oPage;
|
||||
12
sources/Application/Dashboard/DashboardException.php
Normal file
12
sources/Application/Dashboard/DashboardException.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Application\Dashboard;
|
||||
|
||||
class DashboardException extends \Exception
|
||||
{
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
namespace Combodo\iTop\Application\Dashboard\Layout;
|
||||
|
||||
use Combodo\iTop\Application\Dashlet\Service\DashletService;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\Dashboard\DashboardGrid;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\Dashboard\DashboardLayout as DashboardLayoutUIBlock;
|
||||
|
||||
@@ -23,9 +24,9 @@ class DashboardLayoutGrid extends \DashboardLayout
|
||||
$oDashlet = $aPosDashlet['dashlet'];
|
||||
if ($oDashlet::IsVisible()) {
|
||||
$sDashletId = $oDashlet->GetID();
|
||||
$sDashletClass = get_class($oDashlet);
|
||||
$sDashletClass = $oDashlet->GetDashletType();
|
||||
$aDashletDenormalizedProperties = $oDashlet->GetDenormalizedProperties();
|
||||
$aDashletsInfo = $sDashletClass::GetInfo();
|
||||
$aDashletsInfo = DashletService::GetInstance()->GetDashletDefinition($sDashletClass);
|
||||
|
||||
// Also set minimal height/width
|
||||
$iPositionX = $aPosDashlet['position_x'];
|
||||
|
||||
126
sources/Application/Dashlet/Core/DashletBadge.php
Normal file
126
sources/Application/Dashlet/Core/DashletBadge.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2012-2024 Combodo SAS
|
||||
//
|
||||
// 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\Application\Dashlet\Core;
|
||||
|
||||
use Combodo\iTop\Application\Dashlet\Dashlet;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Dashlet\DashletContainer;
|
||||
use DBObjectSearch;
|
||||
use DesignerForm;
|
||||
use DesignerIconSelectionField;
|
||||
use Dict;
|
||||
use DisplayBlock;
|
||||
use utils;
|
||||
|
||||
class DashletBadge extends Dashlet
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->aProperties['class'] = 'Contact';
|
||||
$this->aCSSClasses[] = 'ibo-dashlet--is-inline';
|
||||
$this->aCSSClasses[] = 'ibo-dashlet-badge';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = [])
|
||||
{
|
||||
$oDashletContainer = new DashletContainer($this->sId, ['dashlet-content']);
|
||||
|
||||
$sClass = $this->aProperties['class'];
|
||||
$oFilter = new DBObjectSearch($sClass);
|
||||
$oBlock = new DisplayBlock($oFilter, 'actions');
|
||||
$aExtraParams['context_filter'] = 1;
|
||||
$aExtraParams['withJSRefreshCallBack'] = true;
|
||||
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM)
|
||||
$oBlock->DisplayIntoContentBlock($oDashletContainer, $oPage, $sBlockId, $aExtraParams);
|
||||
|
||||
return $oDashletContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = [])
|
||||
{
|
||||
$oDashletContainer = new DashletContainer($this->sId, ['dashlet-content']);
|
||||
|
||||
$sClass = $this->aProperties['class'];
|
||||
$sIconUrl = utils::HtmlEntities($this->oModelReflection->GetClassIcon($sClass, false));
|
||||
$sClassLabel = $this->oModelReflection->GetName($sClass);
|
||||
$sId = $this->sId;
|
||||
$sClassCreate = Dict::Format('UI:ClickToCreateNew', $sClassLabel);
|
||||
|
||||
$sHtml = <<<HTML
|
||||
<div id="block_fake_$sId" class="display_block">
|
||||
<div class="ibo-dashlet-badge--body" data-role="ibo-dashlet-badge--body" title="$sClassLabel">
|
||||
<div class="ibo-dashlet-badge--icon-container"><img class="ibo-dashlet-badge--icon" src="$sIconUrl"></div>
|
||||
<div class="ibo-dashlet-badge--actions"><a class="ibo-dashlet-badge--action-list" href="#" data-role="ibo-dashlet-badge--action-list"><span class="ibo-dashlet-badge--action-list-count">4</span><span class="ibo-dashlet-badge--action-list-label">$sClassLabel</span></a><a class="ibo-dashlet-badge--action-create" href="#"><span class="ibo-dashlet-badge--action-create-icon fas fa-plus"></span><span class="ibo-dashlet-badge--action-create-label"> $sClassCreate </span></a></div>
|
||||
</div>
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
$oDashletContainer->AddHtml($sHtml);
|
||||
|
||||
return $oDashletContainer;
|
||||
}
|
||||
|
||||
protected static $aClassList = null;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetPropertiesFields(DesignerForm $oForm)
|
||||
{
|
||||
if (is_null(self::$aClassList)) {
|
||||
// Cache the ordered list of classes (ordered on the label)
|
||||
// (has a significant impact when editing a page with lots of badges)
|
||||
//
|
||||
$aClasses = [];
|
||||
foreach ($this->oModelReflection->GetClasses('bizmodel', true /*exclude links*/) as $sClass) {
|
||||
$aClasses[$sClass] = $this->oModelReflection->GetName($sClass);
|
||||
}
|
||||
asort($aClasses);
|
||||
|
||||
self::$aClassList = [];
|
||||
foreach ($aClasses as $sClass => $sLabel) {
|
||||
$sIconUrl = $this->oModelReflection->GetClassIcon($sClass, false);
|
||||
if ($sIconUrl == '') {
|
||||
// The icon does not exist, let's use a transparent one of the same size.
|
||||
$sIconUrl = utils::GetAbsoluteUrlAppRoot().'images/transparent_32_32.png';
|
||||
}
|
||||
self::$aClassList[] = ['value' => $sClass, 'label' => $sLabel, 'icon' => $sIconUrl];
|
||||
}
|
||||
}
|
||||
|
||||
$oField = new DesignerIconSelectionField('class', Dict::S('UI:DashletBadge:Prop-Class'), $this->aProperties['class']);
|
||||
$oField->SetAllowedValues(self::$aClassList);
|
||||
|
||||
$oForm->AddField($oField);
|
||||
}
|
||||
}
|
||||
590
sources/Application/Dashlet/Core/DashletGroupBy.php
Normal file
590
sources/Application/Dashlet/Core/DashletGroupBy.php
Normal file
@@ -0,0 +1,590 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2012-2024 Combodo SAS
|
||||
//
|
||||
// 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\Application\Dashlet\Core;
|
||||
|
||||
use Combodo\iTop\Application\Dashlet\Dashlet;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Dashlet\DashletContainer;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
||||
use DBObjectSearch;
|
||||
use DesignerComboField;
|
||||
use DesignerForm;
|
||||
use DesignerFormSelectorField;
|
||||
use DesignerHiddenField;
|
||||
use DesignerIntegerField;
|
||||
use DesignerLongTextField;
|
||||
use DesignerTextField;
|
||||
use Dict;
|
||||
use DisplayBlock;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
use utils;
|
||||
|
||||
abstract class DashletGroupBy extends Dashlet
|
||||
{
|
||||
public function __construct($oModelReflection, $sId, string $sDashletType = null)
|
||||
{
|
||||
parent::__construct($oModelReflection, $sId, $sDashletType);
|
||||
$this->aProperties['title'] = '';
|
||||
$this->aProperties['query'] = 'SELECT Contact';
|
||||
$this->aProperties['group_by'] = 'status';
|
||||
$this->aProperties['style'] = 'table';
|
||||
$this->aProperties['aggregation_function'] = 'count';
|
||||
$this->aProperties['aggregation_attribute'] = '';
|
||||
$this->aProperties['limit'] = '';
|
||||
$this->aProperties['order_by'] = '';
|
||||
$this->aProperties['order_direction'] = '';
|
||||
}
|
||||
|
||||
protected $sGroupByLabel = null;
|
||||
protected $sGroupByExpr = null;
|
||||
protected $sGroupByAttCode = null;
|
||||
protected $sFunction = null;
|
||||
protected $sAggregationFunction = null;
|
||||
protected $sAggregationAttribute = null;
|
||||
protected $sLimit = null;
|
||||
protected $sOrderBy = null;
|
||||
protected $sOrderDirection = null;
|
||||
protected $sClass = null;
|
||||
|
||||
/**
|
||||
* Compute Grouping
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function OnUpdate()
|
||||
{
|
||||
$this->sGroupByExpr = null;
|
||||
$this->sGroupByLabel = null;
|
||||
$this->sGroupByAttCode = null;
|
||||
$this->sFunction = null;
|
||||
$this->sClass = null;
|
||||
|
||||
$sQuery = $this->aProperties['query'];
|
||||
$sGroupBy = $this->aProperties['group_by'];
|
||||
|
||||
$this->sAggregationFunction = $this->aProperties['aggregation_function'];
|
||||
$this->sAggregationAttribute = $this->aProperties['aggregation_attribute'] ?? '';
|
||||
|
||||
$this->sLimit = $this->aProperties['limit'] ?? 0;
|
||||
$this->sOrderBy = $this->aProperties['order_by'] ?? null;
|
||||
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 {
|
||||
$oQuery = $this->oModelReflection->GetQuery($sQuery);
|
||||
$this->sClass = $oQuery->GetClass();
|
||||
$sClassAlias = $oQuery->GetClassAlias();
|
||||
} catch (Exception $e) {
|
||||
// Invalid query, let the user edit the dashlet/dashboard anyhow
|
||||
$this->sClass = null;
|
||||
$sClassAlias = '';
|
||||
}
|
||||
|
||||
// Check groupby... it can be wrong at this stage
|
||||
if (preg_match('/^(.*):(.*)$/', $sGroupBy, $aMatches)) {
|
||||
$this->sGroupByAttCode = $aMatches[1];
|
||||
$this->sFunction = $aMatches[2];
|
||||
} else {
|
||||
$this->sGroupByAttCode = $sGroupBy;
|
||||
$this->sFunction = null;
|
||||
}
|
||||
|
||||
if ((!is_null($this->sClass)) && 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);
|
||||
if (!is_null($this->sFunction)) {
|
||||
switch ($this->sFunction) {
|
||||
case 'hour':
|
||||
$this->sGroupByLabel = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Hour', $sAttLabel);
|
||||
$this->sGroupByExpr = "DATE_FORMAT($sClassAlias.{$this->sGroupByAttCode}, '%H')"; // 0 -> 23
|
||||
break;
|
||||
|
||||
case 'month':
|
||||
$this->sGroupByLabel = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Month', $sAttLabel);
|
||||
$this->sGroupByExpr = "DATE_FORMAT($sClassAlias.{$this->sGroupByAttCode}, '%Y-%m')"; // yyyy-mm
|
||||
break;
|
||||
|
||||
case 'day_of_week':
|
||||
$this->sGroupByLabel = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:DayOfWeek', $sAttLabel);
|
||||
$this->sGroupByExpr = "DATE_FORMAT($sClassAlias.{$this->sGroupByAttCode}, '%w')";
|
||||
break;
|
||||
|
||||
case 'day_of_month':
|
||||
$this->sGroupByLabel = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:DayOfMonth', $sAttLabel);
|
||||
$this->sGroupByExpr = "DATE_FORMAT($sClassAlias.{$this->sGroupByAttCode}, '%Y-%m-%d')"; // mm-dd
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->sGroupByLabel = 'Unknown group by function '.$this->sFunction;
|
||||
$this->sGroupByExpr = $sClassAlias.'.'.$this->sGroupByAttCode;
|
||||
}
|
||||
} else {
|
||||
$this->sGroupByExpr = $sClassAlias.'.'.$this->sGroupByAttCode;
|
||||
$this->sGroupByLabel = $sAttLabel;
|
||||
}
|
||||
} else {
|
||||
$this->sGroupByAttCode = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \ArchivedObjectException
|
||||
*/
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = [])
|
||||
{
|
||||
$sTitle = $this->aProperties['title'];
|
||||
$sQuery = $this->aProperties['query'];
|
||||
$sStyle = $this->aProperties['style'];
|
||||
|
||||
// First perform the query - if the OQL is not ok, it will generate an exception : no need to go further
|
||||
if (isset($aExtraParams['query_params'])) {
|
||||
$aQueryParams = $aExtraParams['query_params'];
|
||||
} elseif (isset($aExtraParams['this->class']) && isset($aExtraParams['this->id'])) {
|
||||
$oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']);
|
||||
$aQueryParams = $oObj->ToArgsForQuery();
|
||||
} else {
|
||||
$aQueryParams = [];
|
||||
}
|
||||
$oFilter = DBObjectSearch::FromOQL($sQuery, $aQueryParams);
|
||||
$oFilter->SetShowObsoleteData(utils::ShowObsoleteData());
|
||||
|
||||
$sClass = $oFilter->GetClass();
|
||||
if (!$this->oModelReflection->IsValidAttCode($sClass, $this->sGroupByAttCode)) {
|
||||
return new Html('<p>'.Dict::S('UI:DashletGroupBy:MissingGroupBy').'</p>');
|
||||
}
|
||||
|
||||
switch ($sStyle) {
|
||||
case 'bars':
|
||||
$sType = 'chart';
|
||||
$aParams = [
|
||||
'chart_type' => 'bars',
|
||||
'chart_title' => $sTitle,
|
||||
'group_by' => $this->sGroupByExpr,
|
||||
'group_by_label' => $this->sGroupByLabel,
|
||||
'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;
|
||||
|
||||
case 'pie':
|
||||
$sType = 'chart';
|
||||
$aParams = [
|
||||
'chart_type' => 'pie',
|
||||
'chart_title' => $sTitle,
|
||||
'group_by' => $this->sGroupByExpr,
|
||||
'group_by_label' => $this->sGroupByLabel,
|
||||
'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;
|
||||
|
||||
case 'table':
|
||||
default:
|
||||
$sHtmlTitle = utils::HtmlEntities(Dict::S($sTitle)); // done in the itop block
|
||||
$sType = 'count';
|
||||
$aParams = [
|
||||
'group_by' => $this->sGroupByExpr,
|
||||
'group_by_label' => $this->sGroupByLabel,
|
||||
'aggregation_function' => $this->sAggregationFunction,
|
||||
'aggregation_attribute' => $this->sAggregationAttribute,
|
||||
'limit' => $this->sLimit,
|
||||
'order_direction' => $this->sOrderDirection,
|
||||
'order_by' => $this->sOrderBy,
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
//$oPanel = \Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory::MakeStandard();
|
||||
//PanelUIBlockFactory::MakeForClass($sClass, Dict::S($sTitle));
|
||||
|
||||
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM)
|
||||
$oBlock = new DisplayBlock($oFilter, $sType);
|
||||
//$oBlock->DisplayIntoContentBlock($oPanel, $oPage, $sBlockId, array_merge($aExtraParams, $aParams));
|
||||
$aExtraParams["surround_with_panel"] = true;
|
||||
$aExtraParams["panel_title"] = Dict::S($sTitle);
|
||||
$aExtraParams["panel_class"] = $sClass;
|
||||
$oPanel = $oBlock->GetDisplay($oPage, $sBlockId, array_merge($aExtraParams, $aParams));
|
||||
if ($bEditMode) {
|
||||
$oPanel->AddHtml('<div class="ibo-dashlet-blocker dashlet-blocker"></div>');
|
||||
}
|
||||
|
||||
return $oPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function MakeSimulatedData()
|
||||
{
|
||||
$sQuery = $this->aProperties['query'];
|
||||
|
||||
$oQuery = $this->oModelReflection->GetQuery($sQuery);
|
||||
$sClass = $oQuery->GetClass();
|
||||
|
||||
$aDisplayValues = [];
|
||||
if ($this->oModelReflection->IsValidAttCode($sClass, $this->sGroupByAttCode)) {
|
||||
$aAttributeTypes = $this->oModelReflection->ListAttributes($sClass);
|
||||
$sAttributeType = $aAttributeTypes[$this->sGroupByAttCode];
|
||||
if (is_subclass_of($sAttributeType, 'AttributeDateTime') || $sAttributeType == 'AttributeDateTime') {
|
||||
// Note: an alternative to this somewhat hardcoded way of doing things would be to implement...
|
||||
//$oExpr = Expression::FromOQL($this->sGroupByExpr);
|
||||
//$aTranslationData = array($oQuery->GetClassAlias() => array($this->sGroupByAttCode => new ScalarExpression(date('Y-m-d H:i:s', $iTime))));
|
||||
//$sRawValue = CMDBSource::QueryToScalar('SELECT '.$oExpr->Translate($aTranslationData)->Render());
|
||||
//$sValueLabel = $oExpr->MakeValueLabel(oFilter, $sRawValue, $sRawValue);
|
||||
// Anyhow, this requires :
|
||||
// - an update to the prototype of MakeValueLabel() so that it takes ModelReflection parameters
|
||||
// - propose clever date/times samples
|
||||
|
||||
$aValues = [];
|
||||
switch ($this->sFunction) {
|
||||
case 'hour':
|
||||
$aValues = [8, 9, 15, 18];
|
||||
break;
|
||||
|
||||
case 'month':
|
||||
$aValues = ['2013 '.Dict::S('Month-11'), '2013 '.Dict::S('Month-12'), '2014 '.Dict::S('Month-01'), '2014 '.Dict::S('Month-02'), '2014 '.Dict::S('Month-03')];
|
||||
break;
|
||||
|
||||
case 'day_of_week':
|
||||
$aValues = [Dict::S('DayOfWeek-Monday'), Dict::S('DayOfWeek-Wednesday'), Dict::S('DayOfWeek-Thursday'), Dict::S('DayOfWeek-Friday')];
|
||||
break;
|
||||
|
||||
case 'day_of_month':
|
||||
$aValues = [Dict::S('Month-03').' 30', Dict::S('Month-03').' 31', Dict::S('Month-04').' 01', Dict::S('Month-04').' 02', Dict::S('Month-04').' 03'];
|
||||
break;
|
||||
}
|
||||
foreach ($aValues as $sValue) {
|
||||
$aDisplayValues[] = ['label' => $sValue, 'value' => (int)rand(1, 15)];
|
||||
}
|
||||
} elseif (is_subclass_of($sAttributeType, 'AttributeEnum') || $sAttributeType == 'AttributeEnum') {
|
||||
$aAllowed = $this->oModelReflection->GetAllowedValues_att($sClass, $this->sGroupByAttCode);
|
||||
if ($aAllowed) { // null for non enums
|
||||
foreach ($aAllowed as $sValue => $sValueLabel) {
|
||||
$iCount = (int)rand(2, 100);
|
||||
$aDisplayValues[] = [
|
||||
'label' => $sValueLabel,
|
||||
'value' => $iCount,
|
||||
];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$aDisplayValues[] = ['label' => 'a', 'value' => 123];
|
||||
$aDisplayValues[] = ['label' => 'b', 'value' => 321];
|
||||
$aDisplayValues[] = ['label' => 'c', 'value' => 456];
|
||||
}
|
||||
}
|
||||
|
||||
return $aDisplayValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = [])
|
||||
{
|
||||
$oDashletContainer = new DashletContainer(null, ['dashlet-content']);
|
||||
$oDashletContainer->AddHtml('error!');
|
||||
|
||||
return $oDashletContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function GetPropertiesFields(DesignerForm $oForm)
|
||||
{
|
||||
$oField = new DesignerTextField('title', Dict::S('UI:DashletGroupBy:Prop-Title'), $this->aProperties['title']);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletGroupBy:Prop-Query'), $this->aProperties['query']);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-query-oql");
|
||||
$oField->AddCSSClass("ibo-is-code");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
try {
|
||||
// Group by field: build the list of possible values (attribute codes + ...)
|
||||
$aGroupBy = $this->GetGroupByOptions($this->aProperties['query']);
|
||||
|
||||
$oField = new DesignerComboField('group_by', Dict::S('UI:DashletGroupBy:Prop-GroupBy'), $this->aProperties['group_by']);
|
||||
$oField->SetMandatory();
|
||||
$oField->SetAllowedValues($aGroupBy);
|
||||
} catch (Exception $e) {
|
||||
$oField = new DesignerTextField('group_by', Dict::S('UI:DashletGroupBy:Prop-GroupBy'), $this->aProperties['group_by']);
|
||||
$oField->SetReadOnly();
|
||||
$aGroupBy = [];
|
||||
}
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$aStyles = [
|
||||
'pie' => Dict::S('UI:DashletGroupByPie:Label'),
|
||||
'bars' => Dict::S('UI:DashletGroupByBars:Label'),
|
||||
'table' => Dict::S('UI:DashletGroupByTable:Label'),
|
||||
];
|
||||
|
||||
$oField = new DesignerComboField('style', Dict::S('UI:DashletGroupBy:Prop-Style'), $this->aProperties['style']);
|
||||
$oField->SetMandatory();
|
||||
$oField->SetAllowedValues($aStyles);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$aFunctionAttributes = $this->GetNumericAttributes($this->aProperties['query']);
|
||||
$aFunctions = $this->GetAllowedFunctions($aFunctionAttributes);
|
||||
$oSelectorField = new DesignerFormSelectorField('aggregation_function', Dict::S('UI:DashletGroupBy:Prop-Function'), $this->aProperties['aggregation_function']);
|
||||
$oForm->AddField($oSelectorField);
|
||||
$oSelectorField->SetMandatory();
|
||||
// Count sub-menu
|
||||
$oSubForm = new DesignerForm();
|
||||
$oSelectorField->AddSubForm($oSubForm, Dict::S('UI:GroupBy:count'), 'count');
|
||||
foreach ($aFunctions as $sFct => $sLabel) {
|
||||
$oSubForm = new DesignerForm();
|
||||
$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 = [];
|
||||
|
||||
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 = [
|
||||
'asc' => Dict::S('UI:DashletGroupBy:Order:asc'),
|
||||
'desc' => Dict::S('UI:DashletGroupBy:Order:desc'),
|
||||
];
|
||||
$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);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function GetOrderBy()
|
||||
{
|
||||
if (is_null($this->sClass)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
$this->aProperties['group_by'] => $this->oModelReflection->GetLabel($this->sClass, $this->aProperties['group_by']),
|
||||
'_itop_'.$this->aProperties['aggregation_function'].'_' => Dict::S('UI:GroupBy:'.$this->aProperties['aggregation_function']),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aFunctionAttributes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function GetAllowedFunctions($aFunctionAttributes)
|
||||
{
|
||||
$aFunctions = [];
|
||||
|
||||
if (!empty($aFunctionAttributes) || is_null($this->sClass)) {
|
||||
$aFunctions['sum'] = Dict::S('UI:GroupBy:sum');
|
||||
$aFunctions['avg'] = Dict::S('UI:GroupBy:avg');
|
||||
$aFunctions['min'] = Dict::S('UI:GroupBy:min');
|
||||
$aFunctions['max'] = Dict::S('UI:GroupBy:max');
|
||||
}
|
||||
|
||||
return $aFunctions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sOql
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function GetNumericAttributes($sOql)
|
||||
{
|
||||
$aFunctionAttributes = [];
|
||||
try {
|
||||
$oQuery = $this->oModelReflection->GetQuery($sOql);
|
||||
$sClass = $oQuery->GetClass();
|
||||
if (is_null($sClass)) {
|
||||
return $aFunctionAttributes;
|
||||
}
|
||||
foreach ($this->oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType) {
|
||||
switch ($sAttType) {
|
||||
case 'AttributeDecimal':
|
||||
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;
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// In case the OQL is bad
|
||||
}
|
||||
|
||||
return $aFunctionAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function Update($aValues, $aUpdatedFields)
|
||||
{
|
||||
if (in_array('query', $aUpdatedFields)) {
|
||||
try {
|
||||
$sCurrQuery = $aValues['query'];
|
||||
$oCurrSearch = $this->oModelReflection->GetQuery($sCurrQuery);
|
||||
$sCurrClass = $oCurrSearch->GetClass();
|
||||
|
||||
$sPrevQuery = $this->aProperties['query'];
|
||||
$oPrevSearch = $this->oModelReflection->GetQuery($sPrevQuery);
|
||||
$sPrevClass = $oPrevSearch->GetClass();
|
||||
|
||||
if ($sCurrClass != $sPrevClass) {
|
||||
$this->bFormRedrawNeeded = true;
|
||||
// wrong but not necessary - unset($aUpdatedFields['group_by']);
|
||||
$this->aProperties['group_by'] = '';
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->bFormRedrawNeeded = true;
|
||||
}
|
||||
}
|
||||
$oDashlet = parent::Update($aValues, $aUpdatedFields);
|
||||
|
||||
if (in_array('style', $aUpdatedFields)) {
|
||||
switch ($aValues['style']) {
|
||||
// Style changed, mutate to the specified type of chart
|
||||
case 'pie':
|
||||
$oDashlet = new DashletGroupByPie($this->oModelReflection, $this->sId);
|
||||
break;
|
||||
|
||||
case 'bars':
|
||||
$oDashlet = new DashletGroupByBars($this->oModelReflection, $this->sId);
|
||||
break;
|
||||
|
||||
case 'table':
|
||||
$oDashlet = new DashletGroupByTable($this->oModelReflection, $this->sId);
|
||||
break;
|
||||
}
|
||||
$oDashlet->FromParams($aValues);
|
||||
$oDashlet->bRedrawNeeded = true;
|
||||
$oDashlet->bFormRedrawNeeded = true;
|
||||
}
|
||||
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('group_by', $aUpdatedFields) || in_array('aggregation_function', $aUpdatedFields)) {
|
||||
$oDashlet->bRedrawNeeded = true;
|
||||
$oDashlet->bFormRedrawNeeded = true;
|
||||
}
|
||||
|
||||
return $oDashlet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static function CanCreateFromOQL()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function GetPropertiesFieldsFromOQL(DesignerForm $oForm, $sOQL = null)
|
||||
{
|
||||
$oField = new DesignerTextField('title', Dict::S('UI:DashletGroupBy:Prop-Title'), '');
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerHiddenField('query', Dict::S('UI:DashletGroupBy:Prop-Query'), $sOQL);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-query-oql");
|
||||
$oField->AddCSSClass("ibo-is-code");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
if (!is_null($sOQL)) {
|
||||
$oField = new DesignerComboField('group_by', Dict::S('UI:DashletGroupBy:Prop-GroupBy'), null);
|
||||
$aGroupBy = $this->GetGroupByOptions($sOQL);
|
||||
$oField->SetAllowedValues($aGroupBy);
|
||||
} else {
|
||||
// Creating a form for reading parameters!
|
||||
$oField = new DesignerTextField('group_by', Dict::S('UI:DashletGroupBy:Prop-GroupBy'), null);
|
||||
}
|
||||
$oField->SetMandatory();
|
||||
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerHiddenField('style', '', $this->aProperties['style']);
|
||||
$oField->SetMandatory();
|
||||
$oForm->AddField($oField);
|
||||
}
|
||||
}
|
||||
112
sources/Application/Dashlet/Core/DashletGroupByBars.php
Normal file
112
sources/Application/Dashlet/Core/DashletGroupByBars.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2012-2024 Combodo SAS
|
||||
//
|
||||
// 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\Application\Dashlet\Core;
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\Dashlet\DashletContainer;
|
||||
use Dict;
|
||||
use utils;
|
||||
|
||||
class DashletGroupByBars extends DashletGroupBy
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->aProperties['style'] = 'bars';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = [])
|
||||
{
|
||||
$oDashletContainer = new DashletContainer(null, ['dashlet-content']);
|
||||
|
||||
$sTitle = $this->aProperties['title'];
|
||||
|
||||
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
|
||||
|
||||
$HTMLsTitle = ($sTitle != '') ? '<h1 style="text-align:center">'.utils::HtmlEntities($sTitle).'</h1>' : '';
|
||||
$oDashletContainer->AddHtml("<div style=\"background-color:#fff;padding:0.25em;\">$HTMLsTitle<div id=\"$sBlockId\" style=\"background-color:#fff;\"></div></div>");
|
||||
|
||||
$aDisplayValues = $this->MakeSimulatedData();
|
||||
|
||||
$aNames = [];
|
||||
foreach ($aDisplayValues as $idx => $aValue) {
|
||||
$aNames[$idx] = $aValue['label'];
|
||||
}
|
||||
$sJSNames = json_encode($aNames);
|
||||
|
||||
$sJson = json_encode($aDisplayValues);
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
window.setTimeout(function() {
|
||||
var chart = c3.generate({
|
||||
bindto: '#{$sBlockId}',
|
||||
data: {
|
||||
json: $sJson,
|
||||
keys: {
|
||||
x: 'label',
|
||||
value: ["value"]
|
||||
},
|
||||
selection: {
|
||||
enabled: true
|
||||
},
|
||||
type: 'bar'
|
||||
},
|
||||
axis: {
|
||||
x: {
|
||||
tick: {
|
||||
culling: {max: 25}, // Maximum 24 labels on x axis (2 years).
|
||||
centered: true,
|
||||
rotate: 90,
|
||||
multiline: false
|
||||
},
|
||||
type: 'category' // this needed to load string x value
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
y: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
tooltip: {
|
||||
grouped: false,
|
||||
format: {
|
||||
title: function() { return '' },
|
||||
name: function (name, ratio, id, index) {
|
||||
var aNames = $sJSNames;
|
||||
return aNames[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
EOF
|
||||
);
|
||||
|
||||
return $oDashletContainer;
|
||||
}
|
||||
}
|
||||
109
sources/Application/Dashlet/Core/DashletGroupByPie.php
Normal file
109
sources/Application/Dashlet/Core/DashletGroupByPie.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2012-2024 Combodo SAS
|
||||
//
|
||||
// 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\Application\Dashlet\Core;
|
||||
|
||||
use Combodo\iTop\Application\Helper\WebResourcesHelper;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Dashlet\DashletContainer;
|
||||
use Dict;
|
||||
use utils;
|
||||
|
||||
class DashletGroupByPie extends DashletGroupBy
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->aProperties['style'] = 'pie';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetJSFilesRelPaths(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::GetJSFilesRelPaths(),
|
||||
WebResourcesHelper::GetJSFilesRelPathsForC3JS()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetCSSFilesRelPaths(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::GetCSSFilesRelPaths(),
|
||||
WebResourcesHelper::GetCSSFilesRelPathsForC3JS()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = [])
|
||||
{
|
||||
$oDashletContainer = new DashletContainer(null, ['dashlet-content']);
|
||||
|
||||
$sTitle = $this->aProperties['title'];
|
||||
|
||||
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
|
||||
|
||||
$HTMLsTitle = ($sTitle != '') ? '<h1 style="text-align:center">'.utils::HtmlEntities($sTitle).'</h1>' : '';
|
||||
$oDashletContainer->AddHtml("<div style=\"background-color:#fff;padding:0.25em;\">$HTMLsTitle<div id=\"$sBlockId\" style=\"background-color:#fff;\"></div></div>");
|
||||
|
||||
$aDisplayValues = $this->MakeSimulatedData();
|
||||
|
||||
$aColumns = [];
|
||||
$aNames = [];
|
||||
foreach ($aDisplayValues as $idx => $aValue) {
|
||||
$aColumns[] = ['series_'.$idx, (int)$aValue['value']];
|
||||
$aNames['series_'.$idx] = $aValue['label'];
|
||||
}
|
||||
$sJSColumns = json_encode($aColumns);
|
||||
$sJSNames = json_encode($aNames);
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
window.setTimeout(function() {
|
||||
var chart = c3.generate({
|
||||
bindto: '#{$sBlockId}',
|
||||
data: {
|
||||
columns: $sJSColumns,
|
||||
type: 'pie',
|
||||
names: $sJSNames,
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
position: 'right',
|
||||
},
|
||||
tooltip: {
|
||||
format: {
|
||||
value: function (value, ratio, id) { return value; }
|
||||
}
|
||||
}
|
||||
});}, 100);
|
||||
EOF
|
||||
);
|
||||
|
||||
return $oDashletContainer;
|
||||
}
|
||||
}
|
||||
79
sources/Application/Dashlet/Core/DashletGroupByTable.php
Normal file
79
sources/Application/Dashlet/Core/DashletGroupByTable.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2012-2024 Combodo SAS
|
||||
//
|
||||
// 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\Application\Dashlet\Core;
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\Dashlet\DashletContainer;
|
||||
use Dict;
|
||||
|
||||
class DashletGroupByTable extends DashletGroupBy
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->aProperties['style'] = 'table';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = [])
|
||||
{
|
||||
$oDashletContainer = new DashletContainer();
|
||||
|
||||
$aDisplayValues = $this->MakeSimulatedData();
|
||||
$iTotal = 0;
|
||||
foreach ($aDisplayValues as $iRow => $aDisplayData) {
|
||||
$iTotal += $aDisplayData['value'];
|
||||
}
|
||||
|
||||
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
|
||||
|
||||
$sHtml = '';
|
||||
$sHtml .= '<div id="'.$sBlockId.'" class="display_block">';
|
||||
$sHtml .= '<div class="dashlet-content">';
|
||||
$sHtml .= '<p>'.Dict::Format('UI:Pagination:HeaderNoSelection', $iTotal).'</p>';
|
||||
$sHtml .= '<table class="listResults">';
|
||||
$sHtml .= '<thead>';
|
||||
$sHtml .= '<tr>';
|
||||
$sHtml .= '<th class="header" title="">'.$this->sGroupByLabel.'</th>';
|
||||
$sHtml .= '<th class="header" title="'.Dict::S('UI:GroupBy:Count+').'">'.Dict::S('UI:GroupBy:Count').'</th>';
|
||||
$sHtml .= '</tr>';
|
||||
$sHtml .= '</thead>';
|
||||
$sHtml .= '<tbody>';
|
||||
foreach ($aDisplayValues as $aDisplayData) {
|
||||
$sHtml .= '<tr class="even">';
|
||||
$sHtml .= '<td class=""><span title="Active">'.$aDisplayData['label'].'</span></td>';
|
||||
$sHtml .= '<td class=""><a>'.$aDisplayData['value'].'</a></td>';
|
||||
$sHtml .= '</tr>';
|
||||
}
|
||||
$sHtml .= '</tbody>';
|
||||
$sHtml .= '</table>';
|
||||
$sHtml .= '</div>';
|
||||
|
||||
$sHtml .= '</div>';
|
||||
|
||||
$oDashletContainer->AddHtml($sHtml);
|
||||
|
||||
return $oDashletContainer;
|
||||
}
|
||||
}
|
||||
326
sources/Application/Dashlet/Core/DashletHeaderDynamic.php
Normal file
326
sources/Application/Dashlet/Core/DashletHeaderDynamic.php
Normal file
@@ -0,0 +1,326 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2012-2024 Combodo SAS
|
||||
//
|
||||
// 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\Application\Dashlet\Core;
|
||||
|
||||
use ApplicationContext;
|
||||
use Combodo\iTop\Application\Dashlet\Dashlet;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Dashlet\DashletContainer;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
|
||||
use DBObjectSearch;
|
||||
use DBObjectSet;
|
||||
use DesignerComboField;
|
||||
use DesignerForm;
|
||||
use DesignerLongTextField;
|
||||
use DesignerTextField;
|
||||
use Dict;
|
||||
use DisplayBlock;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
use UnknownClassOqlException;
|
||||
use utils;
|
||||
|
||||
class DashletHeaderDynamic extends Dashlet
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->aProperties['title'] = Dict::S('UI:DashletHeaderDynamic:Prop-Title:Default');
|
||||
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
|
||||
$this->aProperties['icon'] = $oIconSelect->GetDefaultValue('Contact');
|
||||
$this->aProperties['subtitle'] = Dict::S('UI:DashletHeaderDynamic:Prop-Subtitle:Default');
|
||||
$this->aProperties['query'] = 'SELECT Contact';
|
||||
$this->aProperties['group_by'] = 'status';
|
||||
$this->aProperties['values'] = ['active', 'inactive'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function GetValues()
|
||||
{
|
||||
$sQuery = $this->aProperties['query'];
|
||||
$sGroupBy = $this->aProperties['group_by'];
|
||||
$aValues = $this->aProperties['values'];
|
||||
|
||||
if (empty($aValues)) {
|
||||
$aValues = [];
|
||||
}
|
||||
|
||||
$oQuery = $this->oModelReflection->GetQuery($sQuery);
|
||||
$sClass = $oQuery->GetClass();
|
||||
|
||||
if ($this->oModelReflection->IsValidAttCode($sClass, $sGroupBy)) {
|
||||
if (count($aValues) == 0) {
|
||||
$aAllowed = $this->oModelReflection->GetAllowedValues_att($sClass, $sGroupBy);
|
||||
if (is_array($aAllowed)) {
|
||||
$aValues = array_keys($aAllowed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \ArchivedObjectException
|
||||
*/
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = [])
|
||||
{
|
||||
$sTitle = utils::HtmlEntities($this->aProperties['title']);
|
||||
$sIcon = $this->aProperties['icon'];
|
||||
$sSubtitle = utils::HtmlEntities($this->aProperties['subtitle']);
|
||||
$sQuery = $this->aProperties['query'];
|
||||
$sGroupBy = $this->aProperties['group_by'];
|
||||
|
||||
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
|
||||
$sIconPath = '';
|
||||
if (Utils::IsNotNullOrEmptyString($sIcon)) {
|
||||
$sIconPath = $oIconSelect->MakeFileUrl($sIcon);
|
||||
}
|
||||
|
||||
$aValues = $this->GetValues();
|
||||
if (count($aValues) > 0) {
|
||||
// Stats grouped by <group_by>
|
||||
$sCSV = implode(',', $aValues);
|
||||
$aParams = [
|
||||
'title[block]' => $sTitle,
|
||||
'label[block]' => $sSubtitle,
|
||||
'status[block]' => $sGroupBy,
|
||||
'status_codes[block]' => $sCSV,
|
||||
'context_filter' => 1,
|
||||
];
|
||||
} else {
|
||||
// Simple stats
|
||||
$aParams = [
|
||||
'title[block]' => $sTitle,
|
||||
'label[block]' => $sSubtitle,
|
||||
'context_filter' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
if (isset($aExtraParams['query_params'])) {
|
||||
$aQueryParams = $aExtraParams['query_params'];
|
||||
} elseif (isset($aExtraParams['this->class'])) {
|
||||
$oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']);
|
||||
$aQueryParams = $oObj->ToArgsForQuery();
|
||||
} else {
|
||||
$aQueryParams = [];
|
||||
}
|
||||
$oFilter = DBObjectSearch::FromOQL($sQuery, $aQueryParams);
|
||||
$oFilter->SetShowObsoleteData(utils::ShowObsoleteData());
|
||||
$sClass = $oFilter->GetClass();
|
||||
|
||||
$oPanel = PanelUIBlockFactory::MakeNeutral(Dict::S(str_replace('_', ':', $sTitle)))
|
||||
->SetIcon($sIconPath)
|
||||
->SetColorFromClass($sClass);
|
||||
$oBlock = new DisplayBlock($oFilter, 'summary');
|
||||
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
|
||||
$oBlock->DisplayIntoContentBlock($oPanel, $oPage, $sBlockId, array_merge($aExtraParams, $aParams));
|
||||
|
||||
$oSubTitle = $oPanel->GetSubTitleBlock();
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
$iCount = $oSet->Count();
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search'.$oAppContext->GetForLink(true).'&filter='.rawurlencode($oFilter->serialize());
|
||||
$oSubTitle->AddHtml('<a class="summary" href="'.$sHyperlink.'">'.Dict::Format(str_replace('_', ':', $sSubtitle), $iCount).'</a>');
|
||||
|
||||
return $oPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = [])
|
||||
{
|
||||
$sTitle = utils::HtmlEntities($this->aProperties['title']);
|
||||
$sIcon = $this->aProperties['icon'];
|
||||
$sSubtitle = utils::HtmlEntities($this->aProperties['subtitle']);
|
||||
$sQuery = $this->aProperties['query'];
|
||||
$sGroupBy = $this->aProperties['group_by'];
|
||||
|
||||
$aValueLabels = [];
|
||||
$aValues = [];
|
||||
try {
|
||||
$oQuery = $this->oModelReflection->GetQuery($sQuery);
|
||||
$sClass = $oQuery->GetClass();
|
||||
$aValues = $this->GetValues();
|
||||
foreach ($aValues as $sValue) {
|
||||
$aValueLabels[] = $this->oModelReflection->GetValueLabel($sClass, $sGroupBy, $sValue);
|
||||
}
|
||||
} catch (UnknownClassOqlException $e) {
|
||||
$aValueLabels[] = $e->GetUserFriendlyDescription();
|
||||
$aValues[] = 1;
|
||||
}
|
||||
|
||||
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
|
||||
$sIconPath = utils::HtmlEntities($oIconSelect->MakeFileUrl($sIcon));
|
||||
|
||||
$oDashletContainer = new DashletContainer(null, ['dashlet-content']);
|
||||
|
||||
$sHtml = '';
|
||||
$sHtml .= '<img src="'.$sIconPath.'">';
|
||||
|
||||
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
|
||||
|
||||
$iTotal = 0;
|
||||
|
||||
$sHtml .= '<div class="display_block" id="'.$sBlockId.'">';
|
||||
$sHtml .= '<div class="summary-details">';
|
||||
$sHtml .= '<table><tbody>';
|
||||
$sHtml .= '<tr>';
|
||||
foreach ($aValueLabels as $sValueLabel) {
|
||||
$sHtml .= ' <th>'.$sValueLabel.'</th>';
|
||||
}
|
||||
$sHtml .= '</tr>';
|
||||
$sHtml .= '<tr>';
|
||||
foreach ($aValues as $sValue) {
|
||||
$iCount = rand(2, 100);
|
||||
$iTotal += $iCount;
|
||||
$sHtml .= ' <td>'.$iCount.'</td>';
|
||||
}
|
||||
$sHtml .= '</tr>';
|
||||
$sHtml .= '</tbody></table>';
|
||||
$sHtml .= '</div>';
|
||||
|
||||
$sTitle = $this->oModelReflection->DictString($sTitle);
|
||||
$sSubtitle = $this->oModelReflection->DictFormat($sSubtitle, $iTotal);
|
||||
|
||||
$sHtml .= '<h1>'.utils::HtmlEntities($sTitle).'</h1>';
|
||||
$sHtml .= '<a class="summary">'.utils::HtmlEntities($sSubtitle).'</a>';
|
||||
$sHtml .= '</div>';
|
||||
|
||||
$oDashletContainer->AddHtml($sHtml);
|
||||
|
||||
return $oDashletContainer;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function GetPropertiesFields(DesignerForm $oForm)
|
||||
{
|
||||
$oField = new DesignerTextField('title', Dict::S('UI:DashletHeaderDynamic:Prop-Title'), $this->aProperties['title']);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = $this->oModelReflection->GetIconSelectionField('icon', Dict::S('UI:DashletHeaderDynamic:Prop-Icon'), $this->aProperties['icon']);
|
||||
$oField->AddAllowedValue(['value' => '', 'label' => Dict::S('UI:DashletIcon:None'), 'icon' => '']);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerTextField('subtitle', Dict::S('UI:DashletHeaderDynamic:Prop-Subtitle'), $this->aProperties['subtitle']);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletHeaderDynamic:Prop-Query'), $this->aProperties['query']);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-query-oql");
|
||||
$oField->AddCSSClass("ibo-is-code");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
try {
|
||||
// Group by field: build the list of possible values (attribute codes + ...)
|
||||
$oQuery = $this->oModelReflection->GetQuery($this->aProperties['query']);
|
||||
$sClass = $oQuery->GetClass();
|
||||
$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);
|
||||
} catch (Exception $e) {
|
||||
$oField = new DesignerTextField('group_by', Dict::S('UI:DashletHeaderDynamic:Prop-GroupBy'), $this->aProperties['group_by']);
|
||||
$oField->SetReadOnly();
|
||||
}
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerComboField('values', Dict::S('UI:DashletHeaderDynamic:Prop-Values'), $this->aProperties['values']);
|
||||
$oField->MultipleSelection(true);
|
||||
if (isset($sClass) && $this->oModelReflection->IsValidAttCode($sClass, $this->aProperties['group_by'])) {
|
||||
$aValues = $this->oModelReflection->GetAllowedValues_att($sClass, $this->aProperties['group_by']);
|
||||
$oField->SetAllowedValues($aValues);
|
||||
} else {
|
||||
$oField->SetReadOnly();
|
||||
}
|
||||
$oForm->AddField($oField);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function Update($aValues, $aUpdatedFields)
|
||||
{
|
||||
if (in_array('query', $aUpdatedFields)) {
|
||||
try {
|
||||
$sCurrQuery = $aValues['query'];
|
||||
$oCurrSearch = $this->oModelReflection->GetQuery($sCurrQuery);
|
||||
$sCurrClass = $oCurrSearch->GetClass();
|
||||
|
||||
$sPrevQuery = $this->aProperties['query'];
|
||||
$oPrevSearch = $this->oModelReflection->GetQuery($sPrevQuery);
|
||||
$sPrevClass = $oPrevSearch->GetClass();
|
||||
|
||||
if ($sCurrClass != $sPrevClass) {
|
||||
$this->bFormRedrawNeeded = true;
|
||||
// wrong but not necessary - unset($aUpdatedFields['group_by']);
|
||||
$this->aProperties['group_by'] = '';
|
||||
$this->aProperties['values'] = [];
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->bFormRedrawNeeded = true;
|
||||
}
|
||||
}
|
||||
if (in_array('group_by', $aUpdatedFields)) {
|
||||
$this->bFormRedrawNeeded = true;
|
||||
$this->aProperties['values'] = [];
|
||||
}
|
||||
|
||||
return parent::Update($aValues, $aUpdatedFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function PropertyFromDOMNode($oDOMNode, $sProperty)
|
||||
{
|
||||
if ($sProperty == 'icon') {
|
||||
$oIconField = $this->oModelReflection->GetIconSelectionField('icon');
|
||||
|
||||
return $oIconField->ValueFromDOMNode($oDOMNode);
|
||||
} else {
|
||||
return parent::PropertyFromDOMNode($oDOMNode, $sProperty);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function PropertyToDOMNode($oDOMNode, $sProperty, $value)
|
||||
{
|
||||
if ($sProperty == 'icon') {
|
||||
$oIconField = $this->oModelReflection->GetIconSelectionField('icon');
|
||||
$oIconField->ValueToDOMNode($oDOMNode, $value);
|
||||
} else {
|
||||
parent::PropertyToDOMNode($oDOMNode, $sProperty, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
98
sources/Application/Dashlet/Core/DashletHeaderStatic.php
Normal file
98
sources/Application/Dashlet/Core/DashletHeaderStatic.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2012-2024 Combodo SAS
|
||||
//
|
||||
// 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\Application\Dashlet\Core;
|
||||
|
||||
use Combodo\iTop\Application\Dashlet\Dashlet;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Dashlet\DashletFactory;
|
||||
use DesignerForm;
|
||||
use DesignerTextField;
|
||||
use Dict;
|
||||
use utils;
|
||||
|
||||
class DashletHeaderStatic extends Dashlet
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->aProperties['title'] = Dict::S('UI:DashletHeaderStatic:Prop-Title:Default');
|
||||
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
|
||||
$this->aProperties['icon'] = $oIconSelect->GetDefaultValue('Contact');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = [])
|
||||
{
|
||||
$sTitle = $this->aProperties['title'];
|
||||
$sIcon = $this->aProperties['icon'];
|
||||
|
||||
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
|
||||
$sIconPath = '';
|
||||
if (utils::IsNotNullOrEmptyString($sIcon)) {
|
||||
$sIconPath = utils::HtmlEntities($oIconSelect->MakeFileUrl($sIcon));
|
||||
}
|
||||
|
||||
return DashletFactory::MakeForDashletHeaderStatic($this->oModelReflection->DictString($sTitle), $sIconPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function GetPropertiesFields(DesignerForm $oForm)
|
||||
{
|
||||
$oField = new DesignerTextField('title', Dict::S('UI:DashletHeaderStatic:Prop-Title'), $this->aProperties['title']);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = $this->oModelReflection->GetIconSelectionField('icon', Dict::S('UI:DashletHeaderStatic:Prop-Icon'), $this->aProperties['icon']);
|
||||
$oField->AddAllowedValue(['value' => '', 'label' => Dict::S('UI:DashletIcon:None'), 'icon' => '']);
|
||||
$oForm->AddField($oField);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function PropertyFromDOMNode($oDOMNode, $sProperty)
|
||||
{
|
||||
if ($sProperty == 'icon') {
|
||||
$oIconField = $this->oModelReflection->GetIconSelectionField('icon');
|
||||
|
||||
return $oIconField->ValueFromDOMNode($oDOMNode);
|
||||
} else {
|
||||
return parent::PropertyFromDOMNode($oDOMNode, $sProperty);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function PropertyToDOMNode($oDOMNode, $sProperty, $value)
|
||||
{
|
||||
if ($sProperty == 'icon') {
|
||||
$oIconField = $this->oModelReflection->GetIconSelectionField('icon');
|
||||
$oIconField->ValueToDOMNode($oDOMNode, $value);
|
||||
} else {
|
||||
parent::PropertyToDOMNode($oDOMNode, $sProperty, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
176
sources/Application/Dashlet/Core/DashletObjectList.php
Normal file
176
sources/Application/Dashlet/Core/DashletObjectList.php
Normal file
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2012-2024 Combodo SAS
|
||||
//
|
||||
// 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\Application\Dashlet\Core;
|
||||
|
||||
use Combodo\iTop\Application\Dashlet\Dashlet;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Dashlet\DashletContainer;
|
||||
use DBObjectSearch;
|
||||
use DesignerBooleanField;
|
||||
use DesignerForm;
|
||||
use DesignerHiddenField;
|
||||
use DesignerLongTextField;
|
||||
use DesignerTextField;
|
||||
use Dict;
|
||||
use DisplayBlock;
|
||||
use MetaModel;
|
||||
use utils;
|
||||
|
||||
class DashletObjectList extends Dashlet
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->aProperties['title'] = '';
|
||||
$this->aProperties['query'] = 'SELECT Contact';
|
||||
$this->aProperties['menu'] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @throws \OQLException
|
||||
* @throws \CoreException
|
||||
* @throws \ArchivedObjectException
|
||||
*/
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = [])
|
||||
{
|
||||
$sTitle = $this->aProperties['title'];
|
||||
$sShowMenu = $this->aProperties['menu'] ? '1' : '0';
|
||||
$oFilter = $this->GetDBSearch($aExtraParams);
|
||||
$sClass = $oFilter->GetClass();
|
||||
//$oPanel = PanelUIBlockFactory::MakeForClass($sClass, Dict::S($sTitle))
|
||||
// ->AddCSSClass('ibo-datatable-panel');
|
||||
|
||||
$oBlock = new DisplayBlock($oFilter, 'list');
|
||||
$aParams = [
|
||||
'menu' => $sShowMenu,
|
||||
'table_id' => self::APPUSERPREFERENCES_PREFIX.$this->sId,
|
||||
'surround_with_panel' => true,
|
||||
'max_height' => '500px',
|
||||
"panel_title" => Dict::S($sTitle),
|
||||
"panel_class" => $sClass,
|
||||
];
|
||||
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM)
|
||||
//$oBlock->DisplayIntoContentBlock($oPanel, $oPage, $sBlockId, array_merge($aExtraParams, $aParams));
|
||||
|
||||
$oPanel = $oBlock->GetDisplay($oPage, $sBlockId, array_merge($aExtraParams, $aParams));
|
||||
|
||||
return $oPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = [])
|
||||
{
|
||||
$oDashletContainer = new DashletContainer($this->sId, ['dashlet-content']);
|
||||
$sTitle = $this->aProperties['title'];
|
||||
$sQuery = $this->aProperties['query'];
|
||||
$bShowMenu = $this->aProperties['menu'];
|
||||
$sHtmlTitle = utils::HtmlEntities($this->oModelReflection->DictString($sTitle));
|
||||
if ($sHtmlTitle != '') {
|
||||
$sHtmlTitle = '<h1>'.$sHtmlTitle.'</h1>';
|
||||
}
|
||||
$oQuery = $this->oModelReflection->GetQuery($sQuery);
|
||||
$sClass = $oQuery->GetClass();
|
||||
$sId = $this->sId;
|
||||
$sMessage = Dict::S('UI:NoObjectToDisplay');
|
||||
$sMenu = '';
|
||||
if ($bShowMenu) {
|
||||
$sMenu = '<p><a>'.Dict::Format('UI:ClickToCreateNew', $this->oModelReflection->GetName($sClass)).'</a></p>';
|
||||
}
|
||||
|
||||
$sHtml = <<<HTML
|
||||
<div class="dashlet-content">
|
||||
<h1>$sHtmlTitle</h1>
|
||||
<div id="block_fake_$sId" class="display_block">
|
||||
<p>$sMessage</p>
|
||||
$sMenu
|
||||
</div>
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
$oDashletContainer->AddHtml($sHtml);
|
||||
|
||||
return $oDashletContainer;
|
||||
}
|
||||
|
||||
public function GetDBSearch($aExtraParams = [])
|
||||
{
|
||||
$sQuery = $this->aProperties['query'];
|
||||
if (isset($aExtraParams['query_params'])) {
|
||||
$aQueryParams = $aExtraParams['query_params'];
|
||||
} elseif (isset($aExtraParams['this->class']) && isset($aExtraParams['this->id'])) {
|
||||
$oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']);
|
||||
$aQueryParams = $oObj->ToArgsForQuery();
|
||||
} else {
|
||||
$aQueryParams = [];
|
||||
}
|
||||
|
||||
return DBObjectSearch::FromOQL($sQuery, $aQueryParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function GetPropertiesFields(DesignerForm $oForm)
|
||||
{
|
||||
$oField = new DesignerTextField('title', Dict::S('UI:DashletObjectList:Prop-Title'), $this->aProperties['title']);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletObjectList:Prop-Query'), $this->aProperties['query']);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-query-oql");
|
||||
$oField->AddCSSClass("ibo-is-code");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerBooleanField('menu', Dict::S('UI:DashletObjectList:Prop-Menu'), $this->aProperties['menu']);
|
||||
$oForm->AddField($oField);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static function CanCreateFromOQL()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function GetPropertiesFieldsFromOQL(DesignerForm $oForm, $sOQL = null)
|
||||
{
|
||||
$oField = new DesignerTextField('title', Dict::S('UI:DashletObjectList:Prop-Title'), '');
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerHiddenField('query', Dict::S('UI:DashletObjectList:Prop-Query'), $sOQL);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-query-oql");
|
||||
$oField->AddCSSClass("ibo-is-code");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerBooleanField('menu', Dict::S('UI:DashletObjectList:Prop-Menu'), $this->aProperties['menu']);
|
||||
$oForm->AddField($oField);
|
||||
}
|
||||
}
|
||||
63
sources/Application/Dashlet/Core/DashletPlainText.php
Normal file
63
sources/Application/Dashlet/Core/DashletPlainText.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2012-2024 Combodo SAS
|
||||
//
|
||||
// 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\Application\Dashlet\Core;
|
||||
|
||||
use Combodo\iTop\Application\Dashlet\Dashlet;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Dashlet\DashletFactory;
|
||||
use DesignerForm;
|
||||
use DesignerLongTextField;
|
||||
use Dict;
|
||||
use utils;
|
||||
|
||||
class DashletPlainText extends Dashlet
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->aProperties['text'] = Dict::S('UI:DashletPlainText:Prop-Text:Default');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = [])
|
||||
{
|
||||
$sText = $this->aProperties['text'];
|
||||
$sText = utils::EscapeHtml(Dict::S($sText));
|
||||
$sText = str_replace(["\r\n", "\n", "\r"], "<br/>", $sText);
|
||||
|
||||
$sId = 'plaintext_'.($bEditMode ? 'edit_' : '').$this->sId;
|
||||
|
||||
return DashletFactory::MakeForDashletPlainText($sText, $sId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function GetPropertiesFields(DesignerForm $oForm)
|
||||
{
|
||||
$oField = new DesignerLongTextField('text', Dict::S('UI:DashletPlainText:Prop-Text'), $this->aProperties['text']);
|
||||
$oField->SetMandatory();
|
||||
$oForm->AddField($oField);
|
||||
}
|
||||
}
|
||||
75
sources/Application/Dashlet/Core/DashletProxy.php
Normal file
75
sources/Application/Dashlet/Core/DashletProxy.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2012-2024 Combodo SAS
|
||||
//
|
||||
// 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\Application\Dashlet\Core;
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\Dashlet\DashletContainer;
|
||||
use Dict;
|
||||
use utils;
|
||||
|
||||
class DashletProxy extends DashletUnknown
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
|
||||
// Remove DashletUnknown class
|
||||
if (($key = array_search('dashlet-unknown', $this->aCSSClasses)) !== false) {
|
||||
unset($this->aCSSClasses[$key]);
|
||||
}
|
||||
|
||||
$this->aCSSClasses[] = 'dashlet-proxy';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = [])
|
||||
{
|
||||
// This should never be called.
|
||||
$oDashletContainer = new DashletContainer(null, ['dashlet-content']);
|
||||
$oDashletContainer->AddHtml('<div>This dashlet is not supposed to be rendered as it is just a proxy for third-party widgets.</div>');
|
||||
|
||||
return $oDashletContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = [])
|
||||
{
|
||||
$sIconUrl = utils::HtmlEntities(utils::GetAbsoluteUrlAppRoot().'images/dashlet-proxy.png');
|
||||
$sExplainText = Dict::Format('UI:DashletProxy:RenderNoDataText:Edit', $this->GetDashletType());
|
||||
|
||||
$oDashletContainer = new DashletContainer(null, ['dashlet-content']);
|
||||
|
||||
$sHtml = '';
|
||||
$sHtml .= '<div class="dashlet-pxy-image"><img src="'.$sIconUrl.'" /></div>';
|
||||
$sHtml .= '<div class="dashlet-pxy-text">'.$sExplainText.'</div>';
|
||||
|
||||
$oDashletContainer->AddHtml($sHtml);
|
||||
|
||||
return $oDashletContainer;
|
||||
}
|
||||
}
|
||||
227
sources/Application/Dashlet/Core/DashletUnknown.php
Normal file
227
sources/Application/Dashlet/Core/DashletUnknown.php
Normal file
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2012-2024 Combodo SAS
|
||||
//
|
||||
// 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\Application\Dashlet\Core;
|
||||
|
||||
use Combodo\iTop\Application\Dashlet\Dashlet;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Dashlet\DashletContainer;
|
||||
use DesignerForm;
|
||||
use DesignerXMLField;
|
||||
use Dict;
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use DOMFormatException;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Class DashletUnknown
|
||||
*
|
||||
* Used as a fallback in iTop for unknown dashlet classes.
|
||||
*
|
||||
* @since 2.5.0
|
||||
*/
|
||||
class DashletUnknown extends Dashlet
|
||||
{
|
||||
protected static $aClassList = null;
|
||||
|
||||
protected $sOriginalDashletXML;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->sOriginalDashletXML = '';
|
||||
$this->aCSSClasses[] = 'dashlet-unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function FromDOMNode($oDOMNode)
|
||||
{
|
||||
// Parent won't do anything as there is no property declared
|
||||
parent::FromDOMNode($oDOMNode);
|
||||
|
||||
// Build properties from XML
|
||||
$this->sOriginalDashletXML = "";
|
||||
foreach ($oDOMNode->childNodes as $oDOMChildNode) {
|
||||
if ($oDOMChildNode instanceof DOMElement) {
|
||||
$sProperty = $oDOMChildNode->tagName;
|
||||
|
||||
// For all properties but "rank" as it is handle by the dashboard.
|
||||
if ($sProperty !== 'rank') {
|
||||
// We need to initialize the property before setting it, otherwise it will guessed as NULL and not used.
|
||||
$this->aProperties[$sProperty] = '';
|
||||
$this->aProperties[$sProperty] = $this->PropertyFromDOMNode($oDOMChildNode, $sProperty);
|
||||
|
||||
// And build the original XML
|
||||
$this->sOriginalDashletXML .= $oDOMChildNode->ownerDocument->saveXML($oDOMChildNode)."\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->OnUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @throws \Exception
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @throws \DOMException
|
||||
*/
|
||||
public function FromParams($aParams)
|
||||
{
|
||||
// For unknown dashlet, parameters are not parsed but passed as a raw xml
|
||||
if (array_key_exists('xml', $aParams)) {
|
||||
// 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = [])
|
||||
{
|
||||
$sIconUrl = utils::HtmlEntities(utils::GetAbsoluteUrlAppRoot().'images/dashlet-unknown.png');
|
||||
$sExplainText = ($bEditMode) ? Dict::Format('UI:DashletUnknown:RenderText:Edit', $this->GetDashletType()) : Dict::S('UI:DashletUnknown:RenderText:View');
|
||||
|
||||
$oDashletContainer = new DashletContainer(null, ['dashlet-content']);
|
||||
|
||||
$oDashletContainer->AddHtml('<div class="dashlet-ukn-image"><img src="'.$sIconUrl.'" /></div><div class="dashlet-ukn-text">'.$sExplainText.'</div>');
|
||||
|
||||
return $oDashletContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = [])
|
||||
{
|
||||
$sIconUrl = utils::HtmlEntities(utils::GetAbsoluteUrlAppRoot().'images/dashlet-unknown.png');
|
||||
$sExplainText = Dict::Format('UI:DashletUnknown:RenderNoDataText:Edit', $this->GetDashletType());
|
||||
|
||||
$oDashletContainer = new DashletContainer(null, ['dashlet-content']);
|
||||
|
||||
$oDashletContainer->AddHtml('<div class="dashlet-ukn-image"><img src="'.$sIconUrl.'" /></div><div class="dashlet-ukn-text">'.$sExplainText.'</div>');
|
||||
|
||||
return $oDashletContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function GetForm($aInfo = [])
|
||||
{
|
||||
if (isset($aInfo['configuration']) && empty($this->sOriginalDashletXML)) {
|
||||
$this->sOriginalDashletXML = $aInfo['configuration'];
|
||||
}
|
||||
|
||||
return parent::GetForm($aInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function GetPropertiesFields(DesignerForm $oForm)
|
||||
{
|
||||
$oField = new DesignerXMLField('xml', Dict::S('UI:DashletUnknown:Prop-XMLConfiguration'), $this->sOriginalDashletXML);
|
||||
$oForm->AddField($oField);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function PropertyFromDOMNode($oDOMNode, $sProperty)
|
||||
{
|
||||
$bHasSubProperties = false;
|
||||
foreach ($oDOMNode->childNodes as $oDOMChildNode) {
|
||||
if ($oDOMChildNode->nodeType === XML_ELEMENT_NODE) {
|
||||
$bHasSubProperties = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($bHasSubProperties) {
|
||||
$sTmp = $oDOMNode->ownerDocument->saveXML($oDOMNode, LIBXML_NOENT);
|
||||
$sTmp = trim(preg_replace("/(<".$oDOMNode->tagName."[^>]*>|<\/".$oDOMNode->tagName.">)/", "", $sTmp));
|
||||
|
||||
return $sTmp;
|
||||
} else {
|
||||
return parent::PropertyFromDOMNode($oDOMNode, $sProperty);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function PropertyToDOMNode($oDOMNode, $sProperty, $value)
|
||||
{
|
||||
// Save subnodes
|
||||
if (preg_match('/<(.*)>/', $value)) {
|
||||
/** @var \DOMDocumentFragment $oDOMFragment */
|
||||
$oDOMFragment = $oDOMNode->ownerDocument->createDocumentFragment();
|
||||
$oDOMFragment->appendXML($value);
|
||||
$oDOMNode->appendChild($oDOMFragment);
|
||||
} else {
|
||||
parent::PropertyToDOMNode($oDOMNode, $sProperty, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @throws \DOMException
|
||||
*/
|
||||
public function Update($aValues, $aUpdatedFields)
|
||||
{
|
||||
$this->FromParams($aValues);
|
||||
|
||||
// OnUpdate() already done in FromParams()
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
517
sources/Application/Dashlet/Dashlet.php
Normal file
517
sources/Application/Dashlet/Dashlet.php
Normal file
@@ -0,0 +1,517 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2012-2024 Combodo SAS
|
||||
//
|
||||
// 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\Application\Dashlet;
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\Dashlet\DashletContainer;
|
||||
use Combodo\iTop\Application\UI\Base\iUIBlock;
|
||||
use Combodo\iTop\Application\UI\Base\UIBlock;
|
||||
use Combodo\iTop\Application\WebPage\WebPage;
|
||||
use Combodo\iTop\DesignDocument;
|
||||
use Combodo\iTop\DesignElement;
|
||||
use Combodo\iTop\PropertyType\Serializer\XMLNormalizer;
|
||||
use DesignerForm;
|
||||
use DesignerHiddenField;
|
||||
use Dict;
|
||||
use DOMException;
|
||||
use DOMNode;
|
||||
use Exception;
|
||||
use ModelReflection;
|
||||
use OQLException;
|
||||
use UnknownClassOqlException;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Base class for all 'dashlets' (i.e. widgets to be inserted into a dashboard)
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
abstract class Dashlet
|
||||
{
|
||||
/** @var string */
|
||||
public const APPUSERPREFERENCES_PREFIX = 'Dashlet';
|
||||
|
||||
protected $oModelReflection;
|
||||
protected $sId;
|
||||
protected $bRedrawNeeded;
|
||||
protected $bFormRedrawNeeded;
|
||||
protected $aProperties; // array of {property => value}
|
||||
protected $aCSSClasses;
|
||||
protected $sDashletType;
|
||||
protected array $aDefinition;
|
||||
|
||||
/**
|
||||
* Dashlet constructor.
|
||||
*
|
||||
* @param \ModelReflection $oModelReflection
|
||||
* @param string $sId
|
||||
* @param string|null $sDashletType
|
||||
*/
|
||||
public function __construct(ModelReflection $oModelReflection, $sId)
|
||||
{
|
||||
$this->oModelReflection = $oModelReflection;
|
||||
$this->sId = $sId;
|
||||
$this->bRedrawNeeded = true; // By default: redraw each time a property changes
|
||||
$this->bFormRedrawNeeded = false; // By default: no need to redraw the form (independent fields)
|
||||
$this->aProperties = []; // By default: there is no property
|
||||
$this->aCSSClasses = ['ibo-dashlet'];
|
||||
$this->sDashletType = get_class($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assuming that a property has the type of its default value, set in the constructor
|
||||
*
|
||||
* @param string $sProperty
|
||||
* @param string $sValue
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function Str2Prop($sProperty, $sValue)
|
||||
{
|
||||
$refValue = $this->aProperties[$sProperty];
|
||||
$sRefType = gettype($refValue);
|
||||
|
||||
if (gettype($sValue) == $sRefType) {
|
||||
// Do not change anything in that case!
|
||||
$ret = $sValue;
|
||||
} elseif ($sRefType == 'boolean') {
|
||||
$ret = ($sValue == 'true');
|
||||
} elseif ($sRefType == 'array') {
|
||||
$ret = explode(',', $sValue);
|
||||
} elseif (is_array($sValue)) {
|
||||
$ret = $sValue;
|
||||
} else {
|
||||
$ret = $sValue;
|
||||
settype($ret, $sRefType);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function Prop2Str($value)
|
||||
{
|
||||
$sType = gettype($value);
|
||||
if ($sType == 'boolean') {
|
||||
$sRet = $value ? 'true' : 'false';
|
||||
} elseif ($sType == 'array') {
|
||||
$sRet = implode(',', $value);
|
||||
} else {
|
||||
$sRet = (string)$value;
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
protected function OnUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public function FromDOMNode(DesignElement $oDOMNode)
|
||||
{
|
||||
foreach ($this->aProperties as $sProperty => $value) {
|
||||
$oPropNode = $oDOMNode->getElementsByTagName($sProperty)->item(0);
|
||||
if ($oPropNode != null) {
|
||||
$this->aProperties[$sProperty] = $this->PropertyFromDOMNode($oPropNode, $sProperty);
|
||||
}
|
||||
}
|
||||
$this->OnUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $oDOMNode
|
||||
*/
|
||||
public function ToDOMNode($oDOMNode)
|
||||
{
|
||||
foreach ($this->aProperties as $sProperty => $value) {
|
||||
$oPropNode = $oDOMNode->ownerDocument->createElement($sProperty);
|
||||
$oDOMNode->appendChild($oPropNode);
|
||||
$this->PropertyToDOMNode($oPropNode, $sProperty, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $oDOMNode
|
||||
* @param string $sProperty
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function PropertyFromDOMNode($oDOMNode, $sProperty)
|
||||
{
|
||||
$res = $this->Str2Prop($sProperty, $oDOMNode->textContent);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $oDOMNode
|
||||
* @param string $sProperty
|
||||
* @param mixed $value
|
||||
*/
|
||||
protected function PropertyToDOMNode($oDOMNode, $sProperty, $value)
|
||||
{
|
||||
$sXmlValue = $this->Prop2Str($value);
|
||||
$oTextNode = $oDOMNode->ownerDocument->createTextNode($sXmlValue);
|
||||
$oDOMNode->appendChild($oTextNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sXml
|
||||
*
|
||||
* @throws \DOMException
|
||||
*/
|
||||
public function FromXml($sXml)
|
||||
{
|
||||
$oDomDoc = new DesignDocument('1.0', 'UTF-8');
|
||||
libxml_clear_errors();
|
||||
$oDomDoc->loadXml($sXml);
|
||||
$aErrors = libxml_get_errors();
|
||||
if (count($aErrors) > 0) {
|
||||
throw new DOMException("Malformed XML");
|
||||
}
|
||||
|
||||
/** @var DesignElement $oDOMNode */
|
||||
$oDOMNode = $oDomDoc->firstChild;
|
||||
$this->FromDOMNode($oDOMNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aParams
|
||||
*/
|
||||
public function FromParams($aParams)
|
||||
{
|
||||
foreach ($this->aProperties as $sProperty => $value) {
|
||||
if (array_key_exists($sProperty, $aParams)) {
|
||||
$this->aProperties[$sProperty] = $aParams[$sProperty];
|
||||
}
|
||||
}
|
||||
$this->OnUpdate();
|
||||
}
|
||||
|
||||
public function FromDenormalizedParams(array $aDenormalizedParams)
|
||||
{
|
||||
$this->aProperties = XMLNormalizer::GetInstance()->Normalize($aDenormalizedParams, $this->sDashletType, 'Dashlet');
|
||||
$this->OnUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Rel. path to the app. root of the JS files required by the dashlet
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetJSFilesRelPaths(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Rel. path to the app. root of the CSS files required by the dashlet
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetCSSFilesRelPaths(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param bool $bEditMode
|
||||
* @param bool $bEnclosingDiv
|
||||
* @param array $aExtraParams
|
||||
*/
|
||||
public function DoRender($oPage, $bEditMode = false, $bEnclosingDiv = true, $aExtraParams = []): UIBlock
|
||||
{
|
||||
$sId = $this->GetID();
|
||||
|
||||
if ($bEnclosingDiv) {
|
||||
if ($bEditMode) {
|
||||
$oDashletContainer = new DashletContainer("dashlet_{$sId}");
|
||||
} else {
|
||||
$oDashletContainer = new DashletContainer();
|
||||
}
|
||||
$oDashletContainer->AddCSSClasses($this->aCSSClasses);
|
||||
} else {
|
||||
$oDashletContainer = new DashletContainer();
|
||||
$oDashletContainer->AddCSSClasses($this->aCSSClasses);
|
||||
}
|
||||
|
||||
$oDashletContainer->AddMultipleJsFilesRelPaths($this->GetJSFilesRelPaths());
|
||||
$oDashletContainer->AddMultipleCssFilesRelPaths($this->GetCSSFilesRelPaths());
|
||||
|
||||
try {
|
||||
if (get_class($this->oModelReflection) == 'ModelReflectionRuntime') {
|
||||
$oBlock = $this->Render($oPage, $bEditMode, $aExtraParams);
|
||||
} else {
|
||||
$oBlock = $this->RenderNoData($oPage, $bEditMode, $aExtraParams);
|
||||
}
|
||||
$oDashletContainer->AddSubBlock($oBlock);
|
||||
} catch (UnknownClassOqlException $e) {
|
||||
// Maybe the class is part of a non-installed module, fail silently
|
||||
// Except in Edit mode
|
||||
if ($bEditMode) {
|
||||
$oDashletContainer->AddCSSClass("dashlet-content");
|
||||
$oDashletContainer->AddHtml('<h2>'.$e->GetUserFriendlyDescription().'</h2>');
|
||||
}
|
||||
} catch (OqlException $e) {
|
||||
$oDashletContainer->AddCSSClass("dashlet-content");
|
||||
$oDashletContainer->AddHtml('<p>'.utils::HtmlEntities($e->GetUserFriendlyDescription()).'</p>');
|
||||
} catch (Exception $e) {
|
||||
$oDashletContainer->AddCSSClass("dashlet-content");
|
||||
$oDashletContainer->AddHtml('<p>'.$e->getMessage().'</p>');
|
||||
}
|
||||
|
||||
if ($bEditMode) {
|
||||
$sClass = $this->sDashletType;
|
||||
$sType = $this->sDashletType;
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('#dashlet_$sId').dashlet({dashlet_id: '$sId', dashlet_class: '$sClass', 'dashlet_type': '$sType'});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
return $oDashletContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sId
|
||||
*/
|
||||
public function SetID($sId)
|
||||
{
|
||||
$this->sId = $sId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GetID()
|
||||
{
|
||||
return $this->sId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param bool $bEditMode
|
||||
* @param array $aExtraParams
|
||||
*
|
||||
* @return iUIBlock
|
||||
*/
|
||||
abstract public function Render($oPage, $bEditMode = false, $aExtraParams = []);
|
||||
|
||||
/**
|
||||
* Rendering without the real data
|
||||
*
|
||||
* @param WebPage $oPage
|
||||
* @param bool $bEditMode
|
||||
* @param array $aExtraParams
|
||||
*
|
||||
* @return iUIBlock
|
||||
*/
|
||||
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = [])
|
||||
{
|
||||
return $this->Render($oPage, $bEditMode, $aExtraParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DesignerForm $oForm
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function GetPropertiesFields(\DesignerForm $oForm);
|
||||
|
||||
/**
|
||||
* @param \DOMNode $oContainerNode
|
||||
*/
|
||||
public function ToXml(DOMNode $oContainerNode)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aValues
|
||||
* @param array $aUpdatedFields
|
||||
*
|
||||
* @return Dashlet
|
||||
*/
|
||||
public function Update($aValues, $aUpdatedFields)
|
||||
{
|
||||
foreach ($aUpdatedFields as $sProp) {
|
||||
if (array_key_exists($sProp, $this->aProperties)) {
|
||||
$this->aProperties[$sProp] = $this->Str2Prop($sProp, $aValues[$sProp]);
|
||||
}
|
||||
}
|
||||
$this->OnUpdate();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function IsRedrawNeeded()
|
||||
{
|
||||
return $this->bRedrawNeeded;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function IsFormRedrawNeeded()
|
||||
{
|
||||
return $this->bFormRedrawNeeded;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aInfo
|
||||
*
|
||||
* @return \DesignerForm
|
||||
*/
|
||||
public function GetForm($aInfo = [])
|
||||
{
|
||||
$oForm = new DesignerForm();
|
||||
$sPrefix = "dashlet_".$this->GetID();
|
||||
$oForm->SetPrefix($sPrefix);
|
||||
$oForm->SetHierarchyPath($sPrefix);
|
||||
$oForm->SetParamsContainer('params');
|
||||
|
||||
$this->GetPropertiesFields($oForm);
|
||||
|
||||
$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);
|
||||
|
||||
return $oForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function IsVisible()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function CanCreateFromOQL()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DesignerForm $oForm
|
||||
* @param string|null $sOQL
|
||||
*/
|
||||
public function GetPropertiesFieldsFromOQL(DesignerForm $oForm, $sOQL = null)
|
||||
{
|
||||
// Default: do nothing since it's not supported
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sOql
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function GetGroupByOptions($sOql)
|
||||
{
|
||||
$aGroupBy = [];
|
||||
try {
|
||||
$oQuery = $this->oModelReflection->GetQuery($sOql);
|
||||
$sClass = $oQuery->GetClass();
|
||||
foreach ($this->oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType) {
|
||||
// For external fields, find the real type of the target
|
||||
$sExtFieldAttCode = $sAttCode;
|
||||
$sTargetClass = $sClass;
|
||||
while (is_a($sAttType, 'AttributeExternalField', true)) {
|
||||
$sExtKeyAttCode = $this->oModelReflection->GetAttributeProperty($sTargetClass, $sExtFieldAttCode, 'extkey_attcode');
|
||||
$sTargetAttCode = $this->oModelReflection->GetAttributeProperty($sTargetClass, $sExtFieldAttCode, 'target_attcode');
|
||||
$sTargetClass = $this->oModelReflection->GetAttributeProperty($sTargetClass, $sExtKeyAttCode, 'targetclass');
|
||||
$aTargetAttCodes = $this->oModelReflection->ListAttributes($sTargetClass);
|
||||
$sAttType = $aTargetAttCodes[$sTargetAttCode];
|
||||
$sExtFieldAttCode = $sTargetAttCode;
|
||||
}
|
||||
|
||||
$aForbidenAttType = [
|
||||
'AttributeLinkedSet',
|
||||
'AttributeFriendlyName',
|
||||
|
||||
'iAttributeNoGroupBy', //we cannot only use iAttributeNoGroupBy since this method is also used by the designer who do not have access to the classes' PHP reflection API. So the known classes has to be listed altogether
|
||||
'AttributeOneWayPassword',
|
||||
'AttributeEncryptedString',
|
||||
'AttributePassword',
|
||||
];
|
||||
foreach ($aForbidenAttType as $sForbidenAttType) {
|
||||
if (is_a($sAttType, $sForbidenAttType, true)) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
$sLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);
|
||||
if (!in_array($sLabel, $aGroupBy)) {
|
||||
$aGroupBy[$sAttCode] = $sLabel;
|
||||
|
||||
if (is_a($sAttType, 'AttributeDateTime', true)) {
|
||||
$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);
|
||||
} catch (Exception $e) {
|
||||
// Fallback in case of OQL problem
|
||||
}
|
||||
|
||||
return $aGroupBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GetDashletType()
|
||||
{
|
||||
return $this->sDashletType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sDashletType
|
||||
*/
|
||||
public function SetDashletType($sDashletType)
|
||||
{
|
||||
$this->sDashletType = $sDashletType;
|
||||
}
|
||||
|
||||
public function GetDenormalizedProperties(): ?array
|
||||
{
|
||||
return XMLNormalizer::GetInstance()->Denormalize($this->aProperties, $this->sDashletType, 'Dashlet');
|
||||
}
|
||||
}
|
||||
12
sources/Application/Dashlet/DashletException.php
Normal file
12
sources/Application/Dashlet/DashletException.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Application\Dashlet;
|
||||
|
||||
class DashletException extends \Exception
|
||||
{
|
||||
}
|
||||
49
sources/Application/Dashlet/DashletFactory.php
Normal file
49
sources/Application/Dashlet/DashletFactory.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Application\Dashlet;
|
||||
|
||||
use Combodo\iTop\Application\Dashlet\Service\DashletService;
|
||||
use ModelReflectionRuntime;
|
||||
|
||||
class DashletFactory
|
||||
{
|
||||
private static DashletFactory $oInstance;
|
||||
private $oModelReflectionRuntime;
|
||||
|
||||
protected function __construct()
|
||||
{
|
||||
$this->oModelReflectionRuntime = new ModelReflectionRuntime();
|
||||
}
|
||||
|
||||
final public static function GetInstance(): DashletFactory
|
||||
{
|
||||
if (!isset(static::$oInstance)) {
|
||||
static::$oInstance = new DashletFactory();
|
||||
}
|
||||
|
||||
return static::$oInstance;
|
||||
}
|
||||
|
||||
public function SetModelReflectionRuntime(ModelReflectionRuntime $oModelReflectionRuntime): void
|
||||
{
|
||||
$this->oModelReflectionRuntime = $oModelReflectionRuntime;
|
||||
}
|
||||
|
||||
public function CreateDashlet(string $sClass, string $sId): Dashlet
|
||||
{
|
||||
if (!DashletService::GetInstance()->IsDashletAvailable($sClass)) {
|
||||
throw new DashletException("Dashlet ".json_encode($sClass)." is not available");
|
||||
}
|
||||
|
||||
/** @var Dashlet $oDashlet */
|
||||
$oDashlet = new $sClass($this->oModelReflectionRuntime, $sId);
|
||||
$oDashlet->SetDashletType($sClass);
|
||||
|
||||
return $oDashlet;
|
||||
}
|
||||
}
|
||||
143
sources/Application/Dashlet/Service/DashletService.php
Normal file
143
sources/Application/Dashlet/Service/DashletService.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Application\Dashlet\Service;
|
||||
|
||||
use Combodo\iTop\Application\Dashlet\DashletException;
|
||||
use Combodo\iTop\DesignDocument;
|
||||
use Combodo\iTop\DesignElement;
|
||||
use Dict;
|
||||
use utils;
|
||||
|
||||
class DashletService
|
||||
{
|
||||
private static DashletService $oInstance;
|
||||
private array $aDashlets = [];
|
||||
|
||||
protected function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
final public static function GetInstance(): DashletService
|
||||
{
|
||||
if (!isset(static::$oInstance)) {
|
||||
static::$oInstance = new DashletService();
|
||||
}
|
||||
|
||||
return static::$oInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws \Combodo\iTop\Application\Dashlet\DashletException
|
||||
* @throws \DOMFormatException
|
||||
*/
|
||||
public function GetAvailableDashlets(): array
|
||||
{
|
||||
$this->InitDashletDefinitions();
|
||||
|
||||
return $this->aDashlets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sXMLContent
|
||||
*
|
||||
* @return \Combodo\iTop\DesignElement
|
||||
* @throws \Combodo\iTop\Application\Dashlet\DashletException
|
||||
*/
|
||||
private function GetDomNode(string $sXMLContent): DesignElement
|
||||
{
|
||||
$oDoc = new DesignDocument();
|
||||
libxml_clear_errors();
|
||||
$oDoc->loadXML($sXMLContent);
|
||||
$aErrors = libxml_get_errors();
|
||||
if (count($aErrors) > 0) {
|
||||
throw new DashletException('Dashlets definition not correctly formatted!');
|
||||
}
|
||||
|
||||
/** @var \Combodo\iTop\DesignElement $oRoot */
|
||||
$oRoot = $oDoc->firstChild;
|
||||
|
||||
return $oRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws \Combodo\iTop\Application\Dashlet\DashletException
|
||||
*/
|
||||
private function GetXMLContent(): string
|
||||
{
|
||||
$sPath = utils::GetAbsoluteModulePath('core')."dashlets.xml";
|
||||
if (!file_exists($sPath)) {
|
||||
throw new DashletException("Dashlets definition not present");
|
||||
}
|
||||
|
||||
return file_get_contents($sPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Combodo\iTop\Application\Dashlet\DashletException
|
||||
* @throws \DOMFormatException
|
||||
*/
|
||||
public function IsDashletAvailable(string $sClass)
|
||||
{
|
||||
$this->InitDashletDefinitions();
|
||||
|
||||
return array_key_exists($sClass, $this->aDashlets);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
*
|
||||
* @return array
|
||||
* @throws \Combodo\iTop\Application\Dashlet\DashletException
|
||||
*/
|
||||
public function GetDashletDefinition(string $sClass): array
|
||||
{
|
||||
if ($this->IsDashletAvailable($sClass)) {
|
||||
return $this->aDashlets[$sClass];
|
||||
}
|
||||
|
||||
throw new DashletException('Dashlets definition '.json_encode($sClass).' not present');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws \Combodo\iTop\Application\Dashlet\DashletException
|
||||
* @throws \DOMFormatException
|
||||
*/
|
||||
private function InitDashletDefinitions(): void
|
||||
{
|
||||
if (count($this->aDashlets) === 0) {
|
||||
$this->aDashlets = [];
|
||||
|
||||
$oDashletsNode = $this->GetDomNode($this->GetXMLContent());
|
||||
/** @var DesignElement $oDashletNode */
|
||||
foreach ($oDashletsNode->getElementsByTagName('dashlet') as $oDashletNode) {
|
||||
$sType = $oDashletNode->getAttribute('id');
|
||||
$aInfo = [
|
||||
'label' => Dict::S($oDashletNode->GetChildText('label')),
|
||||
'icon' => $oDashletNode->GetChildText('icon'),
|
||||
'description' => Dict::S($oDashletNode->GetChildText('description')),
|
||||
'min_width' => intval($oDashletNode->GetChildText('min_width', '2')),
|
||||
'min_height' => intval($oDashletNode->GetChildText('min_height', '1')),
|
||||
'preferred_width' => intval($oDashletNode->GetChildText('preferred_width', '2')),
|
||||
'preferred_height' => intval($oDashletNode->GetChildText('preferred_height', '1')),
|
||||
'can_create_by_oql' => boolval($oDashletNode->GetChildText('can_create_by_oql', 'false')),
|
||||
];
|
||||
$this->aDashlets[$sType] = $aInfo;
|
||||
}
|
||||
|
||||
uasort($this->aDashlets, function ($a, $b) {
|
||||
return strcmp($a['label'], $b['label']);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Combodo\iTop\Application\UI\Base\Layout\DashletPanel;
|
||||
|
||||
use Combodo\iTop\Service\Dashboard\DashletService;
|
||||
use Combodo\iTop\Application\Dashlet\Service\DashletService;
|
||||
|
||||
class DashletPanelFactory
|
||||
{
|
||||
@@ -10,7 +10,7 @@ class DashletPanelFactory
|
||||
{
|
||||
$oDashletPanel = new DashletPanel($sId);
|
||||
|
||||
$aAvailableDashlets = DashletService::GetAvailableDashlets();
|
||||
$aAvailableDashlets = DashletService::GetInstance()->GetAvailableDashlets();
|
||||
|
||||
foreach ($aAvailableDashlets as $sDashletClass => $aDashletInformation) {
|
||||
$oDashletEntry = new DashletEntry($sDashletClass, $aDashletInformation['label'], $aDashletInformation['description'], $aDashletInformation['icon']);
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Service\Dashboard;
|
||||
|
||||
use ReflectionClass;
|
||||
|
||||
class DashletService
|
||||
{
|
||||
public static function GetAvailableDashlets(): array
|
||||
{
|
||||
$aDashlets = [];
|
||||
|
||||
foreach (get_declared_classes() as $sDashletClass) {
|
||||
// DashletUnknown is not among the selection as it is just a fallback for dashlets that can't be instantiated.
|
||||
if (is_subclass_of($sDashletClass, 'Dashlet') && !in_array($sDashletClass, ['DashletUnknown', 'DashletProxy'])) {
|
||||
$oReflection = new ReflectionClass($sDashletClass);
|
||||
if (!$oReflection->isAbstract()) {
|
||||
$aCallSpec = [$sDashletClass, 'IsVisible'];
|
||||
$bVisible = call_user_func($aCallSpec);
|
||||
if ($bVisible) {
|
||||
$aCallSpec = [$sDashletClass, 'GetInfo'];
|
||||
$aInfo = call_user_func($aCallSpec);
|
||||
$aDashlets[$sDashletClass] = $aInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aDashlets;
|
||||
}
|
||||
}
|
||||
@@ -97,6 +97,19 @@ class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeURL::class, 'Attribu
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\iAttributeNoGroupBy::class, 'iAttributeNoGroupBy');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\MissingColumnException::class, 'MissingColumnException');
|
||||
|
||||
class_alias(\Combodo\iTop\Application\Dashlet\Dashlet::class, 'Dashlet');
|
||||
class_alias(\Combodo\iTop\Application\Dashlet\Core\DashletBadge::class, 'DashletBadge');
|
||||
class_alias(\Combodo\iTop\Application\Dashlet\Core\DashletGroupBy::class, 'DashletGroupBy');
|
||||
class_alias(\Combodo\iTop\Application\Dashlet\Core\DashletGroupByBars::class, 'DashletGroupByBars');
|
||||
class_alias(\Combodo\iTop\Application\Dashlet\Core\DashletGroupByPie::class, 'DashletGroupByPie');
|
||||
class_alias(\Combodo\iTop\Application\Dashlet\Core\DashletGroupByTable::class, 'DashletGroupByTable');
|
||||
class_alias(\Combodo\iTop\Application\Dashlet\Core\DashletHeaderDynamic::class, 'DashletHeaderDynamic');
|
||||
class_alias(\Combodo\iTop\Application\Dashlet\Core\DashletHeaderStatic::class, 'DashletHeaderStatic');
|
||||
class_alias(\Combodo\iTop\Application\Dashlet\Core\DashletObjectList::class, 'DashletObjectList');
|
||||
class_alias(\Combodo\iTop\Application\Dashlet\Core\DashletPlainText::class, 'DashletPlainText');
|
||||
class_alias(\Combodo\iTop\Application\Dashlet\Core\DashletProxy::class, 'DashletProxy');
|
||||
class_alias(\Combodo\iTop\Application\Dashlet\Core\DashletUnknown::class, 'DashletUnknown');
|
||||
|
||||
class_alias(\Combodo\iTop\PropertyType\PropertyType::class, 'Combodo-PropertyType');
|
||||
|
||||
class_alias(\Combodo\iTop\PropertyType\ValueType\Branch\ValueTypeCollection::class, 'Combodo-ValueType-Collection');
|
||||
|
||||
Reference in New Issue
Block a user