mirror of
https://github.com/Combodo/iTop.git
synced 2026-06-19 14:26:37 +02:00
Compare commits
48 Commits
feature/91
...
class_extr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c80c93b4f | ||
|
|
efc6e6730b | ||
|
|
3769bc1024 | ||
|
|
e5338f28eb | ||
|
|
5cfe7fa6eb | ||
|
|
27c16a782c | ||
|
|
7c21178e1d | ||
|
|
3a6e148c11 | ||
|
|
4d9e18890a | ||
|
|
50ffaa2b55 | ||
|
|
15c8f5903b | ||
|
|
0bc6f5d56a | ||
|
|
d248524cc8 | ||
|
|
410dc152d7 | ||
|
|
423413e3a0 | ||
|
|
8896c576d7 | ||
|
|
975046da17 | ||
|
|
e4a281c3ff | ||
|
|
90729f84b6 | ||
|
|
c075a5c145 | ||
|
|
cd6d130bcb | ||
|
|
7ca2c56dad | ||
|
|
dd0ac58643 | ||
|
|
df943ec8b5 | ||
|
|
076a6d0495 | ||
|
|
02e59c906b | ||
|
|
49f91961e7 | ||
|
|
441519d71d | ||
|
|
5c75d0ce7c | ||
|
|
2efe80265d | ||
|
|
b58a64e143 | ||
|
|
ff11aec7fe | ||
|
|
f79bb9d51c | ||
|
|
3c365fc103 | ||
|
|
7b193dd737 | ||
|
|
1538596db8 | ||
|
|
9d4fc345bc | ||
|
|
08c9309572 | ||
|
|
d072aa05f1 | ||
|
|
85c1f091e2 | ||
|
|
be6a8abdf4 | ||
|
|
60bcf0c85f | ||
|
|
4a8804b8ac | ||
|
|
b014b9f638 | ||
|
|
1c633c6173 | ||
|
|
cdc95aca7b | ||
|
|
b4460999ef | ||
|
|
a713e1b56e |
@@ -1,9 +0,0 @@
|
||||
# Developers
|
||||
|
||||
## PHP Code Styles
|
||||
We use `PHP CS Fixer` to ensure code formating consistency across PHP codebase.
|
||||
You can find the configuration and instructions to run it [here](../tests/php-code-style/README.md).
|
||||
|
||||
## PHP Static Analysis
|
||||
We use `PHPStan` to ensure code quality and to detect potential bugs in our PHP codebase.
|
||||
You can find the configuration and instructions to run it [here](../tests/php-static-analysis/README.md).
|
||||
14
.github/workflows/action.yml
vendored
14
.github/workflows/action.yml
vendored
@@ -26,23 +26,13 @@ jobs:
|
||||
|
||||
fi
|
||||
|
||||
- name: Add internal tag if member of the organization
|
||||
- name: Add internal tag if member
|
||||
if: env.is_member == 'true'
|
||||
run: |
|
||||
curl -X POST -H "Authorization: token ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/Combodo/iTop/issues/${{ github.event.pull_request.number }}/labels \
|
||||
-d '{"labels":["internal"]}'
|
||||
|
||||
- name: Set PR author as assignee if member of the organization
|
||||
if: env.is_member == 'true'
|
||||
run: |
|
||||
curl -L \
|
||||
-X POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}" \
|
||||
https://api.github.com/repos/Combodo/iTop/issues/${{ github.event.pull_request.number }}/assignees \
|
||||
-d '{"assignees":["${{ github.event.pull_request.user.login }}"]}'
|
||||
env:
|
||||
is_member: ${{ env.is_member }}
|
||||
|
||||
@@ -50,4 +40,4 @@ jobs:
|
||||
uses: actions/add-to-project@v1.0.2
|
||||
with:
|
||||
project-url: ${{ env.project_url }}
|
||||
github-token: ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}
|
||||
github-token: ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -58,9 +58,6 @@ tests/*/vendor/*
|
||||
/tests/php-unit-tests/phpunit.xml
|
||||
/tests/php-unit-tests/postbuild_integration.xml
|
||||
|
||||
# PHP CS Fixer: Cache file
|
||||
/.php-cs-fixer.cache
|
||||
|
||||
|
||||
# Jetbrains
|
||||
/.idea/**
|
||||
|
||||
@@ -73,9 +73,6 @@ iTop development is sponsored, led, and supported by [Combodo][0].
|
||||
|
||||
[0]: https://www.combodo.com
|
||||
|
||||
## Developers
|
||||
|
||||
You can find information and instructions about our quality tools and how to run them [here](.doc/developers.md).
|
||||
|
||||
## Contributors
|
||||
|
||||
|
||||
@@ -16,5 +16,5 @@ require_once(APPROOT.'/application/audit.category.class.inc.php');
|
||||
require_once(APPROOT.'/application/audit.domain.class.inc.php');
|
||||
require_once(APPROOT.'/application/audit.rule.class.inc.php');
|
||||
require_once(APPROOT.'/application/query.class.inc.php');
|
||||
require_once(APPROOT.'/setup/moduleinstallation/moduleinstallation.class.inc.php');
|
||||
require_once(APPROOT.'/setup/moduleinstallation.class.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
|
||||
@@ -819,7 +819,6 @@ HTML
|
||||
foreach ($aNotificationClasses as $sNotifClass) {
|
||||
$aNotifSearches[$sNotifClass] = DBObjectSearch::FromOQL("SELECT $sNotifClass AS Ev WHERE Ev.object_id = :id AND Ev.object_class = :class");
|
||||
$aNotifSearches[$sNotifClass]->SetInternalParams($aParams);
|
||||
$aNotifSearches[$sNotifClass]->AllowAllData();
|
||||
$oNotifSet = new DBObjectSet($aNotifSearches[$sNotifClass], []);
|
||||
$iNotifsCount += $oNotifSet->Count();
|
||||
}
|
||||
@@ -833,7 +832,6 @@ HTML
|
||||
'menu' => false,
|
||||
'panel_title' => MetaModel::GetName($sNotifClass),
|
||||
'panel_icon' => MetaModel::GetClassIcon($sNotifClass, false),
|
||||
'display_unauthorized_objects' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -5634,7 +5632,7 @@ JS
|
||||
* @return void
|
||||
* @since 3.1.0
|
||||
*/
|
||||
final public function AddAttributeFlags(string $sAttCode, int $iFlags, string $sTargetState = '', ?string $sReason = null): void
|
||||
final public function AddAttributeFlags(string $sAttCode, int $iFlags, string $sTargetState = '', string $sReason = null): void
|
||||
{
|
||||
if (!isset($this->aAttributesFlags[$sTargetState])) {
|
||||
$this->aAttributesFlags[$sTargetState] = [];
|
||||
@@ -5657,7 +5655,7 @@ JS
|
||||
* @return void
|
||||
* @since 3.1.0
|
||||
*/
|
||||
final public function ForceAttributeFlags(string $sAttCode, int $iFlags, string $sTargetState = '', ?string $sReason = null): void
|
||||
final public function ForceAttributeFlags(string $sAttCode, int $iFlags, string $sTargetState = '', string $sReason = null): void
|
||||
{
|
||||
if (!isset($this->aAttributesFlags[$sTargetState])) {
|
||||
$this->aAttributesFlags[$sTargetState] = [];
|
||||
@@ -5698,7 +5696,7 @@ JS
|
||||
* @return void
|
||||
* @since 3.1.0
|
||||
*/
|
||||
final public function AddInitialAttributeFlags(string $sAttCode, int $iFlags, ?string $sReason = null)
|
||||
final public function AddInitialAttributeFlags(string $sAttCode, int $iFlags, string $sReason = null)
|
||||
{
|
||||
if (!isset($this->aInitialAttributesFlags)) {
|
||||
$this->aInitialAttributesFlags = [];
|
||||
@@ -5720,7 +5718,7 @@ JS
|
||||
* @return void
|
||||
* @since 3.1.0
|
||||
*/
|
||||
final public function ForceInitialAttributeFlags(string $sAttCode, int $iFlags, ?string $sReason = null)
|
||||
final public function ForceInitialAttributeFlags(string $sAttCode, int $iFlags, string $sReason = null)
|
||||
{
|
||||
if (!isset($this->aInitialAttributesFlags)) {
|
||||
$this->aInitialAttributesFlags = [];
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
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;
|
||||
@@ -12,9 +15,12 @@ use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\Dashboard\DashboardLayout as DashboardLayoutUIBlock;
|
||||
use Combodo\iTop\Application\WebPage\iTopWebPage;
|
||||
use Combodo\iTop\Application\WebPage\WebPage;
|
||||
use Combodo\iTop\DesignDocument;
|
||||
use Combodo\iTop\DesignElement;
|
||||
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');
|
||||
|
||||
/**
|
||||
@@ -24,7 +30,7 @@ require_once(APPROOT.'core/modelreflection.class.inc.php');
|
||||
*/
|
||||
abstract class Dashboard
|
||||
{
|
||||
/** @var string $sTitle*/
|
||||
/** @var string $sTitle */
|
||||
protected $sTitle;
|
||||
/** @var bool $bAutoReload */
|
||||
protected $bAutoReload;
|
||||
@@ -34,7 +40,7 @@ abstract class Dashboard
|
||||
protected $sLayoutClass;
|
||||
/** @var array $aWidgetsData */
|
||||
protected $aWidgetsData;
|
||||
/** @var \DOMNode|null $oDOMNode */
|
||||
/** @var DesignElement|null $oDOMNode */
|
||||
protected $oDOMNode;
|
||||
/** @var string $sId */
|
||||
protected $sId;
|
||||
@@ -43,6 +49,11 @@ abstract class Dashboard
|
||||
/** @var \ModelReflection $oMetaModel */
|
||||
protected $oMetaModel;
|
||||
|
||||
/** @var array Array of dashlets with position */
|
||||
protected array $aGridDashlets = [];
|
||||
|
||||
protected $oDashletFactory;
|
||||
|
||||
/**
|
||||
* Dashboard constructor.
|
||||
*
|
||||
@@ -57,6 +68,7 @@ abstract class Dashboard
|
||||
$this->aCells = [];
|
||||
$this->oDOMNode = null;
|
||||
$this->sId = $sId;
|
||||
$this->oDashletFactory = DashletFactory::GetInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,7 +80,7 @@ abstract class Dashboard
|
||||
{
|
||||
$this->aCells = []; // reset the content of the dashboard
|
||||
set_error_handler(['Dashboard', 'ErrorHandler']);
|
||||
$oDoc = new DOMDocument();
|
||||
$oDoc = new PropertyTypeDesign();
|
||||
$oDoc->loadXML($sXml);
|
||||
restore_error_handler();
|
||||
$this->FromDOMDocument($oDoc);
|
||||
@@ -77,10 +89,15 @@ abstract class Dashboard
|
||||
/**
|
||||
* @param \DOMDocument $oDoc
|
||||
*/
|
||||
public function FromDOMDocument(DOMDocument $oDoc)
|
||||
public function FromDOMDocument(DesignDocument $oDoc)
|
||||
{
|
||||
$this->oDOMNode = $oDoc->getElementsByTagName('dashboard')->item(0);
|
||||
|
||||
if ($this->oDOMNode->getElementsByTagName('cells')->count() === 0) {
|
||||
$this->FromDOMDocumentV2($oDoc);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($oLayoutNode = $this->oDOMNode->getElementsByTagName('layout')->item(0)) {
|
||||
$this->sLayoutClass = $oLayoutNode->textContent;
|
||||
} else {
|
||||
@@ -121,7 +138,7 @@ abstract class Dashboard
|
||||
$aDashletOrder = [];
|
||||
/** @var \DOMElement $oDomNode */
|
||||
foreach ($oDashletList as $oDomNode) {
|
||||
$oRank = $oDomNode->getElementsByTagName('rank')->item(0);
|
||||
$oRank = $oDomNode->getElementsByTagName('rank')->item(0);
|
||||
if ($oRank) {
|
||||
$iRank = (float)$oRank->textContent;
|
||||
}
|
||||
@@ -147,7 +164,39 @@ abstract class Dashboard
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $oDomNode
|
||||
* @param \DOMDocument $oDoc
|
||||
*/
|
||||
public function FromDOMDocumentV2(DesignDocument $oDoc)
|
||||
{
|
||||
$this->oDOMNode = $oDoc->getElementsByTagName('dashboard')->item(0);
|
||||
|
||||
$this->sLayoutClass = DashboardLayoutGrid::class;
|
||||
$this->sTitle = $this->oDOMNode->GetChildText('title', '');
|
||||
|
||||
$iRefresh = intval($this->oDOMNode->GetChildText('refresh', '0'));
|
||||
|
||||
$this->bAutoReload = $iRefresh > 0;
|
||||
$this->iAutoReloadSec = $iRefresh;
|
||||
|
||||
$oDashletsNode = $this->oDOMNode->GetUniqueElement('pos_dashlets');
|
||||
$oDashletList = $oDashletsNode->getElementsByTagName('pos_dashlet');
|
||||
foreach ($oDashletList as $oPosDashletNode) {
|
||||
$aGridDashlet = [];
|
||||
$aGridDashlet['position_x'] = intval($oPosDashletNode->GetChildText('position_x', '0'));
|
||||
$aGridDashlet['position_y'] = intval($oPosDashletNode->GetChildText('position_y', '0'));
|
||||
$aGridDashlet['width'] = intval($oPosDashletNode->GetChildText('width', '2'));
|
||||
$aGridDashlet['height'] = intval($oPosDashletNode->GetChildText('height', '1'));
|
||||
$oDashletNode = $oPosDashletNode->GetUniqueElement('dashlet');
|
||||
$sId = $oPosDashletNode->getAttribute('id');
|
||||
$oDashlet = $this->InitDashletFromDOMNode($oDashletNode);
|
||||
$oDashlet->SetID($sId);
|
||||
$aGridDashlet['dashlet'] = $oDashlet;
|
||||
$this->aGridDashlets[] = $aGridDashlet;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DesignElement $oDomNode
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
@@ -160,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);
|
||||
|
||||
@@ -204,20 +253,33 @@ abstract class Dashboard
|
||||
*/
|
||||
public function ToXml()
|
||||
{
|
||||
$oDoc = new DOMDocument();
|
||||
$oDoc->formatOutput = true; // indent (must be loaded with option LIBXML_NOBLANKS)
|
||||
$oDoc->preserveWhiteSpace = true; // otherwise the formatOutput option would have no effect
|
||||
|
||||
$oMainNode = $oDoc->createElement('dashboard');
|
||||
$oMainNode->setAttribute('xmlns:xsi', "http://www.w3.org/2001/XMLSchema-instance");
|
||||
$oDoc->appendChild($oMainNode);
|
||||
$oMainNode = $this->CreateEmptyDashboard();
|
||||
|
||||
$this->ToDOMNode($oMainNode);
|
||||
|
||||
$sXml = $oDoc->saveXML();
|
||||
$sXml = $oMainNode->ownerDocument->saveXML();
|
||||
|
||||
return $sXml;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DesignElement
|
||||
* @throws \DOMException
|
||||
*/
|
||||
public function CreateEmptyDashboard(): DesignElement
|
||||
{
|
||||
$oDoc = new DesignDocument();
|
||||
$oDoc->formatOutput = true; // indent (must be loaded with option LIBXML_NOBLANKS)
|
||||
$oDoc->preserveWhiteSpace = true; // otherwise the formatOutput option would have no effect
|
||||
|
||||
/** @var DesignElement $oMainNode */
|
||||
$oMainNode = $oDoc->createElement('dashboard');
|
||||
$oMainNode->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
|
||||
$oDoc->appendChild($oMainNode);
|
||||
|
||||
return $oMainNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $oDefinition
|
||||
*/
|
||||
@@ -279,7 +341,7 @@ abstract class Dashboard
|
||||
}
|
||||
$this->sTitle = $aParams['title'];
|
||||
$this->bAutoReload = $aParams['auto_reload'] == 'true';
|
||||
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int) $aParams['auto_reload_sec']);
|
||||
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$aParams['auto_reload_sec']);
|
||||
|
||||
foreach ($aParams['cells'] as $aCell) {
|
||||
$aCellDashlets = [];
|
||||
@@ -287,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']);
|
||||
}
|
||||
@@ -305,7 +367,11 @@ abstract class Dashboard
|
||||
|
||||
public function Save()
|
||||
{
|
||||
}
|
||||
|
||||
public function PersistDashboard(string $sXml): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -497,13 +563,13 @@ EOF
|
||||
* @param array $aExtraParams
|
||||
* @param bool $bCanEdit
|
||||
*
|
||||
* @return null|\Combodo\iTop\Application\UI\Base\Layout\Dashboard\DashboardLayout
|
||||
* @return \Combodo\iTop\Application\UI\Base\Layout\Dashboard\DashboardLayout
|
||||
*/
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = [], $bCanEdit = true)
|
||||
{
|
||||
$aExtraParams['dashboard_div_id'] = utils::Sanitize($aExtraParams['dashboard_div_id'] ?? null, $this->GetId(), utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
|
||||
|
||||
/** @var \DashboardLayoutMultiCol $oLayout */
|
||||
/** @var \DashboardLayout $oLayout */
|
||||
$oLayout = new $this->sLayoutClass();
|
||||
|
||||
foreach ($this->aCells as $iCellIdx => $aDashlets) {
|
||||
@@ -513,7 +579,13 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
$oDashboard = $oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams);
|
||||
if (count($this->aCells) > 0) {
|
||||
$aDashlets = $this->aCells;
|
||||
} else {
|
||||
$aDashlets = $this->aGridDashlets;
|
||||
}
|
||||
|
||||
$oDashboard = $oLayout->Render($oPage, $aDashlets, $bEditMode, $aExtraParams);
|
||||
$oPage->AddUiBlock($oDashboard);
|
||||
|
||||
$bFromDasboardPage = isset($aExtraParams['from_dashboard_page']) ? isset($aExtraParams['from_dashboard_page']) : false;
|
||||
@@ -603,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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -640,6 +695,7 @@ JS
|
||||
$iNewId = max($iNewId, (int)$oDashlet->GetID());
|
||||
}
|
||||
}
|
||||
|
||||
return $iNewId + 1;
|
||||
}
|
||||
|
||||
@@ -674,6 +730,7 @@ JS
|
||||
if (is_subclass_of($sType, 'Dashlet')) {
|
||||
return $sType;
|
||||
}
|
||||
|
||||
return 'DashletUnknown';
|
||||
}
|
||||
|
||||
@@ -726,6 +783,8 @@ 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;
|
||||
}
|
||||
|
||||
@@ -740,6 +799,7 @@ class RuntimeDashboard extends Dashboard
|
||||
|
||||
/**
|
||||
* @param bool $bCustomized
|
||||
*
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public function SetCustomFlag($bCustomized)
|
||||
@@ -764,6 +824,26 @@ class RuntimeDashboard extends Dashboard
|
||||
public function Save()
|
||||
{
|
||||
$sXml = $this->ToXml();
|
||||
|
||||
return $this->PersistDashboard($sXml);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sXml
|
||||
*
|
||||
* @return bool
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreCannotSaveObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \CoreWarning
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function PersistDashboard(string $sXml): bool
|
||||
{
|
||||
$oUDSearch = new DBObjectSearch('UserDashboard');
|
||||
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
||||
$oUDSearch->AddCondition('menu_code', $this->sId, '=');
|
||||
@@ -784,6 +864,7 @@ class RuntimeDashboard extends Dashboard
|
||||
utils::PushArchiveMode(false);
|
||||
$oUserDashboard->DBWrite();
|
||||
utils::PopArchiveMode();
|
||||
|
||||
return $bIsNew;
|
||||
}
|
||||
|
||||
@@ -1029,7 +1110,7 @@ EOF
|
||||
var dashboard = $('.ibo-dashboard#$sDivId')
|
||||
dashboard.block();
|
||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
|
||||
{ operation: 'toggle_dashboard', dashboard_id: '$sId', file: '$sFile', extra_params: $sExtraParams, reload_url: $sReloadURL },
|
||||
{ operation: 'toggle_dashboard', dashboard_id: '$sId', file: '$sFile', extra_params: $sExtraParams, reload_url: '$sReloadURL' },
|
||||
function(data) {
|
||||
dashboard.html(data);
|
||||
dashboard.unblock();
|
||||
@@ -1077,6 +1158,7 @@ JS
|
||||
$sId = utils::Sanitize($this->GetId(), '', 'element_identifier');
|
||||
|
||||
$sMenuTogglerId = "ibo-dashboard-menu-toggler-{$sId}";
|
||||
$sActionEditId = "ibo-dashboard-menu-edit-{$sId}";
|
||||
$sPopoverMenuId = "ibo-dashboard-menu-popover-{$sId}";
|
||||
$sName = 'UI:Dashboard:Actions';
|
||||
|
||||
@@ -1090,6 +1172,21 @@ JS
|
||||
} else {
|
||||
$oToolbar = $oDashboard->GetToolbar();
|
||||
}
|
||||
|
||||
// TODO 3.3 Check if we need different action for custom dashboard creation / edition
|
||||
$oActionEditButton = ButtonUIBlockFactory::MakeIconAction(
|
||||
'fas fa-pen',
|
||||
$this->HasCustomDashboard() ? Dict::S('UI:Dashboard:EditCustom') : Dict::S('UI:Dashboard:CreateCustom'),
|
||||
$sActionEditId,
|
||||
'',
|
||||
false,
|
||||
$sActionEditId
|
||||
)
|
||||
->AddCSSClass('ibo-top-bar--toolbar-dashboard-edit-button')
|
||||
->AddCSSClass('ibo-action-button');
|
||||
|
||||
$oToolbar->AddSubBlock($oActionEditButton);
|
||||
|
||||
$oActionButton = ButtonUIBlockFactory::MakeIconAction('fas fa-ellipsis-v', Dict::S($sName), $sName, '', false, $sMenuTogglerId)
|
||||
->AddCSSClass('ibo-top-bar--toolbar-dashboard-menu-toggler')
|
||||
->AddCSSClass('ibo-action-button');
|
||||
@@ -1099,8 +1196,8 @@ JS
|
||||
$sFile = addslashes(utils::LocalPath($this->sDefinitionFile));
|
||||
$sJSExtraParams = json_encode($aExtraParams);
|
||||
if ($this->HasCustomDashboard()) {
|
||||
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:EditCustom'), "return EditDashboard('{$this->sId}', '$sFile', $sJSExtraParams)");
|
||||
$aActions[$oEdit->GetUID()] = $oEdit->GetMenuItem();
|
||||
// $oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:EditCustom'), "return EditDashboard('{$this->sId}', '$sFile', $sJSExtraParams)");
|
||||
// $aActions[$oEdit->GetUID()] = $oEdit->GetMenuItem();
|
||||
$oRevert = new JSPopupMenuItem(
|
||||
'UI:Dashboard:RevertConfirm',
|
||||
Dict::S('UI:Dashboard:DeleteCustom'),
|
||||
@@ -1108,8 +1205,8 @@ JS
|
||||
);
|
||||
$aActions[$oRevert->GetUID()] = $oRevert->GetMenuItem();
|
||||
} else {
|
||||
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:CreateCustom'), "return EditDashboard('{$this->sId}', '$sFile', $sJSExtraParams)");
|
||||
$aActions[$oEdit->GetUID()] = $oEdit->GetMenuItem();
|
||||
// $oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:CreateCustom'), "return EditDashboard('{$this->sId}', '$sFile', $sJSExtraParams)");
|
||||
// $aActions[$oEdit->GetUID()] = $oEdit->GetMenuItem();
|
||||
}
|
||||
|
||||
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_DASHBOARD_ACTIONS, $this, $aActions);
|
||||
@@ -1223,7 +1320,7 @@ EOF
|
||||
$sId = json_encode($this->sId);
|
||||
$sLayoutClass = json_encode($this->sLayoutClass);
|
||||
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
|
||||
$sAutoReloadSec = (string) $this->iAutoReloadSec;
|
||||
$sAutoReloadSec = (string)$this->iAutoReloadSec;
|
||||
$sTitle = json_encode($this->sTitle);
|
||||
$sFile = json_encode($this->GetDefinitionFile());
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
|
||||
@@ -1395,19 +1492,11 @@ 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);
|
||||
$aDashlets[$sDashletClass] = ['label' => $aInfo['label'], 'class' => $sDashletClass, 'icon' => $aInfo['icon']];
|
||||
}
|
||||
}
|
||||
foreach ($aAllDashlets as $sDashletClass => $aInfo) {
|
||||
if ($aInfo['can_create_by_oql']) {
|
||||
$aDashlets[$sDashletClass] = ['label' => $aInfo['label'], 'class' => $sDashletClass, 'icon' => $aInfo['icon']];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1417,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']);
|
||||
@@ -1571,7 +1660,7 @@ JS
|
||||
*/
|
||||
private function UpdateDashletUserPrefs(Dashlet $oDashlet, $sDashletIdOrig, array $aExtraParams)
|
||||
{
|
||||
$bIsDashletWithListPref = ($oDashlet instanceof DashletObjectList);
|
||||
$bIsDashletWithListPref = ($oDashlet instanceof DashletObjectList);
|
||||
if (!$bIsDashletWithListPref) {
|
||||
return;
|
||||
}
|
||||
@@ -1619,6 +1708,7 @@ JS
|
||||
//on error, return default value
|
||||
return null;
|
||||
}
|
||||
|
||||
return DataTableSettings::GetAppUserPreferenceKey($aClassAliases, $sDataTableId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ use Combodo\iTop\Application\WebPage\WebPage;
|
||||
*/
|
||||
abstract class DashboardLayout
|
||||
{
|
||||
abstract public function Render($oPage, $aDashlets, $bEditMode = false);
|
||||
abstract public function Render($oPage, $aDashlets, $bEditMode = false, array $aExtraParams = []);
|
||||
|
||||
/**
|
||||
* @param int $iCellIdx
|
||||
@@ -43,8 +43,8 @@ abstract class DashboardLayout
|
||||
public static function GetInfo()
|
||||
{
|
||||
return [
|
||||
'label' => '',
|
||||
'icon' => '',
|
||||
'label' => '',
|
||||
'icon' => '',
|
||||
'description' => '',
|
||||
];
|
||||
}
|
||||
@@ -74,6 +74,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
|
||||
}
|
||||
$idx++;
|
||||
}
|
||||
|
||||
return $aDashlets;
|
||||
}
|
||||
|
||||
@@ -94,6 +95,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
|
||||
}
|
||||
$idx++;
|
||||
}
|
||||
|
||||
return $aCells;
|
||||
|
||||
}
|
||||
@@ -109,7 +111,8 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
|
||||
// Trim the list of cells to remove the invisible/empty ones at the end of the array
|
||||
$aCells = $this->TrimCellsArray($aCells);
|
||||
|
||||
$oDashboardLayout = new DashboardLayoutUIBlock();
|
||||
// TODO 3.3 Handle dashboard new format, convert old format if needed
|
||||
$oDashboardLayout = new DashboardLayoutUIBlock($aExtraParams['dashboard_div_id']);
|
||||
//$oPage->AddUiBlock($oDashboardLayout);
|
||||
|
||||
$iCellIdx = 0;
|
||||
@@ -117,15 +120,16 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
|
||||
|
||||
//Js given by each dashlet to reload
|
||||
$sJSReload = "";
|
||||
|
||||
$oDashboardGrid = new \Combodo\iTop\Application\UI\Base\Layout\Dashboard\DashboardGrid();
|
||||
$oDashboardLayout->SetGrid($oDashboardGrid);
|
||||
for ($iRows = 0; $iRows < $iNbRows; $iRows++) {
|
||||
$oDashboardRow = new DashboardRow();
|
||||
$oDashboardLayout->AddDashboardRow($oDashboardRow);
|
||||
//$oDashboardLayout->AddDashboardRow($oDashboardRow);
|
||||
|
||||
for ($iCols = 0; $iCols < $this->iNbCols; $iCols++) {
|
||||
$oDashboardColumn = new DashboardColumn($bEditMode);
|
||||
$oDashboardColumn->SetCellIndex($iCellIdx);
|
||||
$oDashboardRow->AddDashboardColumn($oDashboardColumn);
|
||||
//$oDashboardRow->AddDashboardColumn($oDashboardColumn);
|
||||
|
||||
if (array_key_exists($iCellIdx, $aCells)) {
|
||||
$aDashlets = $aCells[$iCellIdx];
|
||||
@@ -133,6 +137,18 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
|
||||
/** @var \Dashlet $oDashlet */
|
||||
foreach ($aDashlets as $oDashlet) {
|
||||
if ($oDashlet::IsVisible()) {
|
||||
$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));
|
||||
}
|
||||
}
|
||||
@@ -147,6 +163,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
|
||||
$sJSReload .= $oDashboardRow->GetJSRefreshCallback()." ";
|
||||
}
|
||||
|
||||
// TODO 3.3 We can probably do better with the new dashboard
|
||||
$oPage->add_script("function updateDashboard".$aExtraParams['dashboard_div_id']."(){".$sJSReload."}");
|
||||
|
||||
if ($bEditMode) { // Add one row for extensibility
|
||||
@@ -168,8 +185,8 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
|
||||
*/
|
||||
public function GetDashletCoordinates($iCellIdx)
|
||||
{
|
||||
$iColNumber = (int) $iCellIdx % $this->iNbCols;
|
||||
$iRowNumber = (int) floor($iCellIdx / $this->iNbCols);
|
||||
$iColNumber = (int)$iCellIdx % $this->iNbCols;
|
||||
$iRowNumber = (int)floor($iCellIdx / $this->iNbCols);
|
||||
|
||||
return [$iColNumber, $iRowNumber];
|
||||
}
|
||||
@@ -182,11 +199,12 @@ class DashboardLayoutOneCol extends DashboardLayoutMultiCol
|
||||
parent::__construct();
|
||||
$this->iNbCols = 1;
|
||||
}
|
||||
|
||||
public static function GetInfo()
|
||||
{
|
||||
return [
|
||||
'label' => 'One Column',
|
||||
'icon' => 'images/layout_1col.png',
|
||||
'label' => 'One Column',
|
||||
'icon' => 'images/layout_1col.png',
|
||||
'description' => '',
|
||||
];
|
||||
}
|
||||
@@ -199,11 +217,12 @@ class DashboardLayoutTwoCols extends DashboardLayoutMultiCol
|
||||
parent::__construct();
|
||||
$this->iNbCols = 2;
|
||||
}
|
||||
|
||||
public static function GetInfo()
|
||||
{
|
||||
return [
|
||||
'label' => 'Two Columns',
|
||||
'icon' => 'images/layout_2col.png',
|
||||
'label' => 'Two Columns',
|
||||
'icon' => 'images/layout_2col.png',
|
||||
'description' => '',
|
||||
];
|
||||
}
|
||||
@@ -216,11 +235,12 @@ class DashboardLayoutThreeCols extends DashboardLayoutMultiCol
|
||||
parent::__construct();
|
||||
$this->iNbCols = 3;
|
||||
}
|
||||
|
||||
public static function GetInfo()
|
||||
{
|
||||
return [
|
||||
'label' => 'Two Columns',
|
||||
'icon' => 'images/layout_3col.png',
|
||||
'label' => 'Two Columns',
|
||||
'icon' => 'images/layout_3col.png',
|
||||
'description' => '',
|
||||
];
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@
|
||||
<class id="AbstractResource" _delta="define">
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<comment>/* Resource access control abstraction. Can be herited by abstract resource access control classes. Generally controlled using UR_ACTION_MODIFY access right. */</comment>
|
||||
<comment>/* Resource access control abstraction. Can be inherited by abstract resource access control classes. Generally controlled using UR_ACTION_MODIFY access right. */</comment>
|
||||
<abstract>true</abstract>
|
||||
</properties>
|
||||
<presentation/>
|
||||
@@ -851,9 +851,295 @@ Call $this->AddInitialAttributeFlags($sAttCode, $iFlags) for all the initial att
|
||||
</methods>
|
||||
</class>
|
||||
</classes>
|
||||
<dashlets>
|
||||
<dashlet id="DashletGroupByTable" _delta="define">
|
||||
<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>
|
||||
<property_types _delta="define">
|
||||
<property_type id="Dashboard" xsi:type="Combodo-AbstractPropertyType"/>
|
||||
<property_type id="DashboardGrid" xsi:type="Combodo-PropertyType">
|
||||
<extends>Dashboard</extends>
|
||||
<definition xsi:type="Combodo-ValueType-PropertyTree">
|
||||
<label>Dashboard</label>
|
||||
<nodes>
|
||||
<node id="title" xsi:type="Combodo-ValueType-String">
|
||||
<label>UI:DashboardEdit:DashboardTitle</label>
|
||||
</node>
|
||||
<node id="refresh" xsi:type="Combodo-ValueType-Choice"> <!-- Possible de le cacher, etc celui-ci nous met dedans -->
|
||||
<label>UI:DashboardEdit:AutoReload</label>
|
||||
<values>
|
||||
<value id="0">
|
||||
<label>No auto-refresh</label>
|
||||
</value>
|
||||
<value id="30">
|
||||
<label>Every 30 seconds</label>
|
||||
</value>
|
||||
<value id="60">
|
||||
<label>Every 1 minute</label>
|
||||
</value>
|
||||
<value id="300">
|
||||
<label>Every 5 minutes</label>
|
||||
</value>
|
||||
<value id="600">
|
||||
<label>Every 10 minutes</label>
|
||||
</value>
|
||||
<value id="1800">
|
||||
<label>Every 30 minutes</label>
|
||||
</value>
|
||||
<value id="3600">
|
||||
<label>Every 1 hour</label>
|
||||
</value>
|
||||
</values>
|
||||
</node>
|
||||
<node id="pos_dashlets" xsi:type="Combodo-ValueType-Collection">
|
||||
<label>Dashlet List</label>
|
||||
<xml-format xsi:type="Combodo-XMLFormat-CollectionWithId">
|
||||
<tag-name>pos_dashlet</tag-name>
|
||||
</xml-format>
|
||||
<prototype>
|
||||
<node id="position_x" xsi:type="Combodo-ValueType-Integer">
|
||||
<label>X</label>
|
||||
</node>
|
||||
<node id="position_y" xsi:type="Combodo-ValueType-Integer">
|
||||
<label>Y</label>
|
||||
</node>
|
||||
<node id="width" xsi:type="Combodo-ValueType-Integer">
|
||||
<label>W</label>
|
||||
</node>
|
||||
<node id="height" xsi:type="Combodo-ValueType-Integer">
|
||||
<label>H</label>
|
||||
</node>
|
||||
<node id="dashlet" xsi:type="Combodo-ValueType-Polymorphic">
|
||||
<label>Dashlet</label>
|
||||
<allowed-types>
|
||||
<allowed-type>Dashlet</allowed-type>
|
||||
</allowed-types>
|
||||
</node>
|
||||
</prototype>
|
||||
</node>
|
||||
</nodes>
|
||||
</definition>
|
||||
</property_type>
|
||||
<property_type id="Dashlet" xsi:type="Combodo-AbstractPropertyType"/>
|
||||
<property_type id="DashletGroupBy" xsi:type="Combodo-PropertyType">
|
||||
<property_type id="DashletGroupByTable" xsi:type="Combodo-PropertyType">
|
||||
<extends>Dashlet</extends>
|
||||
<definition xsi:type="Combodo-ValueType-PropertyTree">
|
||||
<label>UI:DashletGroupBy:Title</label>
|
||||
<nodes>
|
||||
<node id="title" xsi:type="Combodo-ValueType-Label">
|
||||
<label>UI:DashletGroupBy:Prop-Title</label>
|
||||
</node>
|
||||
<node id="query" xsi:type="Combodo-ValueType-OQL">
|
||||
<label>UI:DashletGroupBy:Prop-Query</label>
|
||||
</node>
|
||||
<node id="group_by" xsi:type="Combodo-ValueType-ClassAttributeGroupBy">
|
||||
<label>UI:DashletGroupBy:Prop-GroupBy</label>
|
||||
<class>{{query.selected_class}}</class>
|
||||
</node>
|
||||
<node id="style" xsi:type="Combodo-ValueType-Choice"> <!-- Possible de le cacher, etc celui-ci nous met dedans -->
|
||||
<label>UI:DashletGroupBy:Prop-Style</label>
|
||||
<values>
|
||||
<value id="bars">
|
||||
<label>UI:DashletGroupByBars:Label</label>
|
||||
</value>
|
||||
<value id="pie">
|
||||
<label>UI:DashletGroupByPie:Label</label>
|
||||
</value>
|
||||
<value id="table">
|
||||
<label>UI:DashletGroupByTable:Label</label>
|
||||
</value>
|
||||
</values>
|
||||
</node>
|
||||
<node id="aggregation_function" xsi:type="Combodo-ValueType-AggregateFunction">
|
||||
<label>UI:DashletGroupBy:Prop-Function</label>
|
||||
<class>{{query.selected_class}}</class> <!-- pour savoir si il y a des attributs additionnables -->
|
||||
</node>
|
||||
<node id="aggregation_attribute" xsi:type="Combodo-ValueType-ClassAttribute">
|
||||
<label>UI:DashletGroupBy:Prop-FunctionAttribute</label>
|
||||
<relevance-condition>{{aggregation_function.value != 'count'}}</relevance-condition>
|
||||
<class>{{query.selected_class}}</class>
|
||||
<category>numeric</category>
|
||||
</node>
|
||||
<node id="order_by" xsi:type="Combodo-ValueType-ChoiceFromInput">
|
||||
<label>UI:DashletGroupBy:Prop-OrderField</label>
|
||||
<values>
|
||||
<value id="attribute">
|
||||
<label>{{aggregation_attribute.label}}</label>
|
||||
</value>
|
||||
<value id="function">
|
||||
<label>{{aggregation_function.label}}</label>
|
||||
</value>
|
||||
</values>
|
||||
</node>
|
||||
<node id="limit" xsi:type="Combodo-ValueType-Integer">
|
||||
<label>UI:DashletGroupBy:Prop-Limit</label>
|
||||
<relevance-condition>{{order_by.value = 'function'}}</relevance-condition>
|
||||
</node>
|
||||
<node id="order_direction" xsi:type="Combodo-ValueType-Choice">
|
||||
<label>UI:DashletGroupBy:Prop-OrderDirection</label>
|
||||
<values>
|
||||
<value id="asc">
|
||||
<label>UI:DashletGroupBy:Order:asc</label>
|
||||
</value>
|
||||
<value id="desc">
|
||||
<label>UI:DashletGroupBy:Order:desc</label>
|
||||
</value>
|
||||
</values>
|
||||
</node>
|
||||
</nodes>
|
||||
</definition>
|
||||
</property_type>
|
||||
<property_type id="DashletGroupByBars" xsi:type="Combodo-PropertyType">
|
||||
<extends>Dashlet</extends>
|
||||
<definition xsi:type="Combodo-ValueType-PropertyTree">
|
||||
<label>UI:DashletGroupBy:Title</label>
|
||||
<nodes>
|
||||
<node id="title" xsi:type="Combodo-ValueType-Label">
|
||||
<label>UI:DashletGroupBy:Prop-Title</label>
|
||||
</node>
|
||||
<node id="query" xsi:type="Combodo-ValueType-OQL">
|
||||
<label>UI:DashletGroupBy:Prop-Query</label>
|
||||
</node>
|
||||
<node id="group_by" xsi:type="Combodo-ValueType-ClassAttributeGroupBy">
|
||||
<label>UI:DashletGroupBy:Prop-GroupBy</label>
|
||||
<class>{{query.selected_class}}</class>
|
||||
</node>
|
||||
<node id="style" xsi:type="Combodo-ValueType-Choice"> <!-- Possible de le cacher, etc celui-ci nous met dedans -->
|
||||
<label>UI:DashletGroupBy:Prop-Style</label>
|
||||
<values>
|
||||
<value id="bars">
|
||||
<label>UI:DashletGroupByBars:Label</label>
|
||||
</value>
|
||||
<value id="pie">
|
||||
<label>UI:DashletGroupByPie:Label</label>
|
||||
</value>
|
||||
<value id="table">
|
||||
<label>UI:DashletGroupByTable:Label</label>
|
||||
</value>
|
||||
</values>
|
||||
</node>
|
||||
<node id="aggregation_function" xsi:type="Combodo-ValueType-AggregateFunction">
|
||||
<label>UI:DashletGroupBy:Prop-Function</label>
|
||||
<class>{{query.selected_class}}</class> <!-- pour savoir si il y a des attributs additionnables -->
|
||||
</node>
|
||||
<node id="aggregation_attribute" xsi:type="Combodo-ValueType-ClassAttribute">
|
||||
<label>UI:DashletGroupBy:Prop-FunctionAttribute</label>
|
||||
<relevance-condition>{{aggregation_function.value != 'count'}}</relevance-condition>
|
||||
<class>{{query.selected_class}}</class>
|
||||
<category>numeric</category>
|
||||
</node>
|
||||
<node id="order_by" xsi:type="Combodo-ValueType-ChoiceFromInput">
|
||||
<label>UI:DashletGroupBy:Prop-OrderField</label>
|
||||
<values>
|
||||
<value id="attribute">
|
||||
<label>{{aggregation_attribute.label}}</label>
|
||||
</value>
|
||||
<value id="function">
|
||||
<label>{{aggregation_function.label}}</label>
|
||||
</value>
|
||||
</values>
|
||||
</node>
|
||||
<node id="limit" xsi:type="Combodo-ValueType-Integer">
|
||||
<label>UI:DashletGroupBy:Prop-Limit</label>
|
||||
<relevance-condition>{{order_by.value = 'function'}}</relevance-condition>
|
||||
</node>
|
||||
<node id="order_direction" xsi:type="Combodo-ValueType-Choice">
|
||||
<label>UI:DashletGroupBy:Prop-OrderDirection</label>
|
||||
<values>
|
||||
<value id="asc">
|
||||
<label>UI:DashletGroupBy:Order:asc</label>
|
||||
</value>
|
||||
<value id="desc">
|
||||
<label>UI:DashletGroupBy:Order:desc</label>
|
||||
</value>
|
||||
</values>
|
||||
</node>
|
||||
</nodes>
|
||||
</definition>
|
||||
</property_type>
|
||||
<property_type id="DashletGroupByPie" xsi:type="Combodo-PropertyType">
|
||||
<extends>Dashlet</extends>
|
||||
<definition xsi:type="Combodo-ValueType-PropertyTree">
|
||||
<label>UI:DashletGroupBy:Title</label>
|
||||
|
||||
@@ -726,10 +726,6 @@ class DisplayBlock
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->m_oFilter->IsAllDataAllowed() && ($aExtraParams['display_unauthorized_objects'] ?? false) === true) {
|
||||
$this->m_oFilter->AllowAllData();
|
||||
}
|
||||
|
||||
$aExtraParams['query_params'] = $this->m_oFilter->GetInternalParams();
|
||||
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, $aOrderBy, $aQueryParams);
|
||||
}
|
||||
@@ -1383,10 +1379,7 @@ JS
|
||||
|
||||
// Check the classes that can be read (i.e authorized) by this user...
|
||||
foreach ($aClasses as $sAlias => $sClassName) {
|
||||
if (
|
||||
(UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $this->m_oSet) !== UR_ALLOWED_NO)
|
||||
|| ($aExtraParams['display_unauthorized_objects'] ?? false) === true
|
||||
) {
|
||||
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $this->m_oSet) != UR_ALLOWED_NO) {
|
||||
$aAuthorizedClasses[$sAlias] = $sClassName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class ForgotPasswordUserInputException extends Exception
|
||||
{
|
||||
}
|
||||
@@ -1387,7 +1387,7 @@ class DesignerIconSelectionField extends DesignerFormField
|
||||
public function AddAllowedValue($aValue)
|
||||
{
|
||||
// Add a null value to the list of allowed values
|
||||
$this->aAllowedValues = array_merge([$aValue], $this->aAllowedValues ?? [null]);
|
||||
$this->aAllowedValues = array_merge([$aValue], $this->aAllowedValues);
|
||||
}
|
||||
public function EnableUpload($sIconUploadUrl)
|
||||
{
|
||||
|
||||
@@ -75,10 +75,13 @@ class LoginExternal extends AbstractLoginFSMExtension
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|mixed
|
||||
* @return bool
|
||||
*/
|
||||
private function GetAuthUser()
|
||||
{
|
||||
return MetaModel::GetConfig()->GetExternalAuthenticationVariable();
|
||||
$sExtAuthVar = MetaModel::GetConfig()->GetExternalAuthenticationVariable(); // In which variable is the info passed ?
|
||||
eval('$sAuthUser = isset('.$sExtAuthVar.') ? '.$sExtAuthVar.' : false;'); // Retrieve the value
|
||||
/** @var string $sAuthUser */
|
||||
return $sAuthUser; // Retrieve the value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,12 +243,15 @@ class LoginTwigRenderer
|
||||
|
||||
public function GetDefaultVars()
|
||||
{
|
||||
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION);
|
||||
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
|
||||
$sDisplayIcon = Branding::GetLoginLogoAbsoluteUrl();
|
||||
|
||||
$aVars = [
|
||||
'sAppRootUrl' => utils::GetAbsoluteUrlAppRoot(),
|
||||
'aPluginFormData' => $this->GetPluginFormData(),
|
||||
'sItopVersion' => ITOP_VERSION,
|
||||
'sVersionShort' => $sVersionShort,
|
||||
'sIconUrl' => $sIconUrl,
|
||||
'sDisplayIcon' => $sDisplayIcon,
|
||||
];
|
||||
|
||||
@@ -221,15 +221,15 @@ class LoginWebPage extends NiceWebPage
|
||||
|
||||
if ($oUser != null) {
|
||||
if (!MetaModel::IsValidAttCode(get_class($oUser), 'reset_pwd_token')) {
|
||||
throw new ForgotPasswordUserInputException('External accounts do not allow password reset');
|
||||
throw new Exception(Dict::S('UI:ResetPwd-Error-NotPossible'));
|
||||
}
|
||||
if (!$oUser->CanChangePassword()) {
|
||||
throw new ForgotPasswordUserInputException('The account does not allow password reset');
|
||||
throw new Exception(Dict::S('UI:ResetPwd-Error-FixedPwd'));
|
||||
}
|
||||
|
||||
$sTo = $oUser->GetResetPasswordEmail(); // throws Exceptions if not allowed
|
||||
if ($sTo == '') {
|
||||
throw new ForgotPasswordUserInputException('Missing email address for this account');
|
||||
throw new Exception(Dict::S('UI:ResetPwd-Error-NoEmail'));
|
||||
}
|
||||
|
||||
// This token allows the user to change the password without knowing the previous one
|
||||
@@ -255,21 +255,17 @@ class LoginWebPage extends NiceWebPage
|
||||
|
||||
case EMAIL_SEND_ERROR:
|
||||
default:
|
||||
throw new ForgotPasswordApplicationException('Failed to send the password reset email for '.$oUser->Get('friendlyname').': '.implode(', ', $aIssues));
|
||||
IssueLog::Error('Failed to send the email with the NEW password for '.$oUser->Get('friendlyname').': '.implode(', ', $aIssues));
|
||||
throw new Exception(Dict::S('UI:ResetPwd-Error-Send'));
|
||||
}
|
||||
}
|
||||
|
||||
} catch (ForgotPasswordApplicationException $e) {
|
||||
IssueLog::Error('Failed to process the forgot password request for user "'.$sAuthUser.'" [reason='.get_class($e).']: '.$e->getMessage());
|
||||
} catch (ForgotPasswordUserInputException $e) {
|
||||
IssueLog::Info('Failed to process the forgot password request for user "'.$sAuthUser.'" [reason='.get_class($e).']: '.$e->getMessage());
|
||||
} catch (\Throwable $e) {
|
||||
IssueLog::Error('Unexpected error while processing the forgot password request for user "'.$sAuthUser.'": '.$e->getMessage());
|
||||
$oTwigContext = new LoginTwigRenderer();
|
||||
$aVars = $oTwigContext->GetDefaultVars();
|
||||
$oTwigContext->Render($this, 'forgotpwdsent.html.twig', $aVars);
|
||||
} catch (Exception $e) {
|
||||
$this->DisplayForgotPwdForm(true, $e->getMessage());
|
||||
}
|
||||
|
||||
$oTwigContext = new LoginTwigRenderer();
|
||||
$aVars = $oTwigContext->GetDefaultVars();
|
||||
$oTwigContext->Render($this, 'forgotpwdsent.html.twig', $aVars);
|
||||
}
|
||||
|
||||
public function DisplayResetPwdForm($sErrorMessage = null)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\Helper\WebResourcesHelper;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\PageContent\PageContentFactory;
|
||||
use Combodo\iTop\Application\WebPage\ErrorPage;
|
||||
use Combodo\iTop\Application\WebPage\iTopWebPage;
|
||||
use Combodo\iTop\Application\WebPage\WebPage;
|
||||
@@ -1279,13 +1280,14 @@ class DashboardMenuNode extends MenuNode
|
||||
if ($oDashboard != null) {
|
||||
WebResourcesHelper::EnableC3JSToWebPage($oPage);
|
||||
|
||||
// TODO 3.3 this works for dashboard menu, what about other places ?
|
||||
$oPageLayout = PageContentFactory::MakeForDashboard();
|
||||
$oPage->SetContentLayout($oPageLayout, $oPage);
|
||||
$sDivId = utils::Sanitize($this->sMenuId, '', 'element_identifier');
|
||||
$oPage->add('<div id="'.$sDivId.'" class="ibo-dashboard" data-role="ibo-dashboard">');
|
||||
$aExtraParams['dashboard_div_id'] = $sDivId;
|
||||
$aExtraParams['from_dashboard_page'] = true;
|
||||
$oDashboard->SetReloadURL($this->GetHyperlink($aExtraParams));
|
||||
$oDashboard->Render($oPage, false, $aExtraParams);
|
||||
$oPage->add('</div>');
|
||||
|
||||
$bEdit = utils::ReadParam('edit', false);
|
||||
if ($bEdit) {
|
||||
@@ -1423,21 +1425,12 @@ class ShortcutMenuNode extends MenuNode
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
$sContext = $this->oShortcut->Get('context');
|
||||
try {
|
||||
$aContext = utils::Unserialize($sContext);
|
||||
if (isset($aContext['menu'])) {
|
||||
unset($aContext['menu']);
|
||||
}
|
||||
foreach ($aContext as $sArgName => $sArgValue) {
|
||||
$aExtraParams[$sArgName] = $sArgValue;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
IssueLog::Warning("User shortcut corrupted, delete the shortcut", LogChannels::CONSOLE, [
|
||||
'shortcut_name' => $this->oShortcut->GetName(),
|
||||
'root_cause' => $e->getMessage(),
|
||||
]);
|
||||
// delete the shortcut
|
||||
$this->oShortcut->DBDelete();
|
||||
$aContext = unserialize($sContext);
|
||||
if (isset($aContext['menu'])) {
|
||||
unset($aContext['menu']);
|
||||
}
|
||||
foreach ($aContext as $sArgName => $sArgValue) {
|
||||
$aExtraParams[$sArgName] = $sArgValue;
|
||||
}
|
||||
return parent::GetHyperlink($aExtraParams);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ interface iNewsroomProvider
|
||||
* @param User $oUser The user for who to check if the provider is applicable.
|
||||
* return bool
|
||||
*/
|
||||
public function IsApplicable(?User $oUser = null);
|
||||
public function IsApplicable(User $oUser = null);
|
||||
|
||||
/**
|
||||
* The human readable (localized) label for this provider
|
||||
@@ -139,7 +139,7 @@ abstract class NewsroomProviderBase implements iNewsroomProvider
|
||||
*/
|
||||
abstract public function GetViewAllURL();
|
||||
|
||||
public function IsApplicable(?User $oUser = null)
|
||||
public function IsApplicable(User $oUser = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ abstract class Query extends cmdbAbstractObject
|
||||
* @return string|null
|
||||
* @since 3.1.0
|
||||
*/
|
||||
abstract public function GetExportUrl(?array $aValues = null): ?string;
|
||||
abstract public function GetExportUrl(array $aValues = null): ?string;
|
||||
|
||||
/**
|
||||
* Update last export information.
|
||||
@@ -227,7 +227,7 @@ class QueryOQL extends Query
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function GetExportUrl(?array $aValues = null): ?string
|
||||
public function GetExportUrl(array $aValues = null): ?string
|
||||
{
|
||||
try {
|
||||
// retrieve attributes
|
||||
|
||||
@@ -924,6 +924,11 @@ CSS;
|
||||
public static function CloneThemeParameterAndIncludeVersion($aThemeParameters, $bSetupCompilationTimestamp, $aImportsPaths)
|
||||
{
|
||||
$aThemeParametersVariable = [];
|
||||
if (array_key_exists('variables', $aThemeParameters)) {
|
||||
if (is_array($aThemeParameters['variables'])) {
|
||||
$aThemeParametersVariable = array_merge([], $aThemeParameters['variables']);
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('variable_imports', $aThemeParameters)) {
|
||||
if (is_array($aThemeParameters['variable_imports'])) {
|
||||
@@ -931,14 +936,6 @@ CSS;
|
||||
}
|
||||
}
|
||||
|
||||
// Variables defined in theme XML have the priority over variables defined in XML imports files
|
||||
// They're defined after so they overwrite previous parameters
|
||||
if (array_key_exists('variables', $aThemeParameters)) {
|
||||
if (is_array($aThemeParameters['variables'])) {
|
||||
$aThemeParametersVariable = array_merge($aThemeParametersVariable, $aThemeParameters['variables']);
|
||||
}
|
||||
}
|
||||
|
||||
$aThemeParametersVariable['$version'] = $bSetupCompilationTimestamp;
|
||||
return $aThemeParametersVariable;
|
||||
}
|
||||
@@ -970,9 +967,7 @@ CSS;
|
||||
}
|
||||
}
|
||||
}
|
||||
array_map(function ($sVariableValue) {
|
||||
return ltrim($sVariableValue);
|
||||
}, $aVariablesResults);
|
||||
array_map(function ($sVariableValue) { return ltrim($sVariableValue); }, $aVariablesResults);
|
||||
return $aVariablesResults;
|
||||
}
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ JS
|
||||
<<<HTML
|
||||
<form id="ObjectsAddForm_{$this->sInputid}">
|
||||
<div id="SearchResultsToAdd_{$this->sInputid}">
|
||||
<div style="border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
|
||||
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
|
||||
</div>
|
||||
<input type="hidden" id="count_{$this->sInputid}" value="0"/>
|
||||
</form>
|
||||
|
||||
@@ -27,9 +27,6 @@ require_once(APPROOT.'/application/displayblock.class.inc.php');
|
||||
|
||||
class UISearchFormForeignKeys
|
||||
{
|
||||
private $m_sRemoteClass;
|
||||
private $m_iInputId;
|
||||
|
||||
public function __construct($sTargetClass, $iInputId = null)
|
||||
{
|
||||
$this->m_sRemoteClass = $sTargetClass;
|
||||
@@ -43,7 +40,7 @@ class UISearchFormForeignKeys
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function ShowModalSearchForeignKeys($oPage)
|
||||
public function ShowModalSearchForeignKeys($oPage, $sTitle)
|
||||
{
|
||||
|
||||
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
|
||||
@@ -63,17 +60,52 @@ class UISearchFormForeignKeys
|
||||
]
|
||||
));
|
||||
$sEmptyList = Dict::S('UI:Message:EmptyList:UseSearchForm');
|
||||
$sCancel = Dict::S('UI:Button:Cancel');
|
||||
$sAdd = Dict::S('UI:Button:Add');
|
||||
|
||||
$oPage->add(
|
||||
<<<HTML
|
||||
<form id="ObjectsAddForm_{$this->m_iInputId}">
|
||||
<div id="SearchResultsToAdd_{$this->m_iInputId}" style="vertical-align:top;height:100%;overflow:auto;padding:0;border:0;">
|
||||
<div style="border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
|
||||
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
|
||||
</div>
|
||||
<input type="hidden" id="count_{$this->m_iInputId}" value="0"/>
|
||||
</form>
|
||||
HTML
|
||||
);
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
$('#dlg_{$this->m_iInputId}').dialog({
|
||||
width: $(window).width()*0.8,
|
||||
height: $(window).height()*0.8,
|
||||
autoOpen: false,
|
||||
modal: true,
|
||||
resizeStop: oForeignKeysWidget{$this->m_iInputId}.UpdateSizes,
|
||||
buttons: [
|
||||
{
|
||||
text: Dict.S('UI:Button:Cancel'),
|
||||
class: "cancel ibo-is-alternative ibo-is-neutral",
|
||||
click: function() {
|
||||
$('#dlg_{$this->m_iInputId}').dialog('close');
|
||||
}
|
||||
},
|
||||
{
|
||||
text: Dict.S('UI:Button:Add'),
|
||||
id: 'btn_ok_{$this->m_iInputId}',
|
||||
class: "ok ibo-is-regular ibo-is-primary",
|
||||
click: function() {
|
||||
oForeignKeysWidget{$this->m_iInputId}.DoAddObjects(this.id);
|
||||
}
|
||||
},
|
||||
],
|
||||
|
||||
});
|
||||
$('#dlg_{$this->m_iInputId}').dialog('option', {title:'$sTitle'});
|
||||
$('#SearchFormToAdd_{$this->m_iInputId} form').on('submit.uilinksWizard', oForeignKeysWidget{$this->m_iInputId}.SearchObjectsToAdd);
|
||||
$('#SearchFormToAdd_{$this->m_iInputId}').on('resize', oForeignKeysWidget{$this->m_iInputId}.UpdateSizes);
|
||||
JS
|
||||
);
|
||||
}
|
||||
|
||||
public function GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter)
|
||||
@@ -87,4 +119,31 @@ HTML
|
||||
IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for objects to be linked to the current object (i.e "remote" objects)
|
||||
*
|
||||
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
|
||||
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of m_sRemoteClass
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function ListResultsSearchForeignKeys(WebPage $oP, $sRemoteClass = '')
|
||||
{
|
||||
if ($sRemoteClass != '') {
|
||||
// assert(MetaModel::IsParentClass($this->m_sRemoteClass, $sRemoteClass));
|
||||
$oFilter = new DBObjectSearch($sRemoteClass);
|
||||
} else {
|
||||
// No remote class specified use the one defined in the linkedset
|
||||
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
|
||||
}
|
||||
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$oBlock->Display(
|
||||
$oP,
|
||||
"ResultsToAdd_{$this->m_iInputId}",
|
||||
['menu' => false, 'cssCount' => "#count_{$this->m_iInputId}", 'selection_mode' => true, 'table_id' => "add_{$this->m_iInputId}"]
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -122,11 +122,6 @@ class utils
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_VARIABLE_NAME = 'variable_name';
|
||||
/**
|
||||
* @var string For module codes (e.g. `itop-portal-base`, `combodo-webhook-integration`, `some-module-code-x.y`, ...)
|
||||
* @since 3.2.3 3.3.0 N°8554
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_MODULE_CODE = 'module_code';
|
||||
/**
|
||||
* @var string
|
||||
* @since 2.7.10 3.0.0
|
||||
@@ -186,9 +181,6 @@ class utils
|
||||
|
||||
protected static function LoadParamFile($sParamFile)
|
||||
{
|
||||
if (utils::RealPath($sParamFile, APPROOT) !== false) {
|
||||
throw new Exception("File '".utils::HtmlEntities($sParamFile)."' should be outside iTop");
|
||||
}
|
||||
if (!file_exists($sParamFile)) {
|
||||
throw new Exception("Could not find the parameter file: '".utils::HtmlEntities($sParamFile)."'");
|
||||
}
|
||||
@@ -284,7 +276,9 @@ class utils
|
||||
}
|
||||
// Read and record the value for switching the archive mode
|
||||
$iCurrent = self::ReadParam('with-archive', $iDefault);
|
||||
Session::Set('archive_mode', $iCurrent);
|
||||
if (Session::IsInitialized()) {
|
||||
Session::Set('archive_mode', $iCurrent);
|
||||
}
|
||||
// Read and use the value for the current page (web services)
|
||||
$iCurrent = self::ReadParam('with_archive', $iCurrent, true);
|
||||
self::$bPageMode = ($iCurrent == 1);
|
||||
@@ -396,7 +390,6 @@ class utils
|
||||
* @since 2.7.10 N°6606 use the utils::ENUM_SANITIZATION_* const
|
||||
* @since 2.7.10 N°6606 new case for ENUM_SANITIZATION_FILTER_PHP_CLASS
|
||||
* @since 3.2.1-1 N°8242 Allow value to be an array for every filter
|
||||
* @since 3.2.3 3.3.0 N°8554 new case for ENUM_SANITIZATION_FILTER_MODULE_CODE
|
||||
*
|
||||
* @link https://www.php.net/manual/en/filter.filters.sanitize.php PHP sanitization filters
|
||||
*/
|
||||
@@ -484,7 +477,7 @@ class utils
|
||||
);
|
||||
break;
|
||||
|
||||
// For XML / HTML node selector
|
||||
// For XML / HTML node id selector
|
||||
case static::ENUM_SANITIZATION_FILTER_ELEMENT_SELECTOR:
|
||||
$retValue = filter_var(
|
||||
$value,
|
||||
@@ -497,15 +490,6 @@ class utils
|
||||
$retValue = preg_replace('/[^a-zA-Z0-9_]/', '', $value);
|
||||
break;
|
||||
|
||||
case static::ENUM_SANITIZATION_FILTER_MODULE_CODE:
|
||||
// Module codes allow all alphabets letters, numbers, dash and dot characters
|
||||
$retValue = filter_var(
|
||||
$value,
|
||||
FILTER_VALIDATE_REGEXP,
|
||||
['options' => ['regexp' => '/^[\p{L}\d.-]+$/u']]
|
||||
);
|
||||
break;
|
||||
|
||||
// For URL
|
||||
case static::ENUM_SANITIZATION_FILTER_URL:
|
||||
$retValue = filter_var($value, FILTER_SANITIZE_URL);
|
||||
@@ -974,7 +958,7 @@ class utils
|
||||
return self::$oConfig;
|
||||
}
|
||||
|
||||
$sProductionEnvConfigPath = self::GetConfigFilePath(ITOP_DEFAULT_ENV);
|
||||
$sProductionEnvConfigPath = self::GetConfigFilePath('production');
|
||||
if (file_exists($sProductionEnvConfigPath)) {
|
||||
$oProductionEnvDiskConfig = new Config($sProductionEnvConfigPath);
|
||||
self::SetConfig($oProductionEnvDiskConfig);
|
||||
@@ -1300,7 +1284,7 @@ class utils
|
||||
* @throws \CoreException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function ExecITopScript(string $sScriptName, array $aArguments, ?string $sAuthUser = null, ?string $sAuthPwd = null)
|
||||
public static function ExecITopScript(string $sScriptName, array $aArguments, string $sAuthUser = null, string $sAuthPwd = null)
|
||||
{
|
||||
$aDisabled = explode(', ', ini_get('disable_functions'));
|
||||
if (in_array('exec', $aDisabled)) {
|
||||
@@ -1390,7 +1374,7 @@ class utils
|
||||
* @return string A path to a folder into which any module can store cache data
|
||||
* The corresponding folder is created or cleaned upon code compilation
|
||||
*/
|
||||
public static function GetCachePath(?string $sEnvironment = null): string
|
||||
public static function GetCachePath(string $sEnvironment = null): string
|
||||
{
|
||||
if (is_null($sEnvironment)) {
|
||||
$sEnvironment = MetaModel::GetEnvironment();
|
||||
@@ -1453,12 +1437,6 @@ class utils
|
||||
|
||||
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
|
||||
/** @var \DBObjectSet $param */
|
||||
|
||||
// Check if the user has the right to read the objects of this list, otherwise do not propose any action (eg. configure this list, export, etc.)
|
||||
if (UserRights::IsActionAllowed($param->GetFilter()->GetClass(), UR_ACTION_READ, $param) !== UR_ALLOWED_YES) {
|
||||
break;
|
||||
}
|
||||
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink(true);
|
||||
$sDataTableId = is_null($sDataTableId) ? '' : $sDataTableId;
|
||||
@@ -2103,9 +2081,7 @@ SQL;
|
||||
}
|
||||
|
||||
// Remove any remaining nulls (for positions that weren't referenced)
|
||||
$aReplacements = array_filter($aReplacements, static function ($val) {
|
||||
return $val !== null;
|
||||
});
|
||||
$aReplacements = array_filter($aReplacements, static function ($val) { return $val !== null; });
|
||||
} else {
|
||||
// For non-positional, we need to map each position
|
||||
$aReplacements = [];
|
||||
@@ -3144,50 +3120,4 @@ TXT
|
||||
|
||||
return $aTrace;
|
||||
}
|
||||
|
||||
/**
|
||||
* PHP unserialize encapsulation, allow throwing exception when not allowed object class is detected (for security hardening)
|
||||
*
|
||||
* @param string $data data to unserialize
|
||||
* @param array $aOptions PHP @unserialise options
|
||||
* @param bool $bThrowNotAllowedObjectClassException flag to throw exception
|
||||
*
|
||||
* @return mixed PHP @unserialise return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function Unserialize(string $data, array $aOptions = ['allowed_classes' => false], bool $bThrowNotAllowedObjectClassException = true): mixed
|
||||
{
|
||||
$data = unserialize($data, $aOptions);
|
||||
|
||||
if ($bThrowNotAllowedObjectClassException) {
|
||||
try {
|
||||
self::AssertNoIncompleteClassDetected($data);
|
||||
} catch (Exception $e) {
|
||||
throw new CoreException('Unserialization failed because an incomplete class was detected.', [], '', $e);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that data provided doesn't contain any incomplete class.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function AssertNoIncompleteClassDetected(mixed $data): void
|
||||
{
|
||||
if (is_object($data)) {
|
||||
if ($data instanceof __PHP_Incomplete_Class) {
|
||||
throw new Exception('__PHP_Incomplete_Class_Name object detected');
|
||||
}
|
||||
foreach (get_object_vars($data) as $property) {
|
||||
self::AssertNoIncompleteClassDetected($property);
|
||||
}
|
||||
} elseif (is_array($data)) {
|
||||
foreach ($data as $value) {
|
||||
self::AssertNoIncompleteClassDetected($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "project",
|
||||
"license": "AGPL-3.0-only",
|
||||
"require": {
|
||||
"php": ">=8.1.0 <8.5.0",
|
||||
"php": ">=8.1.0 <8.4.0",
|
||||
"ext-ctype": "*",
|
||||
"ext-dom": "*",
|
||||
"ext-gd": "*",
|
||||
@@ -12,43 +12,38 @@
|
||||
"ext-json": "*",
|
||||
"ext-mysqli": "*",
|
||||
"ext-soap": "*",
|
||||
"apereo/phpcas": "dev-master",
|
||||
"apereo/phpcas": "~1.6.0",
|
||||
"firebase/php-jwt": "^6.4.0",
|
||||
"guzzlehttp/guzzle": "^7.5.1",
|
||||
"league/oauth2-google": "^4.0.1",
|
||||
"nikic/php-parser": "^4.14.0",
|
||||
"nikic/php-parser": "dev-master",
|
||||
"pear/archive_tar": "~1.4.14",
|
||||
"pelago/emogrifier": "^7.2.0",
|
||||
"psr/log": "^3.0.0",
|
||||
"scssphp/scssphp": "dev-combodo/1.x",
|
||||
"scssphp/scssphp": "^1.12.1",
|
||||
"soundasleep/html2text": "~2.1",
|
||||
"symfony/console": "~6.4.0",
|
||||
"symfony/dotenv": "~6.4.0",
|
||||
"symfony/form": "^6.4",
|
||||
"symfony/framework-bundle": "~6.4.0",
|
||||
"symfony/http-foundation": "~6.4.0",
|
||||
"symfony/http-kernel": "~6.4.0",
|
||||
"symfony/runtime": "~6.4.0",
|
||||
"symfony/mailer": "^6.4",
|
||||
"symfony/security-csrf": "^6.4",
|
||||
"symfony/twig-bundle": "~6.4.0",
|
||||
"symfony/var-dumper": "~6.4.0",
|
||||
"symfony/validator" : "^6.4",
|
||||
"symfony/yaml": "~6.4.0",
|
||||
"symfony/mailer": "~6.4.0",
|
||||
"tecnickcom/tcpdf": "^6.6.0",
|
||||
"thenetworg/oauth2-azure": "^2.0",
|
||||
"soundasleep/html2text": "~2.1"
|
||||
"thenetworg/oauth2-azure": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/debug-bundle": "~6.4.0",
|
||||
"symfony/stopwatch": "~6.4.0",
|
||||
"symfony/web-profiler-bundle": "~6.4.0"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/EsupPortail/phpCAS"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/combodo-itop-libs/scssphp"
|
||||
}
|
||||
],
|
||||
"repositories": [{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/Combodo/PHP-Parser"
|
||||
}],
|
||||
"suggest": {
|
||||
"ext-libsodium": "Required to use the AttributeEncryptedString.",
|
||||
"ext-openssl": "Can be used as a polyfill if libsodium is not installed",
|
||||
@@ -68,10 +63,7 @@
|
||||
},
|
||||
"sort-packages": true,
|
||||
"classmap-authoritative": true,
|
||||
"platform-check": true,
|
||||
"allow-plugins": {
|
||||
"symfony/runtime": true
|
||||
}
|
||||
"platform-check": true
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
@@ -80,7 +72,6 @@
|
||||
"sources"
|
||||
],
|
||||
"exclude-from-classmap": [
|
||||
"application/twigextension.class.inc.php",
|
||||
"core/oql/build/PHP/",
|
||||
"core/apc-emulation.php",
|
||||
"application/startup.inc.php",
|
||||
@@ -100,7 +91,7 @@
|
||||
"extra": {
|
||||
"symfony": {
|
||||
"allow-contrib": false,
|
||||
"require": "3.4.*"
|
||||
"require": "6.4.*"
|
||||
},
|
||||
"runtime": {
|
||||
"dotenv_path": "resources/symfony/.env"
|
||||
|
||||
1320
composer.lock
generated
1320
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -234,11 +234,10 @@ abstract class Action extends cmdbAbstractObject
|
||||
}
|
||||
|
||||
$oActionFilter = DBObjectSearch::FromOQL($sActionQueryOql, $aActionQueryParams);
|
||||
$oActionFilter->AllowAllData();
|
||||
$oSet = new DBObjectSet($oActionFilter, ['date' => false]);
|
||||
|
||||
$sPanelTitle = Dict::Format('Action:last_executions_tab_panel_title', $sActionQueryLimit);
|
||||
$oExecutionsListBlock = DataTableUIBlockFactory::MakeForResult($oPage, 'action_executions_list', $oSet, ['panel_title' => $sPanelTitle, 'display_unauthorized_objects' => true]);
|
||||
$oExecutionsListBlock = DataTableUIBlockFactory::MakeForResult($oPage, 'action_executions_list', $oSet, ['panel_title' => $sPanelTitle]);
|
||||
|
||||
$oPage->AddUiBlock($oExecutionsListBlock);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ MetaModel::IncludeModule('application/user.dashboard.class.inc.php');
|
||||
MetaModel::IncludeModule('application/audit.rule.class.inc.php');
|
||||
MetaModel::IncludeModule('application/audit.domain.class.inc.php');
|
||||
MetaModel::IncludeModule('application/query.class.inc.php');
|
||||
MetaModel::IncludeModule('setup/moduleinstallation/moduleinstallation.class.inc.php');
|
||||
MetaModel::IncludeModule('setup/moduleinstallation.class.inc.php');
|
||||
|
||||
MetaModel::IncludeModule('core/event.class.inc.php');
|
||||
MetaModel::IncludeModule('core/action.class.inc.php');
|
||||
|
||||
@@ -199,7 +199,7 @@ class CellStatus_SearchIssue extends CellStatus_Issue
|
||||
* @param null $sAllowedValues : used for additional message that provides allowed values $sAllowedValues for current class
|
||||
* @param string|null $sAllowedValuesSearch : used to search all allowed values
|
||||
*/
|
||||
public function __construct($sSerializedSearch, $sReason, $sClass = null, $sAllowedValues = null, ?string $sAllowedValuesSearch = null)
|
||||
public function __construct($sSerializedSearch, $sReason, $sClass = null, $sAllowedValues = null, string $sAllowedValuesSearch = null)
|
||||
{
|
||||
parent::__construct(null, null, $sReason);
|
||||
$this->sSerializedSearch = $sSerializedSearch;
|
||||
@@ -876,7 +876,7 @@ class BulkChange
|
||||
return $aResults;
|
||||
}
|
||||
|
||||
protected function CreateObject(&$aResult, $iRow, $aRowData, ?CMDBChange $oChange = null)
|
||||
protected function CreateObject(&$aResult, $iRow, $aRowData, CMDBChange $oChange = null)
|
||||
{
|
||||
$oTargetObj = MetaModel::NewObject($this->m_sClass);
|
||||
|
||||
@@ -965,7 +965,7 @@ class BulkChange
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
protected function UpdateObject(&$aResult, $iRow, $oTargetObj, $aRowData, ?CMDBChange $oChange = null)
|
||||
protected function UpdateObject(&$aResult, $iRow, $oTargetObj, $aRowData, CMDBChange $oChange = null)
|
||||
{
|
||||
$aResult[$iRow] = $this->PrepareObject($oTargetObj, $aRowData, $aErrors);
|
||||
|
||||
@@ -1008,7 +1008,7 @@ class BulkChange
|
||||
*
|
||||
* @throws \BulkChangeException
|
||||
*/
|
||||
protected function UpdateMissingObject(&$aResult, $iRow, $oTargetObj, ?CMDBChange $oChange = null)
|
||||
protected function UpdateMissingObject(&$aResult, $iRow, $oTargetObj, CMDBChange $oChange = null)
|
||||
{
|
||||
$aResult[$iRow] = $this->PrepareMissingObject($oTargetObj, $aErrors);
|
||||
|
||||
@@ -1043,7 +1043,7 @@ class BulkChange
|
||||
}
|
||||
}
|
||||
|
||||
public function Process(?CMDBChange $oChange = null)
|
||||
public function Process(CMDBChange $oChange = null)
|
||||
{
|
||||
if ($oChange) {
|
||||
CMDBObject::SetCurrentChange($oChange);
|
||||
|
||||
@@ -1579,8 +1579,6 @@ class CMDBSource
|
||||
if (static::GetDBVendor() === static::ENUM_DB_VENDOR_MYSQL) {
|
||||
//Mysql 5.7.0 and upper deprecated --ssl and uses --ssl-mode instead
|
||||
return version_compare(static::GetDBVersion(), '5.7.11', '>=');
|
||||
} elseif (static::GetDBVendor() === static::ENUM_DB_VENDOR_MARIADB) {
|
||||
return version_compare(static::GetDBVersion(), '10.2.6', '>=');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Config\Validator\iTopConfigAstValidator;
|
||||
use Combodo\iTop\Config\Validator\iTopConfigSyntaxValidator;
|
||||
|
||||
define('ITOP_APPLICATION', 'iTop');
|
||||
define('ITOP_APPLICATION_SHORT', 'iTop');
|
||||
|
||||
@@ -75,7 +78,6 @@ define('DEFAULT_EXT_AUTH_VARIABLE', '$_SERVER[\'REMOTE_USER\']');
|
||||
define('DEFAULT_ENCRYPTION_KEY', '@iT0pEncr1pti0n!'); // We'll use a random generated key later (if possible)
|
||||
define('DEFAULT_ENCRYPTION_LIB', 'Mcrypt'); // We'll define the best encryption available later
|
||||
define('DEFAULT_HASH_ALGO', PASSWORD_DEFAULT);
|
||||
|
||||
/**
|
||||
* Config
|
||||
* configuration data (this class cannot not be localized, because it is responsible for loading the dictionaries)
|
||||
@@ -869,14 +871,6 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'ext_auth_variable' => [
|
||||
'type' => 'string',
|
||||
'description' => 'External authentication expression (allowed: $_SERVER[\'key\'], $_COOKIE[\'key\'], $_REQUEST[\'key\'], getallheaders()[\'Header-Name\'])',
|
||||
'default' => '$_SERVER[\'REMOTE_USER\']',
|
||||
'value' => '$_SERVER[\'REMOTE_USER\']',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'login_debug' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Activate the login FSM debug',
|
||||
@@ -934,7 +928,7 @@ class Config
|
||||
'type' => 'string',
|
||||
'description' => 'Actions that are available as direct buttons next to the "Actions" menu',
|
||||
// examples... not used
|
||||
'default' => 'UI:Menu:Modify,UI:Menu:New,UI:Menu:impacts_down,UI:Menu:impacts_up',
|
||||
'default' => 'UI:Menu:Modify,UI:Menu:New',
|
||||
'value' => 'UI:Menu:Modify',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
@@ -1179,8 +1173,8 @@ class Config
|
||||
'tracking_level_linked_set_default' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Default tracking level if not explicitly set at the attribute level, for AttributeLinkedSet (defaults to NONE in case of a fresh install, LIST otherwise - this to preserve backward compatibility while upgrading from a version older than 2.0.3 - see TRAC #936)',
|
||||
'default' => LINKSET_TRACKING_NONE,
|
||||
'value' => LINKSET_TRACKING_NONE,
|
||||
'default' => LINKSET_TRACKING_LIST,
|
||||
'value' => LINKSET_TRACKING_LIST,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
@@ -1619,7 +1613,7 @@ class Config
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'search_manual_submit' => [
|
||||
'type' => 'bool',
|
||||
'type' => 'array',
|
||||
'description' => 'Force manual submit of search all requests',
|
||||
'default' => false,
|
||||
'value' => true,
|
||||
@@ -1746,14 +1740,6 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'security.disable_joined_classes_filter' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true, scope filters aren\'t applied to joined classes or union classes not directly listed in the SELECT clause.',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'security.hide_administrators' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true, non-administrator users will not be able to see the administrator accounts, the Administrator profile and the links between the administrator accounts and their profiles.',
|
||||
@@ -1762,14 +1748,6 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'security.disable_exec_forced_login_for_all_enpoints' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true, when no delegated authentication module is defined, no login will be forced on modules exec endpoints',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'behind_reverse_proxy' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true, then proxies custom header (X-Forwarded-*) are taken into account. Use only if the webserver is not publicly accessible (reachable only by the reverse proxy)',
|
||||
@@ -1976,6 +1954,16 @@ class Config
|
||||
*/
|
||||
protected $m_sDefaultLanguage;
|
||||
|
||||
/**
|
||||
* @var string Type of login process allowed: form|basic|url|external
|
||||
*/
|
||||
protected $m_sAllowedLoginTypes;
|
||||
|
||||
/**
|
||||
* @var string Name of the PHP variable in which external authentication information is passed by the web server
|
||||
*/
|
||||
protected $m_sExtAuthVariable;
|
||||
|
||||
/**
|
||||
* @var string Encryption key used for all attributes of type "encrypted string". Can be set to a random value
|
||||
* unless you want to import a database from another iTop instance, in which case you must use
|
||||
@@ -2044,6 +2032,8 @@ class Config
|
||||
$this->m_iFastReloadInterval = DEFAULT_FAST_RELOAD_INTERVAL;
|
||||
$this->m_bSecureConnectionRequired = DEFAULT_SECURE_CONNECTION_REQUIRED;
|
||||
$this->m_sDefaultLanguage = 'EN US';
|
||||
$this->m_sAllowedLoginTypes = DEFAULT_ALLOWED_LOGIN_TYPES;
|
||||
$this->m_sExtAuthVariable = DEFAULT_EXT_AUTH_VARIABLE;
|
||||
$this->m_aCharsets = [];
|
||||
$this->m_bQueryCacheEnabled = DEFAULT_QUERY_CACHE_ENABLED;
|
||||
$this->m_iPasswordHashAlgo = DEFAULT_HASH_ALGO;
|
||||
@@ -2189,6 +2179,8 @@ class Config
|
||||
$this->m_aModuleSettings = isset($MyModuleSettings) ? $MyModuleSettings : [];
|
||||
|
||||
$this->m_sDefaultLanguage = isset($MySettings['default_language']) ? trim($MySettings['default_language']) : 'EN US';
|
||||
$this->m_sAllowedLoginTypes = isset($MySettings['allowed_login_types']) ? trim($MySettings['allowed_login_types']) : DEFAULT_ALLOWED_LOGIN_TYPES;
|
||||
$this->m_sExtAuthVariable = isset($MySettings['ext_auth_variable']) ? trim($MySettings['ext_auth_variable']) : DEFAULT_EXT_AUTH_VARIABLE;
|
||||
$this->m_sEncryptionKey = isset($MySettings['encryption_key']) ? trim($MySettings['encryption_key']) : $this->m_sEncryptionKey;
|
||||
$this->m_sEncryptionLibrary = isset($MySettings['encryption_library']) ? trim($MySettings['encryption_library']) : $this->m_sEncryptionLibrary;
|
||||
$this->m_aCharsets = isset($MySettings['csv_import_charsets']) ? $MySettings['csv_import_charsets'] : [];
|
||||
@@ -2347,76 +2339,12 @@ class Config
|
||||
|
||||
public function GetAllowedLoginTypes()
|
||||
{
|
||||
return explode('|', $this->m_aSettings['allowed_login_types']['value']);
|
||||
return explode('|', $this->m_sAllowedLoginTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|mixed
|
||||
* @since 3.2.3 return the parsed value instead of an unsecured variable name
|
||||
*/
|
||||
public function GetExternalAuthenticationVariable()
|
||||
{
|
||||
$sExpression = $this->Get('ext_auth_variable');
|
||||
$aParsed = $this->ParseExternalAuthVariableExpression($sExpression);
|
||||
if ($aParsed === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sKey = $aParsed['key'];
|
||||
switch ($aParsed['type']) {
|
||||
case 'server':
|
||||
return $_SERVER[$sKey] ?? false;
|
||||
case 'cookie':
|
||||
return $_COOKIE[$sKey] ?? false;
|
||||
case 'request':
|
||||
return $_REQUEST[$sKey] ?? false;
|
||||
case 'header':
|
||||
if (!function_exists('getallheaders')) {
|
||||
return false;
|
||||
}
|
||||
$aHeaders = getallheaders();
|
||||
if (!is_array($aHeaders)) {
|
||||
return false;
|
||||
}
|
||||
return $aHeaders[$sKey] ?? false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sExpression
|
||||
* @return array|null
|
||||
*/
|
||||
private function ParseExternalAuthVariableExpression($sExpression)
|
||||
{
|
||||
// If it's a configuration parameter it's probably already trimmed, but just in case
|
||||
$sExpression = trim((string) $sExpression);
|
||||
if ($sExpression === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Match $_SERVER/$_COOKIE/$_REQUEST['key'] with optional whitespace and single/double quotes.
|
||||
if (preg_match('/^\$_(SERVER|COOKIE|REQUEST)\s*\[\s*(["\'])\s*([^"\']+)\2\s*\]\s*$/', $sExpression, $aMatches) === 1) {
|
||||
$sContext = strtoupper($aMatches[1]);
|
||||
$sKey = $aMatches[3];
|
||||
return [
|
||||
'type' => strtolower($sContext),
|
||||
'key' => $sKey,
|
||||
'normalized' => '$_'.$sContext.'[\''.$sKey.'\']',
|
||||
];
|
||||
}
|
||||
|
||||
// Match getallheaders()['Header-Name'] in a case-insensitive way.
|
||||
if (preg_match('/^getallheaders\(\)\s*\[\s*(["\'])\s*([^"\']+)\1\s*\]\s*$/i', $sExpression, $aMatches) === 1) {
|
||||
$sKey = $aMatches[2];
|
||||
return [
|
||||
'type' => 'header',
|
||||
'key' => $sKey,
|
||||
'normalized' => 'getallheaders()[\''.$sKey.'\']',
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
return $this->m_sExtAuthVariable;
|
||||
}
|
||||
|
||||
public function GetCSVImportCharsets()
|
||||
@@ -2489,6 +2417,7 @@ class Config
|
||||
|
||||
public function SetAllowedLoginTypes($aAllowedLoginTypes)
|
||||
{
|
||||
$this->m_sAllowedLoginTypes = implode('|', $aAllowedLoginTypes);
|
||||
$this->Set('allowed_login_types', implode('|', $aAllowedLoginTypes));
|
||||
}
|
||||
|
||||
@@ -2512,7 +2441,7 @@ class Config
|
||||
|
||||
public function SetExternalAuthenticationVariable($sExtAuthVariable)
|
||||
{
|
||||
$this->Set('ext_auth_variable', $sExtAuthVariable);
|
||||
$this->m_sExtAuthVariable = $sExtAuthVariable;
|
||||
}
|
||||
|
||||
public function SetEncryptionKey($sKey)
|
||||
@@ -2566,6 +2495,8 @@ class Config
|
||||
$aSettings['fast_reload_interval'] = $this->m_iFastReloadInterval;
|
||||
$aSettings['secure_connection_required'] = $this->m_bSecureConnectionRequired;
|
||||
$aSettings['default_language'] = $this->m_sDefaultLanguage;
|
||||
$aSettings['allowed_login_types'] = $this->m_sAllowedLoginTypes;
|
||||
$aSettings['ext_auth_variable'] = $this->m_sExtAuthVariable;
|
||||
$aSettings['encryption_key'] = $this->m_sEncryptionKey;
|
||||
$aSettings['encryption_library'] = $this->m_sEncryptionLibrary;
|
||||
$aSettings['csv_import_charsets'] = $this->m_aCharsets;
|
||||
@@ -2668,6 +2599,8 @@ class Config
|
||||
// Old fashioned remaining values
|
||||
$aOtherValues = [
|
||||
'default_language' => $this->m_sDefaultLanguage,
|
||||
'allowed_login_types' => $this->m_sAllowedLoginTypes,
|
||||
'ext_auth_variable' => $this->m_sExtAuthVariable,
|
||||
'encryption_key' => $this->m_sEncryptionKey,
|
||||
'encryption_library' => $this->m_sEncryptionLibrary,
|
||||
'csv_import_charsets' => $this->m_aCharsets,
|
||||
@@ -2752,13 +2685,14 @@ class Config
|
||||
*
|
||||
* @param array $aParamValues
|
||||
* @param ?string $sModulesDir
|
||||
* @param bool $bPreserveModuleSettings
|
||||
*
|
||||
* @return void The current object is modified directly
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function UpdateFromParams($aParamValues, $sModulesDir = null)
|
||||
public function UpdateFromParams($aParamValues, $sModulesDir = null, $bPreserveModuleSettings = false)
|
||||
{
|
||||
if (isset($aParamValues['application_path'])) {
|
||||
$this->Set('app_root_url', $aParamValues['application_path']);
|
||||
@@ -2806,10 +2740,7 @@ class Config
|
||||
} else {
|
||||
$aSelectedModules = null;
|
||||
}
|
||||
|
||||
if (! is_null($sModulesDir)) {
|
||||
$this->UpdateIncludes($sModulesDir, $aSelectedModules);
|
||||
}
|
||||
$this->UpdateIncludes($sModulesDir, $aSelectedModules);
|
||||
|
||||
if (isset($aParamValues['source_dir'])) {
|
||||
$this->Set('source_dir', $aParamValues['source_dir']);
|
||||
@@ -2827,13 +2758,17 @@ class Config
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function UpdateIncludes(string $sModulesDir, $aSelectedModules = null)
|
||||
public function UpdateIncludes($sModulesDir, $aSelectedModules = null)
|
||||
{
|
||||
if ($sModulesDir === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize the arrays below with default values for the application...
|
||||
$oEmptyConfig = new Config('dummy_file', false); // Do NOT load any config file, just set the default values
|
||||
$aAddOns = $oEmptyConfig->GetAddOns();
|
||||
|
||||
$aModules = ModuleDiscovery::GetModulesOrderedByDependencies([APPROOT.$sModulesDir]);
|
||||
$aModules = ModuleDiscovery::GetAvailableModules([APPROOT.$sModulesDir]);
|
||||
foreach ($aModules as $sModuleId => $aModuleInfo) {
|
||||
list($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
|
||||
if (is_null($aSelectedModules) || in_array($sModuleName, $aSelectedModules)) {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\Helper\ExportHelper;
|
||||
use Combodo\iTop\Application\UI\Base\Component\FieldSet\FieldSetUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
|
||||
@@ -14,6 +13,7 @@ use Combodo\iTop\Application\UI\Base\Component\Input\Select\SelectUIBlockFactory
|
||||
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column\ColumnUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\MultiColumnUIBlockFactory;
|
||||
use Combodo\iTop\Application\Helper\ExportHelper;
|
||||
use Combodo\iTop\Application\WebPage\Page;
|
||||
use Combodo\iTop\Application\WebPage\WebPage;
|
||||
|
||||
@@ -55,8 +55,6 @@ class CSVBulkExport extends TabularBulkExport
|
||||
$this->aStatusInfo['charset'] = strtoupper(utils::ReadParam('charset', 'UTF-8', true, 'raw_data'));
|
||||
$this->aStatusInfo['formatted_text'] = (bool)utils::ReadParam('formatted_text', 0, true);
|
||||
|
||||
$this->aStatusInfo['ignore_excel_sanitization'] = (bool)utils::ReadParam('ignore_excel_sanitization', 0, true, utils::ENUM_SANITIZATION_FILTER_INTEGER);
|
||||
|
||||
$sDateFormatRadio = utils::ReadParam('csv_date_format_radio', '');
|
||||
switch ($sDateFormatRadio) {
|
||||
case 'default':
|
||||
@@ -225,10 +223,6 @@ class CSVBulkExport extends TabularBulkExport
|
||||
$oRadioCustom->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
$oFieldSetDate->AddSubBlock($oRadioCustom);
|
||||
|
||||
$oFieldSetSecurity = FieldSetUIBlockFactory::MakeStandard(Dict::S('Core:BulkExport:Security'));
|
||||
$oMulticolumn->AddColumn(ColumnUIBlockFactory::MakeForBlock($oFieldSetSecurity));
|
||||
$oFieldSetSecurity->AddSubBlock(ExportHelper::GetInputForSanitizeExcelExport());
|
||||
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#form_part_csv_options').on('preview_updated', function() { FormatDatesInPreview('csv', 'csv'); });
|
||||
@@ -270,13 +264,6 @@ EOF
|
||||
default:
|
||||
$sRet = trim($oObj->GetAsCSV($sAttCode), '"');
|
||||
}
|
||||
|
||||
// If the option to ignore Excel sanitization is not set or explicitly set to false, apply sanitization
|
||||
if (!(array_key_exists('ignore_excel_sanitization', $this->aStatusInfo)) || $this->aStatusInfo['ignore_excel_sanitization'] === false) {
|
||||
return ExportHelper::SanitizeField($sRet, $this->aStatusInfo['text_qualifier'] ?? '');
|
||||
}
|
||||
|
||||
// The option to ignore Excel sanitization is explicitly set to true: return the raw value without sanitization
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
@@ -350,12 +337,6 @@ EOF
|
||||
$sField = $oObj->GetAsCSV($sAttCode, $this->aStatusInfo['separator'], $this->aStatusInfo['text_qualifier'], $this->bLocalizeOutput, !$this->aStatusInfo['formatted_text']);
|
||||
}
|
||||
}
|
||||
|
||||
// If the option to ignore Excel sanitization is not set or absent, sanitize the field
|
||||
if (!(array_key_exists('ignore_excel_sanitization', $this->aStatusInfo)) || $this->aStatusInfo['ignore_excel_sanitization'] === false) {
|
||||
$sField = ExportHelper::SanitizeField($sField, $this->aStatusInfo['text_qualifier']);
|
||||
}
|
||||
|
||||
if ($this->aStatusInfo['charset'] != 'UTF-8') {
|
||||
// Note: due to bugs in the glibc library it's safer to call iconv on the smallest possible string
|
||||
// and thus to convert field by field and not the whole row or file at once (see ticket N°991)
|
||||
|
||||
@@ -425,7 +425,7 @@
|
||||
</php_parent>
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>core/cmdb,grant_by_profile,silo</category>
|
||||
<category>core/cmdb,view_in_gui</category>
|
||||
<abstract>false</abstract>
|
||||
<key_type>autoincrement</key_type>
|
||||
<db_table>priv_event_newsroom</db_table>
|
||||
@@ -904,7 +904,7 @@
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>Event</parent>
|
||||
<properties>
|
||||
<category>core/cmdb,grant_by_profile,silo</category>
|
||||
<category>core/cmdb,view_in_gui</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="message" xsi:type="AttributeText"/>
|
||||
|
||||
@@ -2567,7 +2567,7 @@ abstract class DBObject implements iDisplay
|
||||
*
|
||||
* @see \RestUtils::FindObjectFromKey for the same check in the REST endpoint
|
||||
*/
|
||||
final public function CheckChangedExtKeysValues(?callable $oIsObjectLoadableCallback = null)
|
||||
final public function CheckChangedExtKeysValues(callable $oIsObjectLoadableCallback = null)
|
||||
{
|
||||
if (is_null($oIsObjectLoadableCallback)) {
|
||||
$oIsObjectLoadableCallback = function ($sClass, $sId) {
|
||||
@@ -3727,7 +3727,7 @@ abstract class DBObject implements iDisplay
|
||||
* @throws \MySQLException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
private function ActivateOnObjectUpdateTriggers(?DBObject $oObject, ?array $aAttributes = null): void
|
||||
private function ActivateOnObjectUpdateTriggers(?DBObject $oObject, array $aAttributes = null): void
|
||||
{
|
||||
if (is_null($oObject)) {
|
||||
return;
|
||||
@@ -5108,8 +5108,8 @@ abstract class DBObject implements iDisplay
|
||||
protected function GetReferencingObjectsForDeletion($bAllowAllData = false)
|
||||
{
|
||||
$aDependentObjects = [];
|
||||
$aReferencingMe = MetaModel::EnumReferencingClasses(get_class($this));
|
||||
foreach ($aReferencingMe as $sRemoteClass => $aExtKeys) {
|
||||
$aRererencingMe = MetaModel::EnumReferencingClasses(get_class($this));
|
||||
foreach ($aRererencingMe as $sRemoteClass => $aExtKeys) {
|
||||
/** @var \AttributeExternalKey $oExtKeyAttDef */
|
||||
foreach ($aExtKeys as $sExtKeyAttCode => $oExtKeyAttDef) {
|
||||
// skip if external key doesn't require the deletion cascading
|
||||
|
||||
@@ -1932,37 +1932,4 @@ class DBObjectSearch extends DBSearch
|
||||
{
|
||||
return $this->GetCriteria()->ListParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return DBObjectSearch
|
||||
*/
|
||||
protected function ApplyDataFilters(): DBObjectSearch
|
||||
{
|
||||
if ($this->IsAllDataAllowed() || $this->IsDataFiltered()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$oSearch = $this;
|
||||
$aClassesToFilter = $this->GetSelectedClasses();
|
||||
|
||||
// Opt-in for joined classes filtering, otherwise only filter the selected class(es)
|
||||
if (MetaModel::GetConfig()->Get('security.disable_joined_classes_filter') === false) {
|
||||
$aClassesToFilter = $this->GetJoinedClasses();
|
||||
}
|
||||
|
||||
// Apply filter (this is similar to the one in DBSearch but the factorization could make it less readable)
|
||||
foreach ($aClassesToFilter as $sClassAlias => $sClass) {
|
||||
$oVisibleObjects = UserRights::GetSelectFilter($sClass, $this->GetModifierProperties('UserRightsGetSelectFilter'));
|
||||
if ($oVisibleObjects === false) {
|
||||
$oVisibleObjects = DBObjectSearch::FromEmptySet($sClass);
|
||||
}
|
||||
if (is_object($oVisibleObjects)) {
|
||||
$oVisibleObjects->AllowAllData();
|
||||
$oSearch = $oSearch->Filter($sClassAlias, $oVisibleObjects);
|
||||
$oSearch->SetDataFiltered();
|
||||
}
|
||||
}
|
||||
return $oSearch;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -650,7 +650,7 @@ abstract class DBSearch
|
||||
*
|
||||
* @throws OQLException
|
||||
*/
|
||||
public static function FromOQL($sQuery, $aParams = null, ?ModelReflection $oMetaModel = null)
|
||||
public static function FromOQL($sQuery, $aParams = null, ModelReflection $oMetaModel = null)
|
||||
{
|
||||
if (empty($sQuery)) {
|
||||
return null;
|
||||
@@ -1048,7 +1048,21 @@ abstract class DBSearch
|
||||
*/
|
||||
protected function GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $aGroupByExpr = null, $aSelectExpr = null)
|
||||
{
|
||||
$oSearch = $this->ApplyDataFilters();
|
||||
$oSearch = $this;
|
||||
if (!$this->IsAllDataAllowed() && !$this->IsDataFiltered()) {
|
||||
foreach ($this->GetSelectedClasses() as $sClassAlias => $sClass) {
|
||||
$oVisibleObjects = UserRights::GetSelectFilter($sClass, $this->GetModifierProperties('UserRightsGetSelectFilter'));
|
||||
if ($oVisibleObjects === false) {
|
||||
// Make sure this is a valid search object, saying NO for all
|
||||
$oVisibleObjects = DBObjectSearch::FromEmptySet($sClass);
|
||||
}
|
||||
if (is_object($oVisibleObjects)) {
|
||||
$oVisibleObjects->AllowAllData();
|
||||
$oSearch = $oSearch->Filter($sClassAlias, $oVisibleObjects);
|
||||
$oSearch->SetDataFiltered();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($aGroupByExpr)) {
|
||||
foreach ($aGroupByExpr as $sAlias => $oGroupByExp) {
|
||||
@@ -1510,33 +1524,4 @@ abstract class DBSearch
|
||||
* @return array{\VariableExpression}
|
||||
*/
|
||||
abstract public function GetExpectedArguments(): array;
|
||||
|
||||
/**
|
||||
* Apply data filters to the search, if needed
|
||||
*
|
||||
* @return DBSearch
|
||||
* @throws CoreException
|
||||
*/
|
||||
protected function ApplyDataFilters(): DBSearch
|
||||
{
|
||||
if ($this->IsAllDataAllowed() || $this->IsDataFiltered()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$oSearch = $this;
|
||||
$aClassesToFilter = $this->GetSelectedClasses();
|
||||
|
||||
foreach ($aClassesToFilter as $sClassAlias => $sClass) {
|
||||
$oVisibleObjects = UserRights::GetSelectFilter($sClass, $this->GetModifierProperties('UserRightsGetSelectFilter'));
|
||||
if ($oVisibleObjects === false) {
|
||||
$oVisibleObjects = DBObjectSearch::FromEmptySet($sClass);
|
||||
}
|
||||
if (is_object($oVisibleObjects)) {
|
||||
$oVisibleObjects->AllowAllData();
|
||||
$oSearch = $oSearch->Filter($sClassAlias, $oVisibleObjects);
|
||||
$oSearch->SetDataFiltered();
|
||||
}
|
||||
}
|
||||
return $oSearch;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ class DBUnionSearch extends DBSearch
|
||||
public function AllowAllData($bAllowAllData = true)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch) {
|
||||
$oSearch->AllowAllData($bAllowAllData);
|
||||
$oSearch->AllowAllData();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,30 +673,4 @@ class DBUnionSearch extends DBSearch
|
||||
|
||||
return $aVariableCriteria;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return DBUnionSearch
|
||||
*/
|
||||
protected function ApplyDataFilters(): DBUnionSearch
|
||||
{
|
||||
if ($this->IsAllDataAllowed() || $this->IsDataFiltered()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Opt-in for joined classes filtering, otherwise fallback on DBSearch filtering
|
||||
if (MetaModel::GetConfig()->Get('security.disable_joined_classes_filter') === true) {
|
||||
return parent::ApplyDataFilters();
|
||||
}
|
||||
|
||||
// Apply filters per sub-search
|
||||
$aFilteredSearches = [];
|
||||
foreach ($this->GetSearches() as $oSubSearch) {
|
||||
// Recursively call ApplyDataFilters on sub-searches
|
||||
$aFilteredSearches[] = $oSubSearch->ApplyDataFilters();
|
||||
}
|
||||
|
||||
$oSearch = new DBUnionSearch($aFilteredSearches);
|
||||
return $oSearch;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,10 +360,10 @@ class DesignElement extends \DOMElement
|
||||
* @param string $sTagName
|
||||
* @param string|null $sDefault
|
||||
*
|
||||
* @return null|string
|
||||
* @return string
|
||||
* @throws \DOMFormatException
|
||||
*/
|
||||
public function GetChildText($sTagName, $sDefault = null): ?string
|
||||
public function GetChildText($sTagName, $sDefault = null)
|
||||
{
|
||||
$sRet = $sDefault;
|
||||
if ($oChild = $this->GetOptionalElement($sTagName)) {
|
||||
@@ -453,7 +453,7 @@ class DesignElement extends \DOMElement
|
||||
* @throws Exception
|
||||
* @since 3.1.2 3.2.0 N°6974
|
||||
*/
|
||||
public static function _FindNode(DOMNode $oParent, DesignElement $oRefNode, ?string $sSearchId = null): ?DesignElement
|
||||
public static function _FindNode(DOMNode $oParent, DesignElement $oRefNode, string $sSearchId = null): ?DesignElement
|
||||
{
|
||||
$oNodes = self::_FindNodes($oParent, $oRefNode, $sSearchId);
|
||||
if ($oNodes instanceof DOMNodeList) {
|
||||
@@ -477,7 +477,7 @@ class DesignElement extends \DOMElement
|
||||
* @return \DOMNodeList|false|mixed
|
||||
* @since 3.1.2 3.2.0 N°6974
|
||||
*/
|
||||
public static function _FindNodes(DOMNode $oParent, DesignElement $oRefNode, ?string $sSearchId = null)
|
||||
public static function _FindNodes(DOMNode $oParent, DesignElement $oRefNode, string $sSearchId = null)
|
||||
{
|
||||
if ($oParent instanceof DOMDocument) {
|
||||
$oDoc = $oParent->firstChild->ownerDocument;
|
||||
|
||||
@@ -632,7 +632,7 @@ class DisplayableGroupNode extends DisplayableNode
|
||||
$this->aObjects = [];
|
||||
}
|
||||
|
||||
public function AddObject(?DBObject $oObj = null)
|
||||
public function AddObject(DBObject $oObj = null)
|
||||
{
|
||||
if (is_object($oObj)) {
|
||||
$sPrevClass = $this->GetObjectClass();
|
||||
|
||||
@@ -26,7 +26,7 @@ class Event extends DBObject implements iDisplay
|
||||
{
|
||||
$aParams =
|
||||
[
|
||||
"category" => "core/cmdb,grant_by_profile,silo",
|
||||
"category" => "core/cmdb,view_in_gui",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
@@ -120,7 +120,7 @@ class EventNotification extends Event
|
||||
{
|
||||
$aParams =
|
||||
[
|
||||
"category" => "core/cmdb,grant_by_profile,silo",
|
||||
"category" => "core/cmdb,view_in_gui",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
@@ -156,7 +156,7 @@ class EventNotificationEmail extends EventNotification
|
||||
{
|
||||
$aParams =
|
||||
[
|
||||
"category" => "core/cmdb,grant_by_profile,silo",
|
||||
"category" => "core/cmdb,view_in_gui",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
@@ -192,7 +192,7 @@ class EventIssue extends Event
|
||||
{
|
||||
$aParams =
|
||||
[
|
||||
"category" => "core/cmdb,grant_by_profile,silo",
|
||||
"category" => "core/cmdb,view_in_gui",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
@@ -286,7 +286,7 @@ class EventWebService extends Event
|
||||
{
|
||||
$aParams =
|
||||
[
|
||||
"category" => "core/cmdb,grant_by_profile,silo",
|
||||
"category" => "core/cmdb,view_in_gui",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
@@ -321,7 +321,7 @@ class EventRestService extends Event
|
||||
{
|
||||
$aParams =
|
||||
[
|
||||
"category" => "core/cmdb,grant_by_profile,silo",
|
||||
"category" => "core/cmdb,view_in_gui",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
@@ -356,7 +356,7 @@ class EventLoginUsage extends Event
|
||||
{
|
||||
$aParams =
|
||||
[
|
||||
"category" => "core/cmdb,grant_by_profile,silo",
|
||||
"category" => "core/cmdb,view_in_gui",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
@@ -394,7 +394,7 @@ class EventOnObject extends Event
|
||||
{
|
||||
$aParams =
|
||||
[
|
||||
"category" => "core/cmdb,grant_by_profile,silo",
|
||||
"category" => "core/cmdb,view_in_gui",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\Helper\ExportHelper;
|
||||
use Combodo\iTop\Application\UI\Base\Component\FieldSet\FieldSetUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column\ColumnUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\MultiColumnUIBlockFactory;
|
||||
use Combodo\iTop\Application\Helper\ExportHelper;
|
||||
use Combodo\iTop\Application\WebPage\Page;
|
||||
use Combodo\iTop\Application\WebPage\WebPage;
|
||||
|
||||
@@ -63,8 +63,6 @@ class ExcelBulkExport extends TabularBulkExport
|
||||
// Export from the command line (or scripted) => default format is SQL, as in previous versions of iTop, unless specified otherwise
|
||||
$this->aStatusInfo['date_format'] = utils::ReadParam('date_format', (string)AttributeDateTime::GetSQLFormat(), true, 'raw_data');
|
||||
}
|
||||
|
||||
$this->aStatusInfo['ignore_excel_sanitization'] = (bool)utils::ReadParam('ignore_excel_sanitization', 0, true, utils::ENUM_SANITIZATION_FILTER_INTEGER);
|
||||
}
|
||||
|
||||
public function EnumFormParts()
|
||||
@@ -123,10 +121,6 @@ class ExcelBulkExport extends TabularBulkExport
|
||||
$oRadioCustom->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
$oFieldSetDate->AddSubBlock($oRadioCustom);
|
||||
|
||||
$oFieldSetSecurity = FieldSetUIBlockFactory::MakeStandard(Dict::S('Core:BulkExport:Security'));
|
||||
$oMulticolumn->AddColumn(ColumnUIBlockFactory::MakeForBlock($oFieldSetSecurity));
|
||||
$oFieldSetSecurity->AddSubBlock(ExportHelper::GetInputForSanitizeExcelExport());
|
||||
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#form_part_xlsx_options').on('preview_updated', function() { FormatDatesInPreview('excel', 'xlsx'); });
|
||||
@@ -222,12 +216,6 @@ EOF
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the option to ignore Excel sanitization is not set or absent, sanitize the field
|
||||
if (!(array_key_exists('ignore_excel_sanitization', $this->aStatusInfo)) || $this->aStatusInfo['ignore_excel_sanitization'] === false) {
|
||||
return ExportHelper::SanitizeField($sRet, '');
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
|
||||
@@ -259,18 +259,13 @@ class InlineImage extends DBObject
|
||||
* that refer to an InlineImage (detected via the attribute data-img-id="") so that
|
||||
* the URL is consistent with the current URL of the application.
|
||||
*
|
||||
N°8681 * @param string|null $sHtml The HTML fragment to process
|
||||
* @param string $sHtml The HTML fragment to process
|
||||
*
|
||||
* @return string The modified HTML
|
||||
* @throws \Exception
|
||||
*
|
||||
* @since 3.3.0 N°8681 Add type hint for parameters and return value
|
||||
*/
|
||||
public static function FixUrls(string|null $sHtml): string
|
||||
public static function FixUrls($sHtml)
|
||||
{
|
||||
// N°8681 - Ensure to have a string value
|
||||
$sHtml = $sHtml ?? '';
|
||||
|
||||
$aNeedles = [];
|
||||
$aReplacements = [];
|
||||
// Find img tags with an attribute data-img-id
|
||||
@@ -298,46 +293,6 @@ N°8681 * @param string|null $sHtml The HTML fragment to process
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace <img> tags with a data-img-id attribute by the actual image in base64 representation
|
||||
* so that the image can be displayed even if the download URL is not accessible (e.g. in unauthenticated approval templates)
|
||||
*
|
||||
* @param string $sHtml The HTML fragment to process
|
||||
*
|
||||
* @return String The modified HTML
|
||||
* @since 3.2.3
|
||||
*/
|
||||
public static function ReplaceInlineImagesWithBase64Representation(string $sHtml): String
|
||||
{
|
||||
return preg_replace_callback(
|
||||
'/<img\s+[^>]*'.static::DOM_ATTR_ID.'="(\d+)"[^>]*>/i',
|
||||
function ($matches) {
|
||||
|
||||
// Extract inline image ID from the tag
|
||||
$id = $matches[1];
|
||||
|
||||
try {
|
||||
// Retrieve inline image
|
||||
$oInline = MetaModel::GetObject(InlineImage::class, $id, true, true);
|
||||
$oOrmDocument = $oInline->Get('contents');
|
||||
|
||||
// Replace src image by the base64 representation
|
||||
$sInlineImageAsBase64 = base64_encode($oOrmDocument->GetData());
|
||||
$sDataUri = 'data:'.$oOrmDocument->GetMimeType().';base64,'.$sInlineImageAsBase64;
|
||||
$sImage = preg_replace('/src=["\'][^"\']+["\']/', 'src="'.$sDataUri.'"', $matches[0]);
|
||||
|
||||
// Remove sensitive information (the image ID and secret) from the tag
|
||||
$sImage = preg_replace('/'.static::DOM_ATTR_ID.'="\d+"\s+'.static::DOM_ATTR_SECRET.'="\w+"/', '', $sImage);
|
||||
} catch (Exception $e) {
|
||||
$sImage = '<img src="" alt="'.Dict::S('UI:MissingInlineImage').'">';
|
||||
}
|
||||
|
||||
return $sImage;
|
||||
},
|
||||
$sHtml
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an extra attribute data-img-id for images which are based on an actual InlineImage
|
||||
* so that we can later reconstruct the full "src" URL when needed
|
||||
@@ -434,7 +389,7 @@ JS
|
||||
* Resize an image so that it fits the maximum width/height defined in the config file
|
||||
* @param ormDocument $oImage The original image stored as an array (content / mimetype / filename)
|
||||
* @return ormDocument The resampled image (or the original one if it already fit)
|
||||
* @deprecated since 3.3.0 Replaced by ormDocument::ResizeImageToFit
|
||||
* @deprecated Replaced by ormDocument::ResizeImageToFit
|
||||
*/
|
||||
public static function ResizeImageToFit(ormDocument $oImage, &$aDimensions = null)
|
||||
{
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\EventRegister\ApplicationEvents;
|
||||
use Combodo\iTop\Core\Kpi\KpiLogData;
|
||||
use Combodo\iTop\Service\Events\EventService;
|
||||
use Combodo\iTop\Service\Events\iEventServiceSetup;
|
||||
use Combodo\iTop\Service\Module\ModuleService;
|
||||
|
||||
/**
|
||||
* Measures operations duration, memory usage, etc. (and some other KPIs)
|
||||
*/
|
||||
class ExecutionKPI implements iEventServiceSetup
|
||||
|
||||
class ExecutionKPI
|
||||
{
|
||||
protected static $m_bEnabled_Duration = false;
|
||||
protected static $m_bEnabled_Memory = false;
|
||||
@@ -25,18 +23,15 @@ class ExecutionKPI implements iEventServiceSetup
|
||||
|
||||
protected static $m_aStats = []; // Recurrent operations
|
||||
protected static $m_aExecData = []; // One shot operations
|
||||
/** @var true */
|
||||
private static bool $bMetamodelStarted = false;
|
||||
/**
|
||||
* @var array[ExecutionKPI]
|
||||
*/
|
||||
protected static $m_aExecutionStack = []; // embedded execution stats
|
||||
|
||||
private static ?float $fLastReportTime = null;
|
||||
private static ?float $iLastReportMemory = null;
|
||||
|
||||
// For stats
|
||||
protected $m_fStarted = null;
|
||||
protected $m_fChildrenDuration = 0; // Count embedded
|
||||
protected $m_iInitialMemory = null;
|
||||
|
||||
private static array $aBootstrapOperations = [];
|
||||
|
||||
public static function EnableDuration($iLevel)
|
||||
{
|
||||
if ($iLevel > 0) {
|
||||
@@ -76,7 +71,6 @@ class ExecutionKPI implements iEventServiceSetup
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -103,7 +97,7 @@ class ExecutionKPI implements iEventServiceSetup
|
||||
$sFor = self::$m_sAllowedUser == '*' ? 'EVERYBODY' : "'".trim(self::$m_sAllowedUser)."'";
|
||||
$sSlowQueries = '';
|
||||
if (self::$m_fSlowQueries > 0) {
|
||||
$sSlowQueries = '. Slow Queries: '.self::$m_fSlowQueries.'s';
|
||||
$sSlowQueries = ". Slow Queries: ".self::$m_fSlowQueries."s";
|
||||
}
|
||||
|
||||
$aExtensions = [];
|
||||
@@ -133,7 +127,7 @@ class ExecutionKPI implements iEventServiceSetup
|
||||
$sRequest .= ' operation: '.$_POST['operation'];
|
||||
}
|
||||
|
||||
$fStop = microtime(true);
|
||||
$fStop = MyHelpers::getmicrotime();
|
||||
if (($fStop - $fItopStarted) > self::$m_fSlowQueries) {
|
||||
// Invoke extensions to log the KPI operation
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
@@ -157,17 +151,17 @@ class ExecutionKPI implements iEventServiceSetup
|
||||
|
||||
$sTableStyle = 'background-color: #ccc; margin: 10px;';
|
||||
|
||||
$sHtml = '<hr/>';
|
||||
$sHtml = "<hr/>";
|
||||
$sHtml .= "<div style=\"background-color: grey; padding: 10px;\">";
|
||||
$sHtml .= "<h3><a name=\"".md5($sExecId)."\">KPIs</a> - $sRequest</h3>";
|
||||
$oStarted = DateTime::createFromFormat('U.u', $fItopStarted);
|
||||
$sHtml .= '<p>'.$oStarted->format('Y-m-d H:i:s.u').'</p>';
|
||||
$sHtml .= '<p>log_kpi_user_id: '.UserRights::GetUserId().'</p>';
|
||||
$sHtml .= '<div>';
|
||||
$sHtml .= "<p>log_kpi_user_id: ".UserRights::GetUserId()."</p>";
|
||||
$sHtml .= "<div>";
|
||||
$sHtml .= "<table border=\"1\" style=\"$sTableStyle\">";
|
||||
$sHtml .= '<thead>';
|
||||
$sHtml .= ' <th>Operation</th><th>Begin</th><th>End</th><th>Duration</th><th>Memory start</th><th>Memory end</th><th>Memory peak</th>';
|
||||
$sHtml .= '</thead>';
|
||||
$sHtml .= "<thead>";
|
||||
$sHtml .= " <th>Operation</th><th>Begin</th><th>End</th><th>Duration</th><th>Memory start</th><th>Memory end</th><th>Memory peak</th>";
|
||||
$sHtml .= "</thead>";
|
||||
foreach (self::$m_aExecData as $aOpStats) {
|
||||
$sOperation = $aOpStats['op'];
|
||||
$sBegin = round($aOpStats['time_begin'], 3);
|
||||
@@ -186,12 +180,12 @@ class ExecutionKPI implements iEventServiceSetup
|
||||
}
|
||||
}
|
||||
|
||||
$sHtml .= '<tr>';
|
||||
$sHtml .= "<tr>";
|
||||
$sHtml .= " <td>$sOperation</td><td>$sBegin</td><td>$sEnd</td><td>$sDuration</td><td>$sMemBegin</td><td>$sMemEnd</td><td>$sMemPeak</td>";
|
||||
$sHtml .= '</tr>';
|
||||
$sHtml .= "</tr>";
|
||||
}
|
||||
$sHtml .= '</table>';
|
||||
$sHtml .= '</div>';
|
||||
$sHtml .= "</table>";
|
||||
$sHtml .= "</div>";
|
||||
|
||||
$aConsolidatedStats = [];
|
||||
foreach (self::$m_aStats as $sOperation => $aOpStats) {
|
||||
@@ -214,20 +208,20 @@ class ExecutionKPI implements iEventServiceSetup
|
||||
}
|
||||
}
|
||||
$aConsolidatedStats[$sOperation] = [
|
||||
'count' => $iTotalOp,
|
||||
'count' => $iTotalOp,
|
||||
'duration' => $fTotalOp,
|
||||
'min' => $fMinOp,
|
||||
'max' => $fMaxOp,
|
||||
'avg' => $fTotalOp / $iTotalOp,
|
||||
'min' => $fMinOp,
|
||||
'max' => $fMaxOp,
|
||||
'avg' => $fTotalOp / $iTotalOp,
|
||||
'max_args' => $sMaxOpArguments,
|
||||
];
|
||||
}
|
||||
|
||||
$sHtml .= '<div>';
|
||||
$sHtml .= "<div>";
|
||||
$sHtml .= "<table border=\"1\" style=\"$sTableStyle\">";
|
||||
$sHtml .= '<thead>';
|
||||
$sHtml .= ' <th>Operation</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th><th>Avg</th>';
|
||||
$sHtml .= '</thead>';
|
||||
$sHtml .= "<thead>";
|
||||
$sHtml .= " <th>Operation</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th><th>Avg</th>";
|
||||
$sHtml .= "</thead>";
|
||||
foreach ($aConsolidatedStats as $sOperation => $aOpStats) {
|
||||
$sOperation = '<a href="#'.md5($sExecId.$sOperation).'">'.$sOperation.'</a>';
|
||||
$sCount = $aOpStats['count'];
|
||||
@@ -236,14 +230,14 @@ class ExecutionKPI implements iEventServiceSetup
|
||||
$sMax = '<a href="#'.md5($sExecId.$aOpStats['max_args']).'">'.round($aOpStats['max'], 3).'</a>';
|
||||
$sAvg = round($aOpStats['avg'], 3);
|
||||
|
||||
$sHtml .= '<tr>';
|
||||
$sHtml .= "<tr>";
|
||||
$sHtml .= " <td>$sOperation</td><td>$sCount</td><td>$sDuration</td><td>$sMin</td><td>$sMax</td><td>$sAvg</td>";
|
||||
$sHtml .= '</tr>';
|
||||
$sHtml .= "</tr>";
|
||||
}
|
||||
$sHtml .= '</table>';
|
||||
$sHtml .= '</div>';
|
||||
$sHtml .= "</table>";
|
||||
$sHtml .= "</div>";
|
||||
|
||||
$sHtml .= '</div>';
|
||||
$sHtml .= "</div>";
|
||||
|
||||
$sHtml .= "<p><a href=\"#end-".md5($sExecId)."\">Next page stats</a></p>";
|
||||
|
||||
@@ -293,18 +287,18 @@ class ExecutionKPI implements iEventServiceSetup
|
||||
$sOperationHtml = '<a name="'.md5($sExecId.$sOperation).'">'.$sOperation.'</a>';
|
||||
$sHtml .= "<h4>$sOperationHtml</h4>";
|
||||
$sHtml .= "<table border=\"1\" style=\"$sTableStyle\">";
|
||||
$sHtml .= '<thead>';
|
||||
$sHtml .= ' <th>Operation details (+ blame caller if log_kpi_duration = 2)</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th>';
|
||||
$sHtml .= '</thead>';
|
||||
$sHtml .= "<thead>";
|
||||
$sHtml .= " <th>Operation details (+ blame caller if log_kpi_duration = 2)</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th>";
|
||||
$sHtml .= "</thead>";
|
||||
$bDisplayHeader = false;
|
||||
}
|
||||
$sHtml .= '<tr>';
|
||||
$sHtml .= "<tr>";
|
||||
$sHtml .= " <td>$sHtmlArguments</td><td>$iCountInter</td><td>$sTotalInter</td><td>$sMinInter</td><td>$sMaxInter</td>";
|
||||
$sHtml .= '</tr>';
|
||||
$sHtml .= "</tr>";
|
||||
}
|
||||
}
|
||||
if (!$bDisplayHeader) {
|
||||
$sHtml .= '</table>';
|
||||
$sHtml .= "</table>";
|
||||
$sHtml .= "<p><a href=\"#".md5($sExecId)."\">Back to page stats</a></p>";
|
||||
}
|
||||
self::Report($sHtml);
|
||||
@@ -339,50 +333,39 @@ class ExecutionKPI implements iEventServiceSetup
|
||||
|
||||
$aNewEntry = null;
|
||||
|
||||
if (is_null(static::$fLastReportTime)) {
|
||||
static::$fLastReportTime = $fItopStarted;
|
||||
}
|
||||
|
||||
if (is_null(static::$iLastReportMemory)) {
|
||||
global $iItopInitialMemory;
|
||||
static::$iLastReportMemory = $iItopInitialMemory;
|
||||
}
|
||||
|
||||
$fStarted = static::$fLastReportTime;
|
||||
$fStopped = microtime(true);
|
||||
$fStarted = $this->m_fStarted;
|
||||
$fStopped = $this->m_fStarted;
|
||||
if (self::$m_bEnabled_Duration) {
|
||||
$fStopped = MyHelpers::getmicrotime();
|
||||
$aNewEntry = [
|
||||
'op' => $sOperationDesc,
|
||||
'time_begin' => $fStarted - $fItopStarted,
|
||||
'time_begin' => $this->m_fStarted - $fItopStarted,
|
||||
'time_end' => $fStopped - $fItopStarted,
|
||||
];
|
||||
// Reset for the next operation (if the object is recycled)
|
||||
$this->m_fStarted = $fStopped;
|
||||
}
|
||||
static::$fLastReportTime = $fStopped;
|
||||
|
||||
$iInitialMemory = static::$iLastReportMemory;
|
||||
$iCurrentMemory = $iInitialMemory;
|
||||
$iPeakMemory = $iInitialMemory;
|
||||
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
|
||||
$iCurrentMemory = 0;
|
||||
$iPeakMemory = 0;
|
||||
if (self::$m_bEnabled_Memory) {
|
||||
$iCurrentMemory = self::memory_get_usage();
|
||||
if (is_null($aNewEntry)) {
|
||||
$aNewEntry = ['op' => $sOperationDesc];
|
||||
}
|
||||
$aNewEntry['mem_begin'] = $iInitialMemory;
|
||||
$aNewEntry['mem_begin'] = $this->m_iInitialMemory;
|
||||
$aNewEntry['mem_end'] = $iCurrentMemory;
|
||||
$iPeakMemory = self::memory_get_peak_usage();
|
||||
$aNewEntry['mem_peak'] = $iPeakMemory;
|
||||
// Reset for the next operation (if the object is recycled)
|
||||
static::$iLastReportMemory = $iCurrentMemory;
|
||||
$this->m_iInitialMemory = $iCurrentMemory;
|
||||
}
|
||||
|
||||
if (self::$m_bEnabled_Duration || self::$m_bEnabled_Memory) {
|
||||
$aCallstack = ['callstack' => $this->GetCallStack()];
|
||||
if (static::$bMetamodelStarted) {
|
||||
foreach (static::$aBootstrapOperations as $oLog) {
|
||||
$this->LogOperation($oLog);
|
||||
}
|
||||
static::$aBootstrapOperations = [];
|
||||
// Invoke extensions to log the KPI operation
|
||||
// Invoke extensions to log the KPI operation
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
|
||||
$oKPILogData = new KpiLogData(
|
||||
KpiLogData::TYPE_REPORT,
|
||||
@@ -393,24 +376,9 @@ class ExecutionKPI implements iEventServiceSetup
|
||||
$sExtension,
|
||||
$iInitialMemory,
|
||||
$iCurrentMemory,
|
||||
$iPeakMemory,
|
||||
$aCallstack
|
||||
$iPeakMemory
|
||||
);
|
||||
$this->LogOperation($oKPILogData);
|
||||
} else {
|
||||
$oKPILogData = new KpiLogData(
|
||||
KpiLogData::TYPE_REPORT,
|
||||
'Step',
|
||||
$sOperationDesc,
|
||||
$fStarted,
|
||||
$fStopped,
|
||||
'',
|
||||
$iInitialMemory,
|
||||
$iCurrentMemory,
|
||||
$iPeakMemory,
|
||||
$aCallstack
|
||||
);
|
||||
static::$aBootstrapOperations[] = $oKPILogData;
|
||||
$oExtensionInstance->LogOperation($oKPILogData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,21 +388,13 @@ class ExecutionKPI implements iEventServiceSetup
|
||||
$this->ResetCounters();
|
||||
}
|
||||
|
||||
private function LogOperation(KpiLogData $oKPILogData): void
|
||||
{
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||
$oExtensionInstance->LogOperation($oKPILogData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute statistics for a call to an extension
|
||||
* Note: not working in dev mode (with links to env-production)
|
||||
*
|
||||
* @param object|string $object object called
|
||||
* @param string $sMethod method called on the object
|
||||
* @param string $sMessage additional message
|
||||
* @param string $sMethod method called on the object
|
||||
* @param string $sMessage additional message
|
||||
*
|
||||
* @return bool true if an extension was found for this object::method
|
||||
* @throws \ReflectionException
|
||||
@@ -463,23 +423,21 @@ class ExecutionKPI implements iEventServiceSetup
|
||||
|
||||
$fDuration = 0;
|
||||
if (self::$m_bEnabled_Duration) {
|
||||
$fStopped = microtime(true);
|
||||
$fStopped = MyHelpers::getmicrotime();
|
||||
$fDuration = $fStopped - $this->m_fStarted;
|
||||
$aCallstack = [];
|
||||
if (self::$m_bGenerateLegacyReport) {
|
||||
if (self::$m_bBlameCaller) {
|
||||
$aCallstack = MyHelpers::get_callstack(1);
|
||||
self::$m_aStats[$sOperation][$sArguments][] = [
|
||||
'time' => $fDuration,
|
||||
'callers' => $aCallstack,
|
||||
'time' => $fDuration,
|
||||
'callers' => $aCallstack,
|
||||
];
|
||||
} else {
|
||||
self::$m_aStats[$sOperation][$sArguments][] = [
|
||||
'time' => $fDuration,
|
||||
'time' => $fDuration,
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$aCallstack = ['callstack' => $this->GetCallStack()];
|
||||
}
|
||||
|
||||
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
|
||||
@@ -490,45 +448,33 @@ class ExecutionKPI implements iEventServiceSetup
|
||||
$iPeakMemory = self::memory_get_peak_usage();
|
||||
}
|
||||
|
||||
if (static::$bMetamodelStarted) {
|
||||
foreach (static::$aBootstrapOperations as $oLog) {
|
||||
$this->LogOperation($oLog);
|
||||
}
|
||||
static::$aBootstrapOperations = [];
|
||||
// Invoke extensions to log the KPI operation
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||
//$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
|
||||
$sExtension = '';
|
||||
$oKPILogData = new KpiLogData(
|
||||
KpiLogData::TYPE_STATS,
|
||||
$sOperation,
|
||||
$sArguments,
|
||||
$this->m_fStarted,
|
||||
$fStopped,
|
||||
'',
|
||||
$sExtension,
|
||||
$iInitialMemory,
|
||||
$iCurrentMemory,
|
||||
$iPeakMemory,
|
||||
$aCallstack
|
||||
);
|
||||
$this->LogOperation($oKPILogData);
|
||||
} else {
|
||||
$oKPILogData = new KpiLogData(
|
||||
KpiLogData::TYPE_STATS,
|
||||
$sOperation,
|
||||
$sArguments,
|
||||
$this->m_fStarted,
|
||||
$fStopped,
|
||||
'',
|
||||
$iInitialMemory,
|
||||
$iCurrentMemory,
|
||||
$iPeakMemory,
|
||||
$aCallstack
|
||||
);
|
||||
static::$aBootstrapOperations[] = $oKPILogData;
|
||||
$oExtensionInstance->LogOperation($oKPILogData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function ResetCounters()
|
||||
{
|
||||
$this->m_fStarted = microtime(true);
|
||||
if (self::$m_bEnabled_Duration) {
|
||||
$this->m_fStarted = microtime(true);
|
||||
}
|
||||
|
||||
if (self::$m_bEnabled_Memory) {
|
||||
$this->m_iInitialMemory = self::memory_get_usage();
|
||||
@@ -557,33 +503,7 @@ class ExecutionKPI implements iEventServiceSetup
|
||||
if (function_exists('memory_get_peak_usage')) {
|
||||
return memory_get_peak_usage($bRealUsage);
|
||||
}
|
||||
|
||||
// PHP > 5.2.1 - this verb depends on a compilation option
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ModuleHandlerApiInterface methods
|
||||
*/
|
||||
|
||||
public static function OnMetaModelStarted()
|
||||
{
|
||||
static::$bMetamodelStarted = true;
|
||||
}
|
||||
|
||||
public function RegisterEventsAndListeners()
|
||||
{
|
||||
EventService::RegisterListener(ApplicationEvents::APPLICATION_EVENT_METAMODEL_STARTED, [$this, 'OnMetaModelStarted']);
|
||||
}
|
||||
|
||||
private function GetCallStack(): string
|
||||
{
|
||||
$aCallStack = MyHelpers::get_callstack(2);
|
||||
$sCallStack = "Call stack:\n";
|
||||
foreach ($aCallStack as $index => $aLine) {
|
||||
$sCallStack .= "#$index ".$aLine['File'].'('.$aLine['Line'].'): '.$aLine['Function']."\n";
|
||||
}
|
||||
|
||||
return $sCallStack;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -691,7 +691,7 @@ abstract class LogAPI
|
||||
static::$m_oMockMetaModelConfig = $oMetaModelConfig;
|
||||
}
|
||||
|
||||
public static function Exception(string $sMessage, throwable $oException, ?string $sChannel = null, array $aContext = []): void
|
||||
public static function Exception(string $sMessage, throwable $oException, string $sChannel = null, array $aContext = []): void
|
||||
{
|
||||
$aErrorLogs = [];
|
||||
$aErrorLogs[] = static::PrepareErrorLog($sMessage, $oException, $aContext);
|
||||
|
||||
@@ -22,8 +22,6 @@ use Combodo\iTop\Application\EventRegister\ApplicationEvents;
|
||||
use Combodo\iTop\Core\MetaModel\FriendlyNameType;
|
||||
use Combodo\iTop\Service\Events\EventData;
|
||||
use Combodo\iTop\Service\Events\EventService;
|
||||
use Combodo\iTop\Setup\ModuleDependency\Module;
|
||||
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReader;
|
||||
|
||||
require_once APPROOT.'core/modulehandler.class.inc.php';
|
||||
require_once APPROOT.'core/querymodifier.class.inc.php';
|
||||
@@ -130,7 +128,7 @@ abstract class MetaModel
|
||||
/** @var array */
|
||||
private static $m_aClassToFile = [];
|
||||
/** @var string */
|
||||
protected static $m_sEnvironment = ITOP_DEFAULT_ENV;
|
||||
protected static $m_sEnvironment = 'production';
|
||||
|
||||
/**
|
||||
* Objects currently created/updated.
|
||||
@@ -464,43 +462,6 @@ abstract class MetaModel
|
||||
return call_user_func([$sClass, 'GetClassDescription'], $sClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
*
|
||||
* @return string
|
||||
* @throws \CoreException
|
||||
*/
|
||||
final public static function GetModuleName($sClass)
|
||||
{
|
||||
try {
|
||||
$oReflectionClass = new ReflectionClass($sClass);
|
||||
$sDir = realpath(dirname($oReflectionClass->getFileName()));
|
||||
$sApproot = realpath(APPROOT);
|
||||
while (($sDir !== $sApproot) && (str_contains($sDir, $sApproot))) {
|
||||
$aFiles = glob("$sDir/module.*.php");
|
||||
if (count($aFiles) > 1) {
|
||||
return 'core';
|
||||
}
|
||||
|
||||
if (count($aFiles) == 0) {
|
||||
$sDir = realpath(dirname($sDir));
|
||||
continue;
|
||||
}
|
||||
|
||||
$sModuleFilePath = $aFiles[0];
|
||||
$aModuleInfo = ModuleFileReader::GetInstance()->ReadModuleFileInformation($sModuleFilePath);
|
||||
$sModuleId = $aModuleInfo[ModuleFileReader::MODULE_INFO_ID];
|
||||
list($sModuleName, ) = ModuleDiscovery::GetModuleName($sModuleId);
|
||||
|
||||
return $sModuleName;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw new CoreException("Cannot find class module", ['class' => $sClass], '', $e);
|
||||
}
|
||||
|
||||
return 'core';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
*
|
||||
@@ -5748,7 +5709,7 @@ abstract class MetaModel
|
||||
* @throws \DictExceptionUnknownLanguage
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function Startup($config, $bModelOnly = false, $bAllowCache = true, $bTraceSourceFiles = false, $sEnvironment = ITOP_DEFAULT_ENV)
|
||||
public static function Startup($config, $bModelOnly = false, $bAllowCache = true, $bTraceSourceFiles = false, $sEnvironment = 'production')
|
||||
{
|
||||
// Startup on a new environment is not supported
|
||||
static $bStarted = false;
|
||||
|
||||
@@ -89,9 +89,17 @@ abstract class ModelReflection
|
||||
* @param string $defaultValue
|
||||
*
|
||||
* @return \RunTimeIconSelectionField
|
||||
* @deprecated since 3.3.0 replaced by GetAvailableIcons
|
||||
*/
|
||||
abstract public function GetIconSelectionField($sCode, $sLabel = '', $defaultValue = '');
|
||||
|
||||
/**
|
||||
* Find available icons for the current context
|
||||
*
|
||||
* @return array of ['value', 'label', 'icon'] where 'value' is the relative path on disk, 'label' the name to display and 'icon' is the URL to get the image
|
||||
*/
|
||||
abstract public function GetAvailableIcons(): array;
|
||||
|
||||
abstract public function GetRootClass($sClass);
|
||||
abstract public function EnumChildClasses($sClass, $iOption = ENUM_CHILD_CLASSES_EXCLUDETOP);
|
||||
}
|
||||
@@ -109,6 +117,8 @@ abstract class QueryReflection
|
||||
|
||||
class ModelReflectionRuntime extends ModelReflection
|
||||
{
|
||||
private static array $aAllIcons = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
@@ -255,6 +265,52 @@ class ModelReflectionRuntime extends ModelReflection
|
||||
return new RunTimeIconSelectionField($sCode, $sLabel, $defaultValue);
|
||||
}
|
||||
|
||||
public function GetAvailableIcons(): array
|
||||
{
|
||||
$aFolderList = [
|
||||
APPROOT.'env-'.utils::GetCurrentEnvironment() => utils::GetAbsoluteUrlModulesRoot(),
|
||||
APPROOT.'images/icons' => utils::GetAbsoluteUrlAppRoot().'images/icons',
|
||||
];
|
||||
if (count(self::$aAllIcons) == 0) {
|
||||
foreach ($aFolderList as $sFolderPath => $sUrlPrefix) {
|
||||
$aIcons = self::FindIconsOnDisk($sFolderPath);
|
||||
ksort($aIcons);
|
||||
|
||||
foreach ($aIcons as $sFilePath) {
|
||||
self::$aAllIcons[] = ['value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => $sUrlPrefix.$sFilePath];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self::$aAllIcons;
|
||||
}
|
||||
|
||||
private static function FindIconsOnDisk(string $sBaseDir, string $sDir = '', array &$aFilesSpecs = []): array
|
||||
{
|
||||
$aResult = [];
|
||||
// Populate automatically the list of icon files
|
||||
if ($hDir = @opendir($sBaseDir.'/'.$sDir)) {
|
||||
while (($sFile = readdir($hDir)) !== false) {
|
||||
$aMatches = [];
|
||||
if (($sFile != '.') && ($sFile != '..') && ($sFile != 'lifecycle') && is_dir($sBaseDir.'/'.$sDir.'/'.$sFile)) {
|
||||
$sDirSubPath = ($sDir == '') ? $sFile : $sDir.'/'.$sFile;
|
||||
$aResult = array_merge($aResult, self::FindIconsOnDisk($sBaseDir, $sDirSubPath, $aFilesSpecs));
|
||||
}
|
||||
$sSize = filesize($sBaseDir.'/'.$sDir.'/'.$sFile);
|
||||
if (isset($aFilesSpecs[$sFile]) && $aFilesSpecs[$sFile] == $sSize) {
|
||||
continue;
|
||||
}
|
||||
if (preg_match('/\.(png|jpg|jpeg|gif|svg)$/i', $sFile, $aMatches)) { // png, jp(e)g, gif and svg are considered valid
|
||||
$aResult[$sFile.'_'.$sDir] = $sDir.'/'.$sFile;
|
||||
$aFilesSpecs[$sFile] = $sSize;
|
||||
}
|
||||
}
|
||||
closedir($hDir);
|
||||
}
|
||||
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
public function GetRootClass($sClass)
|
||||
{
|
||||
return MetaModel::GetRootClass($sClass);
|
||||
|
||||
@@ -1656,7 +1656,7 @@ class PHP_ParserGenerator_Data
|
||||
function emit_code($out, PHP_ParserGenerator_Rule $rp, &$lineno)
|
||||
{
|
||||
$linecnt = 0;
|
||||
|
||||
|
||||
/* Generate code to do the reduce action */
|
||||
if ($rp->code) {
|
||||
$this->tplt_linedir($out, $rp->line, $this->filename);
|
||||
|
||||
@@ -1084,7 +1084,7 @@ static public $yy_action = array(
|
||||
function yy_find_shift_action($iLookAhead)
|
||||
{
|
||||
$stateno = $this->yystack[$this->yyidx]->stateno;
|
||||
|
||||
|
||||
/* if ($this->yyidx < 0) return self::YY_NO_ACTION; */
|
||||
if (!isset(self::$yy_shift_ofst[$stateno])) {
|
||||
// no shift actions
|
||||
@@ -1767,7 +1767,7 @@ throw new OQLParserParseFailureException($this->m_sSourceQuery, $this->m_iLine,
|
||||
function yy_syntax_error($yymajor, $TOKEN)
|
||||
{
|
||||
#line 25 "..\oql-parser.y"
|
||||
|
||||
|
||||
throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||
#line 1779 "..\oql-parser.php"
|
||||
}
|
||||
@@ -1806,7 +1806,7 @@ throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $
|
||||
// $yyact; /* The parser action. */
|
||||
// $yyendofinput; /* True if we are at the end of input */
|
||||
$yyerrorhit = 0; /* True if yymajor has invoked an error */
|
||||
|
||||
|
||||
/* (re)initialize the parser, if necessary */
|
||||
if ($this->yyidx === null || $this->yyidx < 0) {
|
||||
/* if ($yymajor == 0) return; // not sure why this was here... */
|
||||
@@ -1819,7 +1819,7 @@ throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $
|
||||
array_push($this->yystack, $x);
|
||||
}
|
||||
$yyendofinput = ($yymajor==0);
|
||||
|
||||
|
||||
if (self::$yyTraceFILE) {
|
||||
fprintf(
|
||||
self::$yyTraceFILE,
|
||||
@@ -1828,7 +1828,7 @@ throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $
|
||||
self::$yyTokenName[$yymajor]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
do {
|
||||
$yyact = $this->yy_find_shift_action($yymajor);
|
||||
if ($yymajor < self::YYERRORSYMBOL
|
||||
@@ -2002,7 +2002,7 @@ class OQLParser extends OQLParserRaw
|
||||
$this->m_sSourceQuery = $sQuery;
|
||||
// no constructor - parent::__construct();
|
||||
}
|
||||
|
||||
|
||||
public function doParse($token, $value, $iCurrPosition = 0)
|
||||
{
|
||||
$this->m_iColPrev = $this->m_iCol;
|
||||
@@ -2016,7 +2016,7 @@ class OQLParser extends OQLParserRaw
|
||||
$this->doParse(0, 0);
|
||||
return $this->my_result;
|
||||
}
|
||||
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
// Bug in the original destructor, causing an infinite loop !
|
||||
|
||||
@@ -370,7 +370,7 @@ class ormCaseLog
|
||||
/**
|
||||
* Produces an HTML representation, aimed at being used within the iTop framework
|
||||
*/
|
||||
public function GetAsHTML(?WebPage $oP = null, $bEditMode = false, $aTransfoHandler = null)
|
||||
public function GetAsHTML(WebPage $oP = null, $bEditMode = false, $aTransfoHandler = null)
|
||||
{
|
||||
$bPrintableVersion = (utils::ReadParam('printable', '0') == '1');
|
||||
|
||||
|
||||
@@ -350,22 +350,20 @@ class ormDocument
|
||||
if (!is_object($oObj)) {
|
||||
// If access to the document is not granted, check if the access to the host object is allowed
|
||||
$oObj = MetaModel::GetObject($sClass, $id, false, true);
|
||||
$bHasHostRights = false;
|
||||
if ($oObj instanceof Attachment) {
|
||||
$sItemClass = $oObj->Get('item_class');
|
||||
$sItemId = $oObj->Get('item_id');
|
||||
$oHost = MetaModel::GetObject($sItemClass, $sItemId, false, false);
|
||||
if (is_object($oHost)) {
|
||||
$bHasHostRights = true;
|
||||
if (!is_object($oHost)) {
|
||||
$oObj = null;
|
||||
}
|
||||
}
|
||||
|
||||
// We could neither read the object nor get a host object matching our rights
|
||||
if ($bHasHostRights !== true) {
|
||||
if (!is_object($oObj)) {
|
||||
throw new Exception("Invalid id ($id) for class '$sClass' - the object does not exist or you are not allowed to view it");
|
||||
}
|
||||
}
|
||||
if (($sSecretField != null) && !hash_equals($oObj->Get($sSecretField), $sSecretValue)) {
|
||||
if (($sSecretField != null) && ($oObj->Get($sSecretField) != $sSecretValue)) {
|
||||
usleep(200);
|
||||
throw new Exception("Invalid secret for class '$sClass' - the object does not exist or you are not allowed to view it");
|
||||
}
|
||||
/** @var \ormDocument $oDocument */
|
||||
|
||||
@@ -93,7 +93,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
* @param DBObjectSet|null $oOriginalSet
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct($sHostClass, $sAttCode, ?DBObjectSet $oOriginalSet = null)
|
||||
public function __construct($sHostClass, $sAttCode, DBObjectSet $oOriginalSet = null)
|
||||
{
|
||||
$this->sHostClass = $sHostClass;
|
||||
$this->sAttCode = $sAttCode;
|
||||
@@ -470,6 +470,17 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
|| ($this->oOriginalSet->GetFilter()->ToOQL() == $oFellow->oOriginalSet->GetFilter()->ToOQL())) {
|
||||
$bUpdateFromDelta = true;
|
||||
}
|
||||
} else {
|
||||
//@since 3.2.2 N°2364 - API : remove old linkedset persistance
|
||||
/* Goo pattern to use:
|
||||
* $oCISet = $oTicket->Get(‘functioncis_list’);
|
||||
* $oCISet->AddItem(MetaModel::NewObject(‘lnkFunctionCIToTicket’, array(‘ci_id’=> 12345));
|
||||
* $oCISet->RemoveItem(123456);
|
||||
* $oTicket->Set(‘functionalcis_list’, $oCISet);
|
||||
*/
|
||||
if (!ContextTag::Check(ContextTag::TAG_SETUP)) {
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('old pattern - please get previous value of the linked set, modify it and set it back to the host object');
|
||||
}
|
||||
}
|
||||
|
||||
if ($bUpdateFromDelta) {
|
||||
|
||||
@@ -98,9 +98,9 @@ class ormPassword
|
||||
$bResult = false;
|
||||
$aInfo = password_get_info($this->m_sHashed);
|
||||
if (is_null($aInfo["algo"]) || $aInfo["algo"] === 0) {
|
||||
// - Unknown algorithm, assume it's a legacy password
|
||||
//unknown, assume it's a legacy password
|
||||
$sHashedPwd = $this->ComputeHash($sClearTextPassword);
|
||||
$bResult = hash_equals($this->m_sHashed, $sHashedPwd);
|
||||
$bResult = ($this->m_sHashed == $sHashedPwd);
|
||||
} else {
|
||||
$bResult = password_verify($sClearTextPassword, $this->m_sHashed);
|
||||
}
|
||||
|
||||
@@ -43,24 +43,24 @@ final class ormTagSet extends ormSet
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array|null $aItems
|
||||
* @param array $aTagCodes
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue when a code is invalid
|
||||
*/
|
||||
public function SetValues($aItems)
|
||||
public function SetValues($aTagCodes)
|
||||
{
|
||||
if (is_null($aItems)) {
|
||||
$aItems = [];
|
||||
if (is_null($aTagCodes)) {
|
||||
$aTagCodes = [];
|
||||
}
|
||||
if (!is_array($aItems)) {
|
||||
throw new CoreUnexpectedValue("Wrong value {$aItems} for {$this->sClass}:{$this->sAttCode}");
|
||||
if (!is_array($aTagCodes)) {
|
||||
throw new CoreUnexpectedValue("Wrong value {$aTagCodes} for {$this->sClass}:{$this->sAttCode}");
|
||||
}
|
||||
|
||||
$oTags = [];
|
||||
$iCount = 0;
|
||||
$bError = false;
|
||||
foreach ($aItems as $sTagCode) {
|
||||
foreach ($aTagCodes as $sTagCode) {
|
||||
$iCount++;
|
||||
if (($this->iLimit != 0) && ($iCount > $this->iLimit)) {
|
||||
$bError = true;
|
||||
|
||||
@@ -1182,7 +1182,7 @@ class UserRights
|
||||
return self::$m_oUser->GetKey();
|
||||
} else {
|
||||
// find the id out of the login string
|
||||
$oUser = self::FindUser($sLogin, bAllowDisabledUsers: true);
|
||||
$oUser = self::FindUser($sLogin);
|
||||
if (is_null($oUser)) {
|
||||
return null;
|
||||
}
|
||||
@@ -1375,7 +1375,7 @@ class UserRights
|
||||
if (empty($sLogin)) {
|
||||
$oUser = self::$m_oUser;
|
||||
} else {
|
||||
$oUser = self::FindUser($sLogin, bAllowDisabledUsers: true);
|
||||
$oUser = self::FindUser($sLogin);
|
||||
}
|
||||
if (is_null($oUser)) {
|
||||
return '';
|
||||
|
||||
@@ -56,8 +56,6 @@ $ibo-shame--slider--is-round--border-radius: 20px !default;
|
||||
$ibo-shame--slider--is-round--before--border-radius: 7px !default;
|
||||
|
||||
|
||||
$ibo-blockquote--color: $ibo-body-text-color !default;
|
||||
|
||||
// N°2847 - Recolor svg illustrations with iTop's primary color
|
||||
.ibo-svg-illustration--container > svg *[fill="#6c63ff"]{
|
||||
fill: $ibo-svg-illustration--fill;
|
||||
@@ -128,11 +126,3 @@ input:checked + .slider:before {
|
||||
.slider.round:before {
|
||||
border-radius: $ibo-shame--slider--is-round--before--border-radius;
|
||||
}
|
||||
|
||||
/*
|
||||
Bulma sets blockquote background color through a variable, it affects ckeditor and html display.
|
||||
This rule is needed harmonize the blockquote text color in both contexts.
|
||||
*/
|
||||
.ibo-is-html-content blockquote {
|
||||
color: $ibo-blockquote--color;
|
||||
}
|
||||
@@ -4,4 +4,3 @@
|
||||
*/
|
||||
|
||||
@import "bulk-modify";
|
||||
@import "bulk-export";
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
#form_part_csv_options:has(#ibo-sanitize-excel-export--input:checked), #form_part_xlsx_options:has(#ibo-sanitize-excel-export--input:checked){
|
||||
#ibo-sanitize-excel-export--alert {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,4 @@
|
||||
@import "medallion-with-blocklist";
|
||||
@import "field-badge-within-datatable";
|
||||
@import "jquery-blockui-within-dialog";
|
||||
@import "jquery-blockui-within-datatable";
|
||||
@import "badge-with-badge";
|
||||
@import "extension-details-with-extension-details";
|
||||
@import "jquery-blockui-within-datatable";
|
||||
@@ -1,10 +0,0 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-badge--spacing-left--with-same-block: $ibo-spacing-200 !default;
|
||||
|
||||
.ibo-badge + .ibo-badge {
|
||||
margin-left: $ibo-badge--spacing-left--with-same-block;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-extension-details--margin-top: $ibo-spacing-300 !default;
|
||||
|
||||
.ibo-extension-details + .ibo-extension-details,
|
||||
.ibo-extension-details--information--description .ibo-extension-details {
|
||||
margin-top: $ibo-extension-details--margin-top;
|
||||
}
|
||||
@@ -33,5 +33,4 @@
|
||||
@import "field-badge";
|
||||
@import "file-select";
|
||||
@import "medallion-icon";
|
||||
@import "toast";
|
||||
@import "badge";
|
||||
@import "toast";
|
||||
@@ -1,41 +0,0 @@
|
||||
$ibo-badge--padding-x : $ibo-spacing-200 !default;
|
||||
$ibo-badge--padding-y : $ibo-spacing-100 !default;
|
||||
$ibo-badge--border-radius : $ibo-border-radius-400 !default;
|
||||
|
||||
$ibo-badge-colors: (
|
||||
'primary': ($ibo-color-primary-100, $ibo-color-primary-900),
|
||||
'secondary': ($ibo-color-secondary-100, $ibo-color-secondary-900),
|
||||
'neutral': ($ibo-color-secondary-100, $ibo-color-secondary-900),
|
||||
'information': ($ibo-color-information-100, $ibo-color-information-900),
|
||||
'success': ($ibo-color-success-100, $ibo-color-success-900),
|
||||
'failure': ($ibo-color-danger-100, $ibo-color-danger-900),
|
||||
'warning': ($ibo-color-warning-100,$ibo-color-warning-900),
|
||||
'danger': ($ibo-color-danger-100,$ibo-color-danger-900),
|
||||
'grey' : ($ibo-color-grey-100, $ibo-color-grey-900),
|
||||
'blue-grey': ($ibo-color-blue-grey-100, $ibo-color-blue-grey-900),
|
||||
'blue': ($ibo-color-blue-100, $ibo-color-blue-900),
|
||||
'cyan': ($ibo-color-cyan-100, $ibo-color-cyan-900),
|
||||
'green': ($ibo-color-green-100, $ibo-color-green-900),
|
||||
'orange' : ($ibo-color-orange-100, $ibo-color-orange-900),
|
||||
'red': ($ibo-color-red-100, $ibo-color-red-900),
|
||||
'pink': ($ibo-color-pink-100, $ibo-color-pink-900),
|
||||
) !default;
|
||||
|
||||
|
||||
|
||||
.ibo-badge {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
padding : $ibo-badge--padding-y $ibo-badge--padding-x;
|
||||
border-radius : $ibo-badge--border-radius;
|
||||
@extend %ibo-font-ral-med-50;
|
||||
|
||||
@each $sColor, $aColorValues in $ibo-badge-colors {
|
||||
$bg-color: nth($aColorValues, 1);
|
||||
$text-color: nth($aColorValues, 2);
|
||||
&.ibo-is-#{$sColor} {
|
||||
background-color: $bg-color;
|
||||
color: $text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,3 +75,26 @@ collection-entry-element {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.ibo-form-compact{
|
||||
|
||||
.ibo-field{
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
|
||||
label{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
max-width: unset;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -72,12 +72,9 @@ $ibo-panel--icon--spacing--as-medallion--is-sticking: $ibo-panel--icon--spacing-
|
||||
$ibo-panel--icon--bottom--as-medallion--is-sticking: -12px !default;
|
||||
$ibo-panel--icon--border--as-medallion--is-sticking: 1px $ibo-panel--base-border-style $ibo-panel--base-border-color !default;
|
||||
|
||||
$ibo-panel--icon-background--size--must-contain: contain !default; // deprecated, to be removed in favor of $ibo-panel--icon-img--size--must-contain
|
||||
$ibo-panel--icon-background--size--must-cover: cover !default; // deprecated, to be removed in favor of $ibo-panel--icon-img--size--must-cover
|
||||
$ibo-panel--icon-background--size--must-zoomout: 66.67% !default; // deprecated, to be removed in favor of $ibo-panel--icon-img--size--must-zoomout
|
||||
$ibo-panel--icon-img--size--must-contain: $ibo-panel--icon-background--size--must-contain !default; // TODO remove when dealing with N°9317
|
||||
$ibo-panel--icon-img--size--must-cover: $ibo-panel--icon-background--size--must-cover !default; // TODO remove when dealing with N°9317
|
||||
$ibo-panel--icon-img--size--must-zoomout: $ibo-panel--icon-background--size--must-zoomout !default; // TODO remove when dealing with N°9317
|
||||
$ibo-panel--icon-background--size--must-contain: contain !default;
|
||||
$ibo-panel--icon-background--size--must-cover: cover !default;
|
||||
$ibo-panel--icon-background--size--must-zoomout: 66.67% !default;
|
||||
|
||||
$ibo-panel--title--font-size--is-sticking: $ibo-font-size-150 !default;
|
||||
$ibo-panel--title--color: $ibo-color-grey-900 !default;
|
||||
@@ -182,25 +179,24 @@ $ibo-panel--is-selectable--body--after--font-size: $ibo-font-size-700 !default;
|
||||
min-height: $ibo-panel--icon--size;
|
||||
}
|
||||
|
||||
.ibo-panel--icon-img, .ibo-panel--icon-background { // second class is deprecated, remove it when dealing with N°9317
|
||||
.ibo-panel--icon-background {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: $ibo-panel--icon-img--size--must-contain;
|
||||
background-size: $ibo-panel--icon-background--size--must-contain;
|
||||
}
|
||||
|
||||
.ibo-panel--icon-img--must-contain, .ibo-panel--icon-background--must-contain { // second class is deprecated, remove it when dealing with N°9317
|
||||
background-size: $ibo-panel--icon-img--size--must-contain;
|
||||
.ibo-panel--icon-background--must-contain {
|
||||
background-size: $ibo-panel--icon-background--size--must-contain;
|
||||
}
|
||||
|
||||
.ibo-panel--icon-img--must-cover, .ibo-panel--icon-background--must-cover { // second class is deprecated, remove it when dealing with N°9317
|
||||
background-size: $ibo-panel--icon-img--size--must-cover;
|
||||
.ibo-panel--icon-background--must-cover {
|
||||
background-size: $ibo-panel--icon-background--size--must-cover;
|
||||
}
|
||||
|
||||
.ibo-panel--icon-img--must-zoomout, .ibo-panel--icon-background--must-zoomout { // second class is deprecated, remove it when dealing with N°9317
|
||||
width: $ibo-panel--icon-img--size--must-zoomout;
|
||||
height: $ibo-panel--icon-img--size--must-zoomout;
|
||||
.ibo-panel--icon-background--must-zoomout {
|
||||
background-size: $ibo-panel--icon-background--size--must-zoomout;
|
||||
}
|
||||
|
||||
.ibo-panel--title {
|
||||
|
||||
@@ -21,8 +21,6 @@ $ibo-search-form-panel--more-criteria--color: $ibo-color-blue-grey-800 !default;
|
||||
$ibo-search-form-panel--more-criteria--background-color: $ibo-color-white-100 !default;
|
||||
$ibo-search-form-panel--more-criteria--icon--color: $ibo-color-primary-600 !default;
|
||||
$ibo-search-form-panel--more-criteria--border-color: $ibo-search-form-panel--criteria--border-color !default;
|
||||
// calc is redundant but avoid SCSS min() from being used instead of CSS min()
|
||||
$ibo-search-form-panel--criteria--max-height: calc(min(#{$ibo-size-750}, 50vh)) !default;
|
||||
|
||||
$ibo-search-form-panel--items--hover--color: $ibo-color-grey-200 !default;
|
||||
|
||||
@@ -280,10 +278,9 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
}
|
||||
|
||||
.sfc_form_group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: -1px;
|
||||
z-index: -1;
|
||||
display: block;
|
||||
margin-top: -1px;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,15 +346,11 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
display: none;
|
||||
max-width: 450px;
|
||||
width: max-content;
|
||||
max-height: $ibo-search-form-panel--criteria--max-height;
|
||||
max-height: 520px;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
|
||||
.sfc_fg_operators {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
min-height: 0;
|
||||
font-size: 12px;
|
||||
|
||||
.sfc_fg_operator {
|
||||
@@ -394,9 +387,6 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
}
|
||||
|
||||
.sfc_opc_multichoices {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
label > input {
|
||||
vertical-align: text-top;
|
||||
margin-left: $ibo-spacing-0;
|
||||
@@ -408,6 +398,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
}
|
||||
|
||||
.sfc_opc_mc_items_wrapper {
|
||||
max-height: 415px; /* Must be less than .sfc_form_group:max-height - .sfc_opc_mc_toggler:height - .sfc_opc_mc_filter:height */
|
||||
overflow-y: auto;
|
||||
margin: $ibo-spacing-0 -8px; /* Compensate .sfc_opc_multichoices side padding so the hover style can take the full with */
|
||||
|
||||
@@ -569,14 +560,8 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
&.search_form_criteria_enum {
|
||||
.sfc_form_group {
|
||||
.sfc_fg_operator_in {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
> label {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
line-height: initial;
|
||||
white-space: nowrap;
|
||||
|
||||
@@ -11,12 +11,9 @@ $ibo-title--icon--size: 90px !default;
|
||||
$ibo-title--icon--size-2: 80px !default;
|
||||
$ibo-title--icon--size-3: 70px !default;
|
||||
|
||||
$ibo-title--icon-background--size--must-contain: contain !default; // deprecated, to be removed in favor of $ibo-title--icon-img--size--must-contain
|
||||
$ibo-title--icon-background--size--must-cover: cover !default; // deprecated, to be removed in favor of $ibo-title--icon-img--size--must-cover
|
||||
$ibo-title--icon-background--size--must-zoomout: 66.67% !default; // deprecated, to be removed in favor of $ibo-title--icon-img--size--must-zoomout
|
||||
$ibo-title--icon-img--size--must-contain: $ibo-title--icon-background--size--must-contain !default; // TODO remove when dealing with N°9317
|
||||
$ibo-title--icon-img--size--must-cover: $ibo-title--icon-background--size--must-cover !default; // TODO remove when dealing with N°9317
|
||||
$ibo-title--icon-img--size--must-zoomout: $ibo-title--icon-background--size--must-zoomout !default; // TODO remove when dealing with N°9317
|
||||
$ibo-title--icon-background--size--must-contain: contain !default;
|
||||
$ibo-title--icon-background--size--must-cover: cover !default;
|
||||
$ibo-title--icon-background--size--must-zoomout: 66.67% !default;
|
||||
|
||||
|
||||
.ibo-title {
|
||||
@@ -47,23 +44,24 @@ $ibo-title--icon-img--size--must-zoomout: $ibo-title--icon-background--size--mus
|
||||
min-height: $ibo-title--icon--size-3;
|
||||
}
|
||||
|
||||
.ibo-title--icon-img, .ibo-title--icon-background { // second class is deprecated, remove it when dealing with N°9317
|
||||
.ibo-title--icon-background {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-position: center;
|
||||
background-size: $ibo-title--icon-img--size--must-contain;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: $ibo-title--icon-background--size--must-contain;
|
||||
}
|
||||
|
||||
.ibo-title--icon-img--must-contain, .ibo-title--icon-background--must-contain { // second class is deprecated, remove it when dealing with N°9317
|
||||
background-size: $ibo-title--icon-img--size--must-contain;
|
||||
.ibo-title--icon-background--must-contain {
|
||||
background-size: $ibo-title--icon-background--size--must-contain;
|
||||
}
|
||||
|
||||
.ibo-title--icon-img--must-cover, .ibo-title--icon-background--must-cover { // second class is deprecated, remove it when dealing with N°9317
|
||||
background-size: $ibo-title--icon-img--size--must-cover;
|
||||
.ibo-title--icon-background--must-cover {
|
||||
background-size: $ibo-title--icon-background--size--must-cover;
|
||||
}
|
||||
|
||||
.ibo-title--icon-img--must-zoomout, .ibo-title--icon-background--must-zoomout { // second class is deprecated, remove it when dealing with N°9317
|
||||
background-size: $ibo-title--icon-img--size--must-zoomout;
|
||||
.ibo-title--icon-background--must-zoomout {
|
||||
background-size: $ibo-title--icon-background--size--must-zoomout;
|
||||
}
|
||||
|
||||
.ibo-title--for-object-details {
|
||||
|
||||
@@ -19,7 +19,10 @@ $ibo-dashlet-blocker--height: 100% !default;
|
||||
.ibo-dashlet {
|
||||
position: relative;
|
||||
width: calc(#{$ibo-dashlet--width} - #{$ibo-dashlet--elements-spacing-x});
|
||||
margin: calc(#{$ibo-dashlet--elements-spacing-y} / 2) calc(#{$ibo-dashlet--elements-spacing-x} / 2);
|
||||
//margin: calc(#{$ibo-dashlet--elements-spacing-y} / 2) calc(#{$ibo-dashlet--elements-spacing-x} / 2);
|
||||
|
||||
height: 100% !important;
|
||||
width: 100% !important;
|
||||
|
||||
&.dashlet-selected {
|
||||
position: relative;
|
||||
@@ -38,4 +41,21 @@ $ibo-dashlet-blocker--height: 100% !default;
|
||||
width: $ibo-dashlet-blocker--width;
|
||||
height: $ibo-dashlet-blocker--height;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.ibo-dashlet--actions {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
display: none;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
|
||||
background-color: $ibo-color-white-100;
|
||||
@extend %ibo-elevation-100;
|
||||
}
|
||||
|
||||
ibo-dashlet[data-edit-mode="edit"] {
|
||||
z-index: 3;
|
||||
}
|
||||
@@ -54,3 +54,31 @@ $ibo-input-select-icon--menu--icon--margin-right: 10px !default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ts-control > .ibo-input-select-icon--menu--item{
|
||||
|
||||
display: flex;
|
||||
column-gap: 5px;
|
||||
align-items: center;
|
||||
padding: 5px 0;
|
||||
|
||||
> img{
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
&:hover{
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.ts-dropdown > .ts-dropdown-content > .ibo-input-select-icon--menu--item{
|
||||
|
||||
display: flex;
|
||||
column-gap: 5px;
|
||||
|
||||
> img{
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -203,9 +203,8 @@ $ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-60
|
||||
}
|
||||
|
||||
// N°7982 Default selectize stylesheet override
|
||||
// N°9468 Dropdown content needs to be a few pixel shorter than the dropdown itself to avoid double scrollbar
|
||||
.selectize-dropdown-content{
|
||||
max-height: calc(#{$ibo-input-select-selectize--dropdown--max-height} - 4px);
|
||||
max-height: $ibo-input-select-selectize--dropdown--max-height;
|
||||
}
|
||||
|
||||
.selectize-dropdown.ui-menu .ui-state-active {
|
||||
|
||||
@@ -8,7 +8,6 @@ $ibo-toggler--wrapper--height: 20px !default;
|
||||
|
||||
$ibo-toggler--slider--border-radius: $ibo-border-radius-900 !default;
|
||||
$ibo-toggler--slider--background-color: $ibo-color-secondary-600 !default;
|
||||
$ibo-toggler--slider--disabled--background-color: $ibo-color-secondary-200 !default;
|
||||
|
||||
$ibo-toggler--slider--before--left: 3px !default;
|
||||
$ibo-toggler--slider--before--bottom: 3px !default;
|
||||
@@ -18,7 +17,6 @@ $ibo-toggler--slider--before--border-radius: $ibo-border-radius-full !default;
|
||||
$ibo-toggler--slider--before--background-color: $ibo-color-grey-100 !default;
|
||||
|
||||
$ibo-toggler--slider--checked--background-color: $ibo-color-primary-600 !default;
|
||||
$ibo-toggler--slider--checked-disabled--background-color: $ibo-color-primary-200 !default;
|
||||
$ibo-toggler--slider--focus--box-shadow: 0 0 1px $ibo-color-primary-600 !default;
|
||||
|
||||
$ibo-toggler--label--margin-left: 4px !default;
|
||||
@@ -63,13 +61,6 @@ $ibo-toggler--label--margin-left: 4px !default;
|
||||
background-color: $ibo-toggler--slider--checked--background-color;
|
||||
}
|
||||
|
||||
.ibo-toggler--wrapper input:disabled + .ibo-toggler--slider {
|
||||
background-color: $ibo-toggler--slider--disabled--background-color;
|
||||
}
|
||||
.ibo-toggler--wrapper input:checked:disabled + .ibo-toggler--slider {
|
||||
background-color: $ibo-toggler--slider--checked-disabled--background-color;
|
||||
}
|
||||
|
||||
input:focus + .ibo-toggler--slider {
|
||||
box-shadow: $ibo-toggler--slider--focus--box-shadow;
|
||||
}
|
||||
|
||||
@@ -15,4 +15,4 @@
|
||||
@import "wizard-container/wizard-container";
|
||||
@import "object/all";
|
||||
@import "activity-panel/all";
|
||||
@import "extension/all";
|
||||
@import "dashlet-panel/all";
|
||||
|
||||
@@ -175,3 +175,73 @@ input:checked + .ibo-dashboard--slider:before {
|
||||
input:checked + .ibo-dashboard--slider:after {
|
||||
content: $ibo-dashboard--slider--before--content;
|
||||
}
|
||||
|
||||
// TODO 3.3 Cleanup variables
|
||||
// TODO 3.3 Move to vendor what's from gridstack
|
||||
|
||||
|
||||
.grid-stack {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ibo-dashboard[data-edit-mode="edit"] .grid-stack{
|
||||
background-size: calc(100% / 12) var(--gs-cell-height);
|
||||
background-color: $ibo-color-white-100;
|
||||
background-image: linear-gradient(to right, $ibo-color-white-200 8px, transparent 8px), linear-gradient(to bottom, $ibo-color-white-200 8px, transparent 8px);
|
||||
--gs-item-margin-top: 8px;
|
||||
--gs-item-margin-bottom: 0;
|
||||
--gs-item-margin-right: 0;
|
||||
--gs-item-margin-left: 8px;
|
||||
}
|
||||
|
||||
ibo-dashboard[data-edit-mode="view"] {
|
||||
.ibo-dashboard--form {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-dashboard--form {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
height: 55px;
|
||||
background-color: $ibo-color-blue-200;
|
||||
margin: -16px -36px 24px -36px;
|
||||
padding: 0 36px;
|
||||
}
|
||||
|
||||
.ibo-dashboard--form--inputs {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
@extend %common-font-ral-med-250;
|
||||
}
|
||||
|
||||
.ibo-dashboard[data-edit-mode="edit"] ibo-dashlet:not([data-edit-mode="edit"]):hover .ibo-dashlet--actions {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ibo-dashboard[data-edit-mode="error"] .grid-stack{
|
||||
background-image: url($approot-relative + '/images/alpha-fatal-error.gif');
|
||||
}
|
||||
|
||||
// Our edit mode dashboard already has its own header, so we hide the standard one
|
||||
#ibo-page-header:has(+ ibo-dashboard[data-edit-mode="edit"]) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ibo-dashboard[data-edit-mode="edit"] .ibo-dashboard--grid:has(ibo-dashboard-grid-slot > ibo-dashlet[data-edit-mode="edit"]) .ibo-dashboard--grid--backdrop {
|
||||
|
||||
position: absolute;
|
||||
height: calc(100% + 24px);
|
||||
// 36px is $ibo-page-container--elements-padding-x, handle variable resolution
|
||||
width: calc(100% + 36px + 36px);
|
||||
margin: -24px -#{36px} 0 -#{36px};
|
||||
background-color: $ibo-color-grey-400;
|
||||
z-index: 2;
|
||||
opacity: 60%;
|
||||
}
|
||||
2
css/backoffice/layout/dashlet-panel/_all.scss
Normal file
2
css/backoffice/layout/dashlet-panel/_all.scss
Normal file
@@ -0,0 +1,2 @@
|
||||
@import "dashlet-entry";
|
||||
@import "dashlet-panel";
|
||||
51
css/backoffice/layout/dashlet-panel/_dashlet-entry.scss
Normal file
51
css/backoffice/layout/dashlet-panel/_dashlet-entry.scss
Normal file
@@ -0,0 +1,51 @@
|
||||
// TODO 3.3 Cleanup variables
|
||||
|
||||
.ibo-dashlet-entry {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 10px;
|
||||
|
||||
|
||||
border: 1px solid $ibo-color-grey-300;
|
||||
border-radius: 5px;
|
||||
padding: 12px;
|
||||
background-color: $ibo-color-grey-100;
|
||||
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
|
||||
&:hover {
|
||||
background-color: $ibo-color-grey-200;
|
||||
border-color: $ibo-color-grey-400;
|
||||
}
|
||||
&:active {
|
||||
background-color: $ibo-color-grey-50;
|
||||
border-color: $ibo-color-grey-500;
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-dashlet-entry--icon {
|
||||
flex-shrink: 0;
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
}
|
||||
|
||||
.ibo-dashlet-entry--content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
overflow-x: hidden;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.ibo-dashlet-entry--title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: $ibo-color-grey-900;
|
||||
}
|
||||
|
||||
.ibo-dashlet-entry--description {
|
||||
font-size: 12px;
|
||||
color: $ibo-color-grey-700;
|
||||
|
||||
}
|
||||
66
css/backoffice/layout/dashlet-panel/_dashlet-panel.scss
Normal file
66
css/backoffice/layout/dashlet-panel/_dashlet-panel.scss
Normal file
@@ -0,0 +1,66 @@
|
||||
// TODO 3.3 Cleanup variables
|
||||
|
||||
.ibo-dashlet-panel {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 326px;
|
||||
background-color: $ibo-color-white-100;
|
||||
}
|
||||
|
||||
.ibo-dashlet-panel--title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
height: 55px;
|
||||
background-color: $ibo-color-white-200;
|
||||
padding: 0 16px;
|
||||
flex-grow: 0;
|
||||
@extend %common-font-ral-med-300;
|
||||
}
|
||||
|
||||
.ibo-dashlet-panel--entries, .ibo-dashlet-panel--form-container {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
|
||||
padding: 16px;
|
||||
gap: 12px;
|
||||
&.ibo-is-hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-center-container:has(ibo-dashboard[data-edit-mode="view"]) .ibo-dashlet-panel{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ibo-dashlet-panel--form-container turbo-frame {
|
||||
height: 100%;
|
||||
}
|
||||
.ibo-dashlet-panel--form-container .ibo-form {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
> .form {
|
||||
overflow: auto;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-dashlet-panel--form-container--buttons {
|
||||
display: flex;
|
||||
flex-direction: row !important;
|
||||
justify-content: end;
|
||||
align-items: center;
|
||||
|
||||
margin: 0 -16px -16px -16px;
|
||||
padding: 0 16px;
|
||||
min-height: 60px;
|
||||
|
||||
background-color: $ibo-color-grey-50;
|
||||
border-top: solid 1px $ibo-color-grey-400;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
@import "extension-details";
|
||||
@@ -1,65 +0,0 @@
|
||||
$ibo-extension-details--information--metadata--padding: $ibo-spacing-200 !default;
|
||||
$ibo-extension-details--information--metadata--delimiter: "-" !default;
|
||||
$ibo-extension-details--information--metadata--color: $ibo-color-grey-700 !default;
|
||||
$ibo-extension-details--actions--button--padding-y: 3px !default;
|
||||
$ibo-extension-details--actions--button--padding-x: $ibo-button--padding-x !default;
|
||||
|
||||
.ibo-extension-details {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ibo-extension-details--information {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.ibo-extension-details--actions {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.ibo-extension-details--information--label {
|
||||
@extend %ibo-font-ral-med-150;
|
||||
}
|
||||
|
||||
.ibo-extension-details--information--metadata {
|
||||
@extend %ibo-font-ral-med-100;
|
||||
color: $ibo-extension-details--information--metadata--color;
|
||||
}
|
||||
|
||||
.ibo-extension-details--information--description {
|
||||
@extend %ibo-font-ral-med-100;
|
||||
}
|
||||
|
||||
.ibo-extension-details--information--metadata span + span:before {
|
||||
content: $ibo-extension-details--information--metadata--delimiter;
|
||||
padding-left: $ibo-extension-details--information--metadata--padding;
|
||||
padding-right: $ibo-extension-details--information--metadata--padding;
|
||||
}
|
||||
|
||||
//ibo-extension-details can have other ibo-extension-details inside its ibo-extension-details--information--description in the setup. We need to only affect direct children
|
||||
.ibo-extension-details:has(>.ibo-extension-details--actions input:is([type="checkbox"], [type="radio"]):checked){
|
||||
&>.ibo-extension-details--information>.ibo-extension-details--information--label .ibo-badge.unchecked {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
//Merging the two lines below with :is([type="checkbox"], [type="radio"]) will generate a warning in scss compiler
|
||||
.ibo-extension-details:has(>.ibo-extension-details--actions input[type="checkbox"]:not(:checked)),
|
||||
.ibo-extension-details:has(>.ibo-extension-details--actions input[type="radio"]:not(:checked)) {
|
||||
&>.ibo-extension-details--information>.ibo-extension-details--information--label .ibo-badge.checked {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.ibo-extension-details--actions > button {
|
||||
padding: $ibo-extension-details--actions--button--padding-y $ibo-extension-details--actions--button--padding-x;
|
||||
}
|
||||
|
||||
.ibo-extension-details--actions:has(.toggler-install:not(:disabled)) .ibo-popover-menu--section a[data-resource-id="force_uninstall"] {
|
||||
display: none;
|
||||
}
|
||||
@@ -5,11 +5,9 @@
|
||||
|
||||
$ibo-multi-column--margin-x: -$ibo-spacing-500 !default; /* This is to compensate columns padding and make the whole multicolumn align with the parent borders (cf. Bootstrap rows / cols) */
|
||||
$ibo-multi-column--margin-y: $ibo-spacing-0 !default;
|
||||
$ibo-multi-column--row-gap: $ibo-spacing-800 !default;
|
||||
|
||||
.ibo-multi-column {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: $ibo-multi-column--margin-y $ibo-multi-column--margin-x;
|
||||
row-gap: $ibo-multi-column--row-gap;
|
||||
}
|
||||
@@ -21,7 +21,6 @@ $text-strong: inherit !default;
|
||||
* See https://bulma.io/documentation/elements/content/
|
||||
*/
|
||||
$content-block-margin-bottom: 0 !default;
|
||||
$content-blockquote-background-color: $ibo-color-grey-200 !default;
|
||||
|
||||
/* Table: Reset style as much as possible to match rich text editor preview, which is the browser's default stylesheet.
|
||||
* As there is no way to avoid bulma rules, we simply make them invalid by setting an invalid variable value, the rules will then be ignored by the browser.
|
||||
|
||||
2
css/backoffice/vendors/_selectize.scss
vendored
2
css/backoffice/vendors/_selectize.scss
vendored
@@ -29,7 +29,7 @@ $ibo-vendors-selectize--element--active--background: $ibo-color-blue-100 !defaul
|
||||
$ibo-vendors-selectize--element--active--color: $ibo-color-grey-900 !default;
|
||||
|
||||
$ibo-vendors-selectize--dropdown--background-color: $ibo-vendors-selectize-input--background-color !default;
|
||||
$ibo-vendors-selectize--dropdown--color: $ibo-vendors-selectize-input--color !default;
|
||||
$ibo-vendors-selectize--dropdown--color: $ibo-vendors-selectize-input--color!default;
|
||||
|
||||
$ibo-vendors-selectize--header--padding-x: 8px !default;
|
||||
$ibo-vendors-selectize--header--padding-y: 5px !default;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -316,34 +316,29 @@ fieldset {
|
||||
background-color: #F7FAFC;
|
||||
padding: 10px;
|
||||
.wiz-choice{
|
||||
&:not(:checked) ~ label .checked{
|
||||
&:checked ~ .description {
|
||||
#itop-ticket-mgmt-simple-ticket-enhanced-portal:not(:checked),
|
||||
#itop-ticket-mgmt-itil-enhanced-portal:not(:checked) {
|
||||
~ .description::after {
|
||||
content: "Legacy portal is no longer part of iTop, by leaving this option unchecked your portal users won't be able to access iTop anymore.";
|
||||
display: block;
|
||||
margin-top: 0.5em;
|
||||
font-weight: bold;
|
||||
color: $legacy-portal-removal-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:not(:checked) ~ label .setup-extension-tag.checked{
|
||||
display:none;
|
||||
}
|
||||
&:checked ~ label .unchecked{
|
||||
&:checked ~ label .setup-extension-tag.unchecked{
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-extension-details:has(>.ibo-extension-details--actions>input:checked) {
|
||||
.ibo-extension-details:has(#itop-ticket-mgmt-simple-ticket-enhanced-portal:not(:checked), #itop-ticket-mgmt-itil-enhanced-portal:not(:checked)) {
|
||||
.ibo-extension-details--information--description::after {
|
||||
content: "Legacy portal is no longer part of iTop, by leaving this option unchecked your portal users won't be able to access iTop anymore.";
|
||||
display: block;
|
||||
margin-top: 0.5em;
|
||||
font-weight: bold;
|
||||
color: $legacy-portal-removal-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-extension-details--information--metadata{
|
||||
color: $ibo-color-grey-800;
|
||||
}
|
||||
|
||||
.choice-disabled {
|
||||
color: $ibo-color-grey-700;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 1.17rem;
|
||||
@@ -527,12 +522,10 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-setup-summary-title, .ibo-setup-summary-title:visited, .ibo-setup-summary-title:hover {
|
||||
font-size: $ibo-font-size-150;
|
||||
color: inherit;
|
||||
.ibo-setup-summary-title {
|
||||
font-size: $ibo-font-size-150;
|
||||
}
|
||||
|
||||
|
||||
#ibo-setup-licenses--components-list {
|
||||
background-color: $ibo-color-white-200;
|
||||
padding: 12px;
|
||||
@@ -612,7 +605,6 @@ body {
|
||||
color:#a00000;
|
||||
}
|
||||
.setup-extension-tag {
|
||||
display: inline-flex;
|
||||
background-color: grey;
|
||||
border-radius: 8px;
|
||||
padding-left: 3px;
|
||||
@@ -638,21 +630,6 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-extension-details {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.ibo-extension-details--actions input{
|
||||
margin:0.2em 0.5em;
|
||||
width: 12px;
|
||||
}
|
||||
:not(.ibo-badge) ~ .ibo-badge{
|
||||
margin-left:0.5em;
|
||||
}
|
||||
.ibo-extension-details--information--label i{
|
||||
font-size : 0.9em;
|
||||
margin-left:0.3em;
|
||||
}
|
||||
|
||||
.setup--wizard-choice--label + .setup--wizard-choice--more-info {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
@@ -704,6 +681,9 @@ body {
|
||||
overflow: auto;
|
||||
text-align: center;
|
||||
}
|
||||
#installation_progress {
|
||||
display: none;
|
||||
}
|
||||
#fresh_content{
|
||||
border: 0;
|
||||
min-height: 300px;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.3">
|
||||
<module_parameters>
|
||||
<parameters id="authent-local" _delta="define">
|
||||
<password_validation.pattern>^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).{12,}$</password_validation.pattern>
|
||||
<password_validation.pattern>^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).{8,}$</password_validation.pattern>
|
||||
<password_validation.message type="hash"/>
|
||||
</parameters>
|
||||
</module_parameters>
|
||||
|
||||
@@ -29,7 +29,7 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Heslo nemůže uživatel změnit.',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Heslo bylo obnoveno',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Termín, kdy bylo heslo změneno',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Heslo musí obsahovat minimálně 12 znaků a musí obsahovat minimálně jedno velké písmeno, jedno malé písmeno, jedno číslo a speciální znak.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Heslo musí obsahovat minimálně 8 znaků a musí obsahovat minimálně jedno velké písmeno, jedno malé písmeno, jedno číslo a speciální znak.',
|
||||
'UserLocal:password:expiration' => 'Níže uvedená pole vyžadují rozšíření',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Nastavení exspirace "Jednorázového hesla" nelze u vlastního účtu uživatele.',
|
||||
]);
|
||||
|
||||
@@ -28,7 +28,7 @@ Dict::Add('DA DA', 'Danish', 'Dansk', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'UserLocal:password:expiration' => 'The fields below require an extension~~',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
|
||||
]);
|
||||
|
||||
@@ -28,7 +28,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => '',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Letzte Passworterneuerung',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Letztes Änderungsdatum',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Das Passwort muss mindestens 12 Zeichen lang sein und Großbuchstaben, Kleinbuchstaben, Zahlen und Sonderzeichen enthalten.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Das Passwort entspricht nicht dem in den Konfigurationsregeln hinterlegten RegEx-Ausdruck',
|
||||
'UserLocal:password:expiration' => 'Die folgenden Felder benötigen eine '.ITOP_APPLICATION_SHORT.' Erweiterung',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Das setzen des Passwortablaufs auf "Einmalpasswort" ist für den eigenen Benutzer nicht erlaubt.',
|
||||
]);
|
||||
|
||||
@@ -55,7 +55,7 @@ Dict::Add('EN US', 'English', 'English', [
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed',
|
||||
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.',
|
||||
'UserLocal:password:expiration' => 'The fields below require an extension',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User',
|
||||
]);
|
||||
|
||||
@@ -55,7 +55,7 @@ Dict::Add('EN GB', 'British English', 'British English', [
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed',
|
||||
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.',
|
||||
'UserLocal:password:expiration' => 'The fields below require an extension',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User',
|
||||
]);
|
||||
|
||||
@@ -25,7 +25,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'El usuario no puede cambiar la contraseña.',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Renovación de contraseña',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Cuando fue el último cambio de contraseña',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'La contraseña debe ser de al menos 12 caracteres e incluir mayúsculas, minúsculas, números y caracteres especiales.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'La contraseña debe ser de al menos 8 caracteres e incluír mayúsculas, minúsculas, números y caracteres especiales.',
|
||||
'UserLocal:password:expiration' => 'El siguiente campo requiere una extensión',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Configurar expiración de contraseña para "ontraseña de un solo uso" no está permitido para su propio Usuario',
|
||||
]);
|
||||
|
||||
@@ -27,7 +27,7 @@ Dict::Add('FR FR', 'French', 'Français', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => '',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Mot de passe changé le',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Dernière date à laquelle le mot de passe a été changé',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Le mot de passe doit contenir au moins 12 caractères, avec minuscule, majuscule, nombre et caractère spécial.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Le mot de passe doit contenir au moins 8 caractères, avec minuscule, majuscule, nombre et caractère spécial.',
|
||||
'UserLocal:password:expiration' => 'Les champs ci-dessous nécessitent une extension',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Impossible de mettre "Usage unique" comme validité du mot de passe pour son propre utilisateur.',
|
||||
]);
|
||||
|
||||
@@ -27,7 +27,7 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'A felhasználó nem változtathat jelszót.',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Jelszó megújítás ideje',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'A jelszó legutóbbi módosításának időpontja',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'A jelszónak legalább 12 karakterből kell állnia, és tartalmaznia kell nagybetűket, kisbetűket, numerikus és speciális karaktereket.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'A jelszónak legalább 8 karakterből kell állnia, és tartalmaznia kell nagybetűket, kisbetűket, numerikus és speciális karaktereket.',
|
||||
'UserLocal:password:expiration' => 'Az alábbi mezőkhöz egy bővítmény szükséges',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'A jelszó lejárati idejének beállítása "Egyszeri jelszóra" nem engedélyezett a saját Felhasználó számára.',
|
||||
]);
|
||||
|
||||
@@ -27,7 +27,7 @@ Dict::Add('IT IT', 'Italian', 'Italiano', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'La password non può essere cambiata dall\'utente.',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Rinnovo della password',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Quando è stata cambiata l\'ultima volta la password',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'La password deve essere di almeno 12 caratteri e includere lettere maiuscole, minuscole, numeri e caratteri speciali.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'La password deve essere di almeno 8 caratteri e includere lettere maiuscole, minuscole, numeri e caratteri speciali.',
|
||||
'UserLocal:password:expiration' => 'I campi sottostanti richiedono un\'estensione',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Impostare la scadenza della password su "Password monouso" non è consentito per il proprio utente',
|
||||
]);
|
||||
|
||||
@@ -28,7 +28,7 @@ Dict::Add('JA JP', 'Japanese', '日本語', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'UserLocal:password:expiration' => 'The fields below require an extension~~',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
|
||||
]);
|
||||
|
||||
@@ -28,7 +28,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'De gebruiker kan dit wachtwoord niet veranderen.',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Wachtwoord laatst aangepast',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Tijdstip waarop het wachtwoord het laatst aangepast werd.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Het wachtwoord bestaat uit minstens 12 tekens en bestaat uit een mix van minstens 1 hoofdletter, kleine letter, cijfer en speciaal teken.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Het wachtwoord bestaat uit minstens 8 tekens en bestaat uit een mix van minstens 1 hoofdletter, kleine letter, cijfer en speciaal teken.',
|
||||
'UserLocal:password:expiration' => 'De velden hieronder vereisen een extensie.',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Je kan geen eenmalig wachtwoord instellen voor je eigen gebruiker.',
|
||||
]);
|
||||
|
||||
@@ -27,7 +27,7 @@ Dict::Add('PL PL', 'Polish', 'Polski', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Hasło nie może być zmienione przez użytkownika.',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Odnowienie hasła',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Kiedy ostatnio zmieniano hasło',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Hasło musi mieć co najmniej 12 znaków i zawierać duże, małe litery, cyfry i znaki specjalne.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Hasło musi mieć co najmniej 8 znaków i zawierać duże, małe litery, cyfry i znaki specjalne.',
|
||||
'UserLocal:password:expiration' => 'Poniższe pola wymagają rozszerzenia',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Ustawienie wygaśnięcia hasła "Hasło jednorazowe" nie jest dozwolone dla własnego użytkownika',
|
||||
]);
|
||||
|
||||
@@ -28,7 +28,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Дата изменения пароля',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Когда пароль был изменен в последний раз',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Пароль должен содержать не менее 12 символов и включать прописные, строчные, числовые и специальные символы.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Пароль должен содержать не менее 8 символов и включать прописные, строчные, числовые и специальные символы.',
|
||||
'UserLocal:password:expiration' => 'Поля требуют наличия доп. расширения',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
|
||||
]);
|
||||
|
||||
@@ -27,7 +27,7 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'UserLocal:password:expiration' => 'The fields below require an extension~~',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
|
||||
]);
|
||||
|
||||
@@ -28,7 +28,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'UserLocal:password:expiration' => 'The fields below require an extension~~',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
|
||||
]);
|
||||
|
||||
@@ -51,7 +51,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => '用户不允许修改密码.',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => '密码更新',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => '上次修改密码的时间',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => '密码必须至少12个字符, 包含大小写, 数字和特殊字符.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => '密码必须至少8个字符, 包含大小写, 数字和特殊字符.',
|
||||
'UserLocal:password:expiration' => '下面的区域需要插件扩展',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => '不允许用户为自己设置 "一次性密码" 的失效期限',
|
||||
]);
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user