mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-23 04:14:12 +01:00
Compare commits
58 Commits
feature/67
...
faf/module
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18c52f1a71 | ||
|
|
045a9c5658 | ||
|
|
4367bb2868 | ||
|
|
5f1765504c | ||
|
|
bca79a1a06 | ||
|
|
8c30199e9f | ||
|
|
996b98c4c7 | ||
|
|
5dea3c7ce2 | ||
|
|
609215857b | ||
|
|
ae83d677a3 | ||
|
|
8a017b6e95 | ||
|
|
dda99616a5 | ||
|
|
e37322e631 | ||
|
|
64bec2181f | ||
|
|
6ed0f8ef3a | ||
|
|
b194a0b17c | ||
|
|
4207b25bde | ||
|
|
9f2da19da0 | ||
|
|
21c4ffa9e3 | ||
|
|
b31a7fac54 | ||
|
|
46d8591761 | ||
|
|
18a66e6884 | ||
|
|
6c13464011 | ||
|
|
809d1a5f7d | ||
|
|
217e890650 | ||
|
|
d7e02d3859 | ||
|
|
1d7e776f6d | ||
|
|
6319d0a9cb | ||
|
|
5c051a30d3 | ||
|
|
78e13fffb4 | ||
|
|
bb16108f04 | ||
|
|
460a3e356e | ||
|
|
dd68017020 | ||
|
|
0281721638 | ||
|
|
15103dc49f | ||
|
|
2ee68ff819 | ||
|
|
44972f34e5 | ||
|
|
65c9145b10 | ||
|
|
a104310379 | ||
|
|
f5cce23bb4 | ||
|
|
0f39106b56 | ||
|
|
ee993ef80a | ||
|
|
0a4180f7fc | ||
|
|
b56113aada | ||
|
|
c82c150411 | ||
|
|
00818f411f | ||
|
|
a6a459967e | ||
|
|
2ba4109343 | ||
|
|
738664e560 | ||
|
|
d48bc15211 | ||
|
|
32f0cfb091 | ||
|
|
20393b55b9 | ||
|
|
437296eab9 | ||
|
|
7cf6f65265 | ||
|
|
491f55c7bd | ||
|
|
7b44ec23a1 | ||
|
|
f00b8d529c | ||
|
|
af44ffd0ad |
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Extend this class instead of implementing iApplicationUIExtension if you don't need to overload
|
||||
*
|
||||
* @api
|
||||
* @package UIExtensibilityAPI
|
||||
* @since 2.7.0
|
||||
*/
|
||||
abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function OnDisplayProperties($oObject, \Combodo\iTop\Application\WebPage\WebPage $oPage, $bEditMode = false)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function OnDisplayRelations($oObject, \Combodo\iTop\Application\WebPage\WebPage $oPage, $bEditMode = false)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function OnFormSubmit($oObject, $sFormPrefix = '')
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function OnFormCancel($sTempId)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function EnumUsedAttributes($oObject)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetIcon($oObject)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetHilightClass($oObject)
|
||||
{
|
||||
return HILIGHT_CLASS_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function EnumAllowedActions(DBObjectSet $oSet)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Extend this class instead of iPageUIBlockExtension if you don't need to overload all methods
|
||||
*
|
||||
* @api
|
||||
* @package UIBlockExtensibilityAPI
|
||||
* @since 3.0.0
|
||||
*/
|
||||
abstract class AbstractPageUIBlockExtension implements iPageUIBlockExtension
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetBannerBlock()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetHeaderBlock()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetFooterBlock()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Extend this class instead of implementing iPreferencesExtension if you don't need to overload all methods
|
||||
*
|
||||
* @api
|
||||
* @package PreferencesExtensibilityAPI
|
||||
* @since 2.7.0
|
||||
*/
|
||||
abstract class AbstractPreferencesExtension implements iPreferencesExtension
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function DisplayPreferences(\Combodo\iTop\Application\WebPage\WebPage $oPage)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function ApplyPreferences(\Combodo\iTop\Application\WebPage\WebPage $oPage, $sOperation)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Inherit from this class to provide messages to be displayed in the "Welcome Popup"
|
||||
*
|
||||
* @api
|
||||
* @since 3.2.0
|
||||
*/
|
||||
abstract class AbstractWelcomePopupExtension implements iWelcomePopupExtension
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetIconRelPath(): string
|
||||
{
|
||||
return \Combodo\iTop\Application\Branding::$aLogoPaths[\Combodo\iTop\Application\Branding::ENUM_LOGO_TYPE_MAIN_LOGO_COMPACT]['default'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetMessages(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function AcknowledgeMessage(string $sMessageId): void
|
||||
{
|
||||
// No need to process the acknowledgment notice by default
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Base class for the various types of custom menus
|
||||
*
|
||||
* @api
|
||||
* @package UIExtensibilityAPI
|
||||
* @since 2.0
|
||||
*/
|
||||
abstract class ApplicationPopupMenuItem
|
||||
{
|
||||
/** @ignore */
|
||||
protected $sUID;
|
||||
/** @ignore */
|
||||
protected $sLabel;
|
||||
/** @ignore */
|
||||
protected $sTooltip;
|
||||
/** @ignore */
|
||||
protected $sIconClass;
|
||||
/** @ignore */
|
||||
protected $aCssClasses;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $sUID The unique identifier of this menu in iTop... make sure you pass something unique enough
|
||||
* @param string $sLabel The display label of the menu (must be localized)
|
||||
* @api
|
||||
*/
|
||||
public function __construct($sUID, $sLabel)
|
||||
{
|
||||
$this->sUID = $sUID;
|
||||
$this->sLabel = $sLabel;
|
||||
$this->sTooltip = '';
|
||||
$this->sIconClass = '';
|
||||
$this->aCssClasses = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the UID
|
||||
*
|
||||
* @return string The unique identifier
|
||||
* @ignore
|
||||
*/
|
||||
public function GetUID()
|
||||
{
|
||||
return $this->sUID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the label
|
||||
*
|
||||
* @return string The label
|
||||
* @ignore
|
||||
*/
|
||||
public function GetLabel()
|
||||
{
|
||||
return $this->sLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CSS classes
|
||||
*
|
||||
* @return array
|
||||
* @ignore
|
||||
*/
|
||||
public function GetCssClasses()
|
||||
{
|
||||
return $this->aCssClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $aCssClasses
|
||||
* @api
|
||||
*/
|
||||
public function SetCssClasses($aCssClasses)
|
||||
{
|
||||
$this->aCssClasses = $aCssClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a CSS class to the CSS classes that will be put on the menu item
|
||||
*
|
||||
* @param $sCssClass
|
||||
* @api
|
||||
*/
|
||||
public function AddCssClass($sCssClass)
|
||||
{
|
||||
$this->aCssClasses[] = $sCssClass;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $sTooltip
|
||||
*
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function SetTooltip($sTooltip)
|
||||
{
|
||||
$this->sTooltip = $sTooltip;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetTooltip()
|
||||
{
|
||||
return $this->sTooltip;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sIconClass
|
||||
*
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function SetIconClass($sIconClass)
|
||||
{
|
||||
$this->sIconClass = $sIconClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetIconClass()
|
||||
{
|
||||
return $this->sIconClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the components to create a popup menu item in HTML
|
||||
*
|
||||
* @return array A hash array: array('label' => , 'url' => , 'target' => , 'onclick' => )
|
||||
* @ignore
|
||||
*/
|
||||
abstract public function GetMenuItem();
|
||||
|
||||
/** @ignore */
|
||||
public function GetLinkedScripts()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
13
application/applicationextension/backoffice/JSButtonItem.php
Normal file
13
application/applicationextension/backoffice/JSButtonItem.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class for adding an item as a button that runs some JS code
|
||||
*
|
||||
* @api
|
||||
* @package UIExtensibilityAPI
|
||||
* @since 2.0
|
||||
*/
|
||||
class JSButtonItem extends JSPopupMenuItem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class for adding an item into a popup menu that triggers some Javascript code
|
||||
*
|
||||
* Note: This works only in the backoffice, {@see \JSButtonItem} for the end-user portal
|
||||
*
|
||||
* @api
|
||||
* @package UIExtensibilityAPI
|
||||
* @since 2.0
|
||||
*/
|
||||
class JSPopupMenuItem extends ApplicationPopupMenuItem
|
||||
{
|
||||
/** @ignore */
|
||||
protected $sJsCode;
|
||||
/** @ignore */
|
||||
protected $sUrl;
|
||||
/** @ignore */
|
||||
protected $aIncludeJSFiles;
|
||||
|
||||
/**
|
||||
* Class for adding an item that triggers some Javascript code
|
||||
*
|
||||
* @param string $sUID The unique identifier of this menu in iTop... make sure you pass something unique enough
|
||||
* @param string $sLabel The display label of the menu (must be localized)
|
||||
* @param string $sJSCode In case the menu consists in executing some havascript code inside the page, pass it here. If supplied $sURL
|
||||
* ans $sTarget will be ignored
|
||||
* @param array $aIncludeJSFiles An array of file URLs to be included (once) to provide some JS libraries for the page.
|
||||
* @api
|
||||
*/
|
||||
public function __construct($sUID, $sLabel, $sJSCode, $aIncludeJSFiles = array())
|
||||
{
|
||||
parent::__construct($sUID, $sLabel);
|
||||
$this->sJsCode = $sJSCode;
|
||||
$this->sUrl = '#';
|
||||
$this->aIncludeJSFiles = $aIncludeJSFiles;
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
public function GetMenuItem()
|
||||
{
|
||||
// Note: the semicolumn is a must here!
|
||||
return array(
|
||||
'label' => $this->GetLabel(),
|
||||
'onclick' => $this->GetJsCode() . '; return false;',
|
||||
'url' => $this->GetUrl(),
|
||||
'css_classes' => $this->GetCssClasses(),
|
||||
'icon_class' => $this->sIconClass,
|
||||
'tooltip' => $this->sTooltip
|
||||
);
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
public function GetLinkedScripts()
|
||||
{
|
||||
return $this->aIncludeJSFiles;
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
public function GetJsCode()
|
||||
{
|
||||
return $this->sJsCode;
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
public function GetUrl()
|
||||
{
|
||||
return $this->sUrl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class for adding a separator (horizontal line, not selectable) the output
|
||||
* will automatically reduce several consecutive separators to just one
|
||||
*
|
||||
* @api
|
||||
* @package UIExtensibilityAPI
|
||||
* @since 2.0
|
||||
*/
|
||||
class SeparatorPopupMenuItem extends ApplicationPopupMenuItem
|
||||
{
|
||||
static $idx = 0;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @api
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('_separator_' . (self::$idx++), '');
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
public function GetMenuItem()
|
||||
{
|
||||
return array('label' => '<hr class="menu-separator">', 'url' => '', 'css_classes' => $this->aCssClasses);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class for adding an item as a button that browses to the given URL
|
||||
*
|
||||
* @api
|
||||
* @package UIExtensibilityAPI
|
||||
* @since 2.0
|
||||
*/
|
||||
class URLButtonItem extends URLPopupMenuItem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class for adding an item into a popup menu that browses to the given URL
|
||||
*
|
||||
* Note: This works only in the backoffice, {@see \URLButtonItem} for the end-user portal
|
||||
*
|
||||
* @api
|
||||
* @package UIExtensibilityAPI
|
||||
* @since 2.0
|
||||
*/
|
||||
class URLPopupMenuItem extends ApplicationPopupMenuItem
|
||||
{
|
||||
/** @ignore */
|
||||
protected $sUrl;
|
||||
/** @ignore */
|
||||
protected $sTarget;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $sUID The unique identifier of this menu in iTop... make sure you pass something unique enough
|
||||
* @param string $sLabel The display label of the menu (must be localized)
|
||||
* @param string $sUrl If the menu is an hyperlink, provide the absolute hyperlink here
|
||||
* @param string $sTarget In case the menu is an hyperlink and a specific target is needed (_blank for example), pass it here
|
||||
* @api
|
||||
*/
|
||||
public function __construct($sUID, $sLabel, $sUrl, $sTarget = '_top')
|
||||
{
|
||||
parent::__construct($sUID, $sLabel);
|
||||
$this->sUrl = $sUrl;
|
||||
$this->sTarget = $sTarget;
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
public function GetMenuItem()
|
||||
{
|
||||
return array('label' => $this->GetLabel(),
|
||||
'url' => $this->GetUrl(),
|
||||
'target' => $this->GetTarget(),
|
||||
'css_classes' => $this->aCssClasses,
|
||||
'icon_class' => $this->sIconClass,
|
||||
'tooltip' => $this->sTooltip
|
||||
);
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
public function GetUrl()
|
||||
{
|
||||
return $this->sUrl;
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
public function GetTarget()
|
||||
{
|
||||
return $this->sTarget;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implement this interface to change the behavior of the GUI for some objects.
|
||||
*
|
||||
* All methods are invoked by iTop for a given object. There are basically two usages:
|
||||
*
|
||||
* 1) To tweak the form of an object, you will have to implement a specific behavior within:
|
||||
*
|
||||
* * OnDisplayProperties (bEditMode = true)
|
||||
* * OnFormSubmit
|
||||
* * OnFormCancel
|
||||
*
|
||||
* 2) To tune the display of the object details, you can use:
|
||||
*
|
||||
* * OnDisplayProperties
|
||||
* * OnDisplayRelations
|
||||
* * GetIcon
|
||||
* * GetHilightClass
|
||||
*
|
||||
* Please note that some of the APIs can be called several times for a single page displayed.
|
||||
* Therefore it is not recommended to perform too many operations, such as querying the database.
|
||||
* A recommended pattern is to cache data by the mean of static members.
|
||||
*
|
||||
* @api
|
||||
* @package UIExtensibilityAPI
|
||||
*/
|
||||
interface iApplicationUIExtension
|
||||
{
|
||||
/**
|
||||
* Invoked when an object is being displayed (wiew or edit)
|
||||
*
|
||||
* The method is called right after the main tab has been displayed.
|
||||
* You can add output to the page, either to change the display, or to add a form input
|
||||
*
|
||||
* Example:
|
||||
* <code>
|
||||
* if ($bEditMode)
|
||||
* {
|
||||
* $oPage->p('Age of the captain: <input type="text" name="captain_age"/>');
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* $oPage->p('Age of the captain: '.$iCaptainAge);
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
* @api
|
||||
*
|
||||
*@param \Combodo\iTop\Application\WebPage\WebPage $oPage The output context
|
||||
* @param boolean $bEditMode True if the edition form is being displayed
|
||||
*
|
||||
* @param DBObject $oObject The object being displayed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function OnDisplayProperties($oObject, \Combodo\iTop\Application\WebPage\WebPage $oPage, $bEditMode = false);
|
||||
|
||||
/**
|
||||
* Invoked when an object is being displayed (wiew or edit)
|
||||
*
|
||||
* The method is called rigth after all the tabs have been displayed
|
||||
*
|
||||
* @api
|
||||
*
|
||||
*@param \Combodo\iTop\Application\WebPage\WebPage $oPage The output context
|
||||
* @param boolean $bEditMode True if the edition form is being displayed
|
||||
*
|
||||
* @param DBObject $oObject The object being displayed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function OnDisplayRelations($oObject, \Combodo\iTop\Application\WebPage\WebPage $oPage, $bEditMode = false);
|
||||
|
||||
/**
|
||||
* Invoked when the end-user clicks on Modify from the object edition form
|
||||
*
|
||||
* The method is called after the changes from the standard form have been
|
||||
* taken into account, and before saving the changes into the database.
|
||||
*
|
||||
* @param DBObject $oObject The object being edited
|
||||
* @param string $sFormPrefix Prefix given to the HTML form inputs
|
||||
*
|
||||
* @return void
|
||||
* @api
|
||||
*/
|
||||
public function OnFormSubmit($oObject, $sFormPrefix = '');
|
||||
|
||||
/**
|
||||
* Invoked when the end-user clicks on Cancel from the object edition form
|
||||
*
|
||||
* Implement here any cleanup. This is necessary when you have injected some
|
||||
* javascript into the edition form, and if that code requires to store temporary data
|
||||
* (this is the case when a file must be uploaded).
|
||||
*
|
||||
* @param string $sTempId Unique temporary identifier made of session_id and transaction_id. It identifies the object in a unique way.
|
||||
*
|
||||
* @return void
|
||||
* @api
|
||||
*/
|
||||
public function OnFormCancel($sTempId);
|
||||
|
||||
/**
|
||||
* Not yet called by the framework!
|
||||
*
|
||||
* Sorry, the verb has been reserved. You must implement it, but it is not called as of now.
|
||||
*
|
||||
* @param DBObject $oObject The object being displayed
|
||||
*
|
||||
* @return string[] desc
|
||||
* @api
|
||||
*/
|
||||
public function EnumUsedAttributes($oObject); // Not yet implemented
|
||||
|
||||
/**
|
||||
* Not yet called by the framework!
|
||||
*
|
||||
* Sorry, the verb has been reserved. You must implement it, but it is not called as of now.
|
||||
*
|
||||
* @param DBObject $oObject The object being displayed
|
||||
*
|
||||
* @return string Path of the icon, relative to the modules directory.
|
||||
* @api
|
||||
*/
|
||||
public function GetIcon($oObject); // Not yet implemented
|
||||
|
||||
/**
|
||||
* Invoked when the object is displayed alone or within a list
|
||||
*
|
||||
* Returns a value influencing the appearance of the object depending on its
|
||||
* state.
|
||||
*
|
||||
* Possible values are:
|
||||
*
|
||||
* * HILIGHT_CLASS_CRITICAL
|
||||
* * HILIGHT_CLASS_WARNING
|
||||
* * HILIGHT_CLASS_OK
|
||||
* * HILIGHT_CLASS_NONE
|
||||
*
|
||||
* @param DBObject $oObject The object being displayed
|
||||
*
|
||||
* @return integer The value representing the mood of the object
|
||||
* @api
|
||||
*/
|
||||
public function GetHilightClass($oObject);
|
||||
|
||||
/**
|
||||
* Called when building the Actions menu for a single object or a list of objects
|
||||
*
|
||||
* Use this to add items to the Actions menu. You will have to specify a label and an URL.
|
||||
*
|
||||
* Example:
|
||||
* <code>
|
||||
* $oObject = $oSet->fetch();
|
||||
* if ($oObject instanceof Sheep)
|
||||
* {
|
||||
* return array('View in my app' => 'http://myserver/view_sheeps?id='.$oObject->Get('name'));
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* return array();
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
* See also iPopupMenuExtension for greater flexibility
|
||||
*
|
||||
* @param DBObjectSet $oSet A set of persistent objects (DBObject)
|
||||
*
|
||||
* @return array
|
||||
* @api
|
||||
*/
|
||||
public function EnumAllowedActions(DBObjectSet $oSet);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implement this interface to add Dict entries
|
||||
*
|
||||
* @see \iTopWebPage::$a_dict_entries
|
||||
* @api
|
||||
* @package BackofficeUIExtensibilityAPI
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeDictEntriesExtension
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
* @see \iTopWebPage::a_dict_entries
|
||||
* @api
|
||||
*/
|
||||
public function GetDictEntries(): array;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implement this interface to add Dict entries prefixes
|
||||
*
|
||||
* @see \iTopWebPage::$a_dict_entries_prefixes
|
||||
* @api
|
||||
* @package BackofficeUIExtensibilityAPI
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeDictEntriesPrefixesExtension
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
* @see \iTopWebPage::a_dict_entries_prefixes
|
||||
* @api
|
||||
*/
|
||||
public function GetDictEntriesPrefixes(): array;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implement this interface to add inline script (JS) to the backoffice pages' head.
|
||||
* Will be executed first, BEFORE the DOM interpretation.
|
||||
*
|
||||
* @see \iTopWebPage::$a_early_scripts
|
||||
* @api
|
||||
* @package BackofficeUIExtensibilityAPI
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeEarlyScriptExtension
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
* @see \iTopWebPage::$a_early_scripts
|
||||
* @api
|
||||
*/
|
||||
public function GetEarlyScript(): string;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implement this interface to add inline script (JS) to the backoffice pages that will be executed right when the DOM is ready.
|
||||
*
|
||||
* @see \iTopWebPage::$a_init_scripts
|
||||
* @api
|
||||
* @package BackofficeUIExtensibilityAPI
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeInitScriptExtension
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
* @see \iTopWebPage::$a_init_scripts
|
||||
* @api
|
||||
*/
|
||||
public function GetInitScript(): string;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implement this interface to add script (JS) files to the backoffice pages
|
||||
*
|
||||
* @see \iTopWebPage::$a_linked_scripts
|
||||
* @api
|
||||
* @package BackofficeUIExtensibilityAPI
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeLinkedScriptsExtension
|
||||
{
|
||||
/**
|
||||
* Each script will be included using this property
|
||||
* @return array An array of absolute URLs to the files to include
|
||||
* @see \iTopWebPage::$a_linked_scripts
|
||||
* @api
|
||||
*/
|
||||
public function GetLinkedScriptsAbsUrls(): array;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implement this interface to add stylesheets (CSS) to the backoffice pages
|
||||
*
|
||||
* @see \iTopWebPage::$a_linked_stylesheets
|
||||
* @api
|
||||
* @package BackofficeUIExtensibilityAPI
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeLinkedStylesheetsExtension
|
||||
{
|
||||
/**
|
||||
* @return array An array of absolute URLs to the files to include
|
||||
* @see \iTopWebPage::$a_linked_stylesheets
|
||||
* @api
|
||||
*/
|
||||
public function GetLinkedStylesheetsAbsUrls(): array;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implement this interface to add inline script (JS) to the backoffice pages that will be executed slightly AFTER the DOM is ready (just after the init. scripts).
|
||||
*
|
||||
* @see \iTopWebPage::$a_ready_scripts
|
||||
* @api
|
||||
* @package BackofficeUIExtensibilityAPI
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeReadyScriptExtension
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
* @see \iTopWebPage::$a_ready_scripts
|
||||
* @api
|
||||
*/
|
||||
public function GetReadyScript(): string;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implement this interface to add sass file (SCSS) to the backoffice pages.
|
||||
* example: return "css/setup.scss"
|
||||
*
|
||||
* @api
|
||||
* @package BackofficeUIExtensibilityAPI
|
||||
* @since 3.3.0
|
||||
*/
|
||||
interface iBackofficeSassExtension
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
* @see \iTopWebPage::$a_styles
|
||||
* @api
|
||||
*/
|
||||
public function GetSass(): string;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implement this interface to add inline script (JS) to the backoffice pages that will be executed immediately, without waiting for the DOM to be ready.
|
||||
*
|
||||
* @see \iTopWebPage::$a_scripts
|
||||
* @api
|
||||
* @package BackofficeUIExtensibilityAPI
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeScriptExtension
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
* @see \iTopWebPage::$a_scripts
|
||||
* @api
|
||||
*/
|
||||
public function GetScript(): string;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implement this interface to add inline style (CSS) to the backoffice pages' head.
|
||||
*
|
||||
* @see \iTopWebPage::$a_styles
|
||||
* @api
|
||||
* @package BackofficeUIExtensibilityAPI
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeStyleExtension
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
* @see \iTopWebPage::$a_styles
|
||||
* @api
|
||||
*/
|
||||
public function GetStyle(): string;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implement this interface to register a new field renderer mapping to either:
|
||||
* - Add the rendering of a new attribute type
|
||||
* - Overload the default rendering of an attribute type
|
||||
*
|
||||
* @since 3.1.0 N°6041
|
||||
*
|
||||
* @experimental Form / Field / Renderer should be used in more places in next iTop releases, which may introduce major API changes
|
||||
*/
|
||||
interface iFieldRendererMappingsExtension
|
||||
{
|
||||
/**
|
||||
* @return array {
|
||||
* array: {
|
||||
* field: string,
|
||||
* form_renderer: string,
|
||||
* field_renderer: string
|
||||
* }
|
||||
* } List of field renderer mapping: FQCN field class, FQCN Form Renderer class, FQCN Field Renderer class
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* ['field' => 'FQCN\FieldA', 'form_renderer' => 'Combodo\iTop\Renderer\Console\ConsoleFormRenderer', 'field_renderer' => 'FQCN\FieldRendererA'],
|
||||
* ['field' => 'FQCN\FieldB', 'form_renderer' => 'Combodo\iTop\Renderer\Console\ConsoleFormRenderer', 'field_renderer' => 'FQCN\FieldRendererB'],
|
||||
* ['field' => 'FQCN\FieldA', 'form_renderer' => 'Combodo\iTop\Renderer\Bootstrap\BsFormRenderer', 'field_renderer' => 'FQCN\FieldRendererA'],
|
||||
* ['field' => 'FQCN\FieldB', 'form_renderer' => 'Combodo\iTop\Renderer\Bootstrap\BsFormRenderer', 'field_renderer' => 'FQCN\FieldRendererB'],
|
||||
* ]
|
||||
* ```
|
||||
*/
|
||||
public static function RegisterSupportedFields(): array;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implement this interface to add content to any iTopWebPage
|
||||
*
|
||||
* There are 3 places where content can be added:
|
||||
*
|
||||
* * The north pane: (normaly empty/hidden) at the top of the page, spanning the whole
|
||||
* width of the page
|
||||
* * The south pane: (normaly empty/hidden) at the bottom of the page, spanning the whole
|
||||
* width of the page
|
||||
* * The admin banner (two tones gray background) at the left of the global search.
|
||||
* Limited space, use it for short messages
|
||||
*
|
||||
* Each of the methods of this interface is supposed to return the HTML to be inserted at
|
||||
* the specified place and can use the passed iTopWebPage object to add javascript or CSS definitions
|
||||
*
|
||||
* @api
|
||||
* @package BackofficeUIExtensibilityAPI
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iPageUIBlockExtension
|
||||
{
|
||||
/**
|
||||
* Add content to the "admin banner"
|
||||
*
|
||||
* @api
|
||||
* @return \Combodo\iTop\Application\UI\Base\iUIBlock|null The Block to add into the page
|
||||
*/
|
||||
public function GetBannerBlock();
|
||||
|
||||
/**
|
||||
* Add content to the header of the page
|
||||
*
|
||||
* @api
|
||||
* @return \Combodo\iTop\Application\UI\Base\iUIBlock|null The Block to add into the page
|
||||
*/
|
||||
public function GetHeaderBlock();
|
||||
|
||||
/**
|
||||
* Add content to the footer of the page
|
||||
*
|
||||
* @api
|
||||
* @return \Combodo\iTop\Application\UI\Base\iUIBlock|null The Block to add into the page
|
||||
*/
|
||||
public function GetFooterBlock();
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* New extension to add menu items in the "popup" menus inside iTop. Provides a greater flexibility than
|
||||
* iApplicationUIExtension::EnumAllowedActions.
|
||||
*
|
||||
* To add some menus into iTop, declare a class that implements this interface, it will be called automatically
|
||||
* by the application, as long as the class definition is included somewhere in the code
|
||||
*
|
||||
* @api
|
||||
* @package UIExtensibilityAPI
|
||||
* @since 2.0
|
||||
*/
|
||||
interface iPopupMenuExtension
|
||||
{
|
||||
/**
|
||||
* Insert an item into the Actions menu of a list
|
||||
*
|
||||
* $param is a DBObjectSet containing the list of objects
|
||||
* @api
|
||||
*/
|
||||
const MENU_OBJLIST_ACTIONS = 1;
|
||||
/**
|
||||
* Insert an item into the Toolkit menu of a list
|
||||
*
|
||||
* $param is a DBObjectSet containing the list of objects
|
||||
* @api
|
||||
*/
|
||||
const MENU_OBJLIST_TOOLKIT = 2;
|
||||
/**
|
||||
* Insert an item into the Actions menu on an object details page
|
||||
*
|
||||
* $param is a DBObject instance: the object currently displayed
|
||||
* @api
|
||||
*/
|
||||
const MENU_OBJDETAILS_ACTIONS = 3;
|
||||
/**
|
||||
* Insert an item into the Dashboard menu
|
||||
*
|
||||
* The dashboad menu is shown on the top right corner when a dashboard
|
||||
* is being displayed.
|
||||
*
|
||||
* $param is a Dashboard instance: the dashboard currently displayed
|
||||
* @api
|
||||
*/
|
||||
const MENU_DASHBOARD_ACTIONS = 4;
|
||||
/**
|
||||
* Insert an item into the User menu (upper right corner)
|
||||
*
|
||||
* $param is null
|
||||
* @api
|
||||
*/
|
||||
const MENU_USER_ACTIONS = 5;
|
||||
/**
|
||||
* Insert an item into the Action menu on an object item in an objects list in the portal
|
||||
*
|
||||
* $param is an array('portal_id' => $sPortalId, 'object' => $oObject) containing the portal id and a DBObject instance (the object on
|
||||
* the current line)
|
||||
* @api
|
||||
*/
|
||||
const PORTAL_OBJLISTITEM_ACTIONS = 7;
|
||||
/**
|
||||
* Insert an item into the Action menu on an object details page in the portal
|
||||
*
|
||||
* $param is an array('portal_id' => $sPortalId, 'object' => $oObject) containing the portal id and a DBObject instance (the object
|
||||
* currently displayed)
|
||||
* @api
|
||||
*/
|
||||
const PORTAL_OBJDETAILS_ACTIONS = 8;
|
||||
|
||||
/**
|
||||
* Insert an item into the Actions menu of a list in the portal
|
||||
* Note: This is not implemented yet !
|
||||
*
|
||||
* $param is an array('portal_id' => $sPortalId, 'object_set' => $oSet) containing DBObjectSet containing the list of objects
|
||||
*
|
||||
* @todo
|
||||
*/
|
||||
const PORTAL_OBJLIST_ACTIONS = 6;
|
||||
/**
|
||||
* Insert an item into the user menu of the portal
|
||||
* Note: This is not implemented yet !
|
||||
*
|
||||
* $param is the portal id
|
||||
*
|
||||
* @todo
|
||||
*/
|
||||
const PORTAL_USER_ACTIONS = 9;
|
||||
/**
|
||||
* Insert an item into the navigation menu of the portal
|
||||
* Note: This is not implemented yet !
|
||||
*
|
||||
* $param is the portal id
|
||||
*
|
||||
* @todo
|
||||
*/
|
||||
const PORTAL_MENU_ACTIONS = 10;
|
||||
|
||||
/**
|
||||
* Get the list of items to be added to a menu.
|
||||
*
|
||||
* This method is called by the framework for each menu.
|
||||
* The items will be inserted in the menu in the order of the returned array.
|
||||
*
|
||||
* @param int $iMenuId The identifier of the type of menu, as listed by the constants MENU_xxx
|
||||
* @param mixed $param Depends on $iMenuId, see the constants defined above
|
||||
*
|
||||
* @return object[] An array of ApplicationPopupMenuItem or an empty array if no action is to be added to the menu
|
||||
* @api
|
||||
*/
|
||||
public static function EnumItems($iMenuId, $param);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @api
|
||||
* @package PreferencesExtensibilityAPI
|
||||
* @since 2.7.0
|
||||
*/
|
||||
interface iPreferencesExtension
|
||||
{
|
||||
/**
|
||||
* @api
|
||||
*
|
||||
* @param \Combodo\iTop\Application\WebPage\WebPage $oPage
|
||||
*
|
||||
*/
|
||||
public function DisplayPreferences(\Combodo\iTop\Application\WebPage\WebPage $oPage);
|
||||
|
||||
/**
|
||||
* @api
|
||||
*
|
||||
* @param string $sOperation
|
||||
*
|
||||
* @param \Combodo\iTop\Application\WebPage\WebPage $oPage
|
||||
*
|
||||
* @return bool true if the operation has been used
|
||||
*/
|
||||
public function ApplyPreferences(\Combodo\iTop\Application\WebPage\WebPage $oPage, $sOperation);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Interface to provide messages to be displayed in the "Welcome Popup"
|
||||
*
|
||||
* @api
|
||||
* @since 3.2.0
|
||||
*/
|
||||
interface iWelcomePopupExtension
|
||||
{
|
||||
// Importance for ordering messages
|
||||
// Just two levels since less important messages have nothing to do in the welcome popup
|
||||
public const ENUM_IMPORTANCE_CRITICAL = 0;
|
||||
public const ENUM_IMPORTANCE_HIGH = 1;
|
||||
public const DEFAULT_IMPORTANCE = self::ENUM_IMPORTANCE_HIGH;
|
||||
|
||||
/**
|
||||
* Overload this method if you need to display an icon representing the provider (eg. your own company logo, module icon, ...)
|
||||
*
|
||||
* @return string Relative path (from app. root) of the icon representing the provider
|
||||
* @api
|
||||
*/
|
||||
public function GetIconRelPath(): string;
|
||||
|
||||
/**
|
||||
* @return \Combodo\iTop\Application\WelcomePopup\Message[]
|
||||
* @api
|
||||
*/
|
||||
public function GetMessages(): array;
|
||||
|
||||
/**
|
||||
* Overload this method if the provider needs to do some additional processing after the message ($sMessageId) has been acknowledged by the current user
|
||||
*
|
||||
* @param string $sMessageId
|
||||
* @api
|
||||
*/
|
||||
public function AcknowledgeMessage(string $sMessageId): void;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implement this interface to add files to the backup
|
||||
*
|
||||
* @api
|
||||
* @since 3.2.0
|
||||
*/
|
||||
interface iBackupExtraFilesExtension
|
||||
{
|
||||
/**
|
||||
* @return string[] Array of relative paths (from app root) for files and directories to be included in the backup
|
||||
* @api
|
||||
*/
|
||||
public function GetExtraFilesRelPaths(): array;
|
||||
}
|
||||
25
application/applicationextension/iKPILoggerExtension.php
Normal file
25
application/applicationextension/iKPILoggerExtension.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* KPI logging extensibility point
|
||||
*
|
||||
* KPI Logger extension
|
||||
*/
|
||||
interface iKPILoggerExtension
|
||||
{
|
||||
/**
|
||||
* Init the statistics collected
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function InitStats();
|
||||
|
||||
/**
|
||||
* Add a new KPI to the stats
|
||||
*
|
||||
* @param \Combodo\iTop\Core\Kpi\KpiLogData $oKpiLogData
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function LogOperation($oKpiLogData);
|
||||
}
|
||||
16
application/applicationextension/iModuleExtension.php
Normal file
16
application/applicationextension/iModuleExtension.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Helpers for modules extensibility, with discover performed by the MetaModel.
|
||||
*
|
||||
*
|
||||
* @api
|
||||
* @package Extensibility
|
||||
*/
|
||||
interface iModuleExtension
|
||||
{
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function __construct();
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Login finite state machine
|
||||
*
|
||||
* Execute the action corresponding to the current login state.
|
||||
*
|
||||
* * If a page is displayed, the action must exit at this point
|
||||
* * if LoginWebPage::LOGIN_FSM_RETURN_ERROR is returned $iErrorCode must be set
|
||||
* * if LoginWebPage::LOGIN_FSM_RETURN_OK is returned then the login is OK and terminated
|
||||
* * if LoginWebPage::LOGIN_FSM_CONTINUE is returned then the FSM will proceed to next plugin or to next state
|
||||
*
|
||||
* @api
|
||||
* @package LoginExtensibilityAPI
|
||||
* @since 2.7.0
|
||||
*/
|
||||
abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
abstract public function ListSupportedLoginModes();
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function LoginAction($sLoginState, &$iErrorCode)
|
||||
{
|
||||
switch ($sLoginState) {
|
||||
case LoginWebPage::LOGIN_STATE_START:
|
||||
return $this->OnStart($iErrorCode);
|
||||
|
||||
case LoginWebPage::LOGIN_STATE_MODE_DETECTION:
|
||||
return $this->OnModeDetection($iErrorCode);
|
||||
|
||||
case LoginWebPage::LOGIN_STATE_READ_CREDENTIALS:
|
||||
return $this->OnReadCredentials($iErrorCode);
|
||||
|
||||
case LoginWebPage::LOGIN_STATE_CHECK_CREDENTIALS:
|
||||
return $this->OnCheckCredentials($iErrorCode);
|
||||
|
||||
case LoginWebPage::LOGIN_STATE_CREDENTIALS_OK:
|
||||
return $this->OnCredentialsOK($iErrorCode);
|
||||
|
||||
case LoginWebPage::LOGIN_STATE_USER_OK:
|
||||
return $this->OnUsersOK($iErrorCode);
|
||||
|
||||
case LoginWebPage::LOGIN_STATE_CONNECTED:
|
||||
return $this->OnConnected($iErrorCode);
|
||||
|
||||
case LoginWebPage::LOGIN_STATE_ERROR:
|
||||
return $this->OnError($iErrorCode);
|
||||
}
|
||||
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization
|
||||
*
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
*
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
|
||||
* @api
|
||||
*/
|
||||
protected function OnStart(&$iErrorCode)
|
||||
{
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect login mode explicitly without respecting configured order (legacy mode)
|
||||
* In most case do nothing here
|
||||
*
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
*
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
|
||||
* @api
|
||||
*/
|
||||
protected function OnModeDetection(&$iErrorCode)
|
||||
{
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the credentials either if login mode is empty or set to yours.
|
||||
* This step can be called multiple times by the FSM:
|
||||
* for example:
|
||||
* 1 - display login form
|
||||
* 2 - read the values posted by the user (store that in session)
|
||||
*
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
*
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
|
||||
* @api
|
||||
*/
|
||||
protected function OnReadCredentials(&$iErrorCode)
|
||||
{
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Control the validity of the data from the session
|
||||
* Automatic user provisioning can be done here
|
||||
*
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
*
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
|
||||
* @api
|
||||
*/
|
||||
protected function OnCheckCredentials(&$iErrorCode)
|
||||
{
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
*
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
|
||||
* @api
|
||||
*/
|
||||
protected function OnCredentialsOK(&$iErrorCode)
|
||||
{
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
*
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
|
||||
* @api
|
||||
*/
|
||||
protected function OnUsersOK(&$iErrorCode)
|
||||
{
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
*
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
|
||||
* @api
|
||||
*/
|
||||
protected function OnConnected(&$iErrorCode)
|
||||
{
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
*
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
|
||||
* @api
|
||||
*/
|
||||
protected function OnError(&$iErrorCode)
|
||||
{
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
}
|
||||
17
application/applicationextension/login/iLoginExtension.php
Normal file
17
application/applicationextension/login/iLoginExtension.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @api
|
||||
* @package LoginExtensibilityAPI
|
||||
* @since 2.7.0
|
||||
*/
|
||||
interface iLoginExtension
|
||||
{
|
||||
/**
|
||||
* Return the list of supported login modes for this plugin
|
||||
*
|
||||
* @return array of supported login modes
|
||||
* @api
|
||||
*/
|
||||
public function ListSupportedLoginModes();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @api
|
||||
* @package LoginExtensibilityAPI
|
||||
* @since 2.7.0
|
||||
*/
|
||||
interface iLoginFSMExtension extends iLoginExtension
|
||||
{
|
||||
/**
|
||||
* Execute action for this login state
|
||||
* If a page is displayed, the action must exit at this point
|
||||
* if LoginWebPage::LOGIN_FSM_RETURN_ERROR is returned $iErrorCode must be set
|
||||
* if LoginWebPage::LOGIN_FSM_RETURN_OK is returned then the login is OK and terminated
|
||||
* if LoginWebPage::LOGIN_FSM_CONTINUE is returned then the FSM will proceed to next plugin or state
|
||||
*
|
||||
* @param string $sLoginState (see LoginWebPage::LOGIN_STATE_...)
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
*
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
|
||||
* @api
|
||||
*/
|
||||
public function LoginAction($sLoginState, &$iErrorCode);
|
||||
}
|
||||
17
application/applicationextension/login/iLoginUIExtension.php
Normal file
17
application/applicationextension/login/iLoginUIExtension.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Login page extensibility
|
||||
*
|
||||
* @api
|
||||
* @package UIExtensibilityAPI
|
||||
* @since 2.7.0
|
||||
*/
|
||||
interface iLoginUIExtension extends iLoginExtension
|
||||
{
|
||||
/**
|
||||
* @return LoginTwigContext
|
||||
* @api
|
||||
*/
|
||||
public function GetTwigContext();
|
||||
}
|
||||
15
application/applicationextension/login/iLogoutExtension.php
Normal file
15
application/applicationextension/login/iLogoutExtension.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @api
|
||||
* @package LoginExtensibilityAPI
|
||||
* @since 2.7.0
|
||||
*/
|
||||
interface iLogoutExtension extends iLoginExtension
|
||||
{
|
||||
/**
|
||||
* Execute all actions to log out properly
|
||||
* @api
|
||||
*/
|
||||
public function LogoutAction();
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Extend this class instead of iPortalUIExtension if you don't need to overload all methods
|
||||
*
|
||||
* @api
|
||||
* @package PortalExtensibilityAPI
|
||||
* @since 2.4.0
|
||||
*/
|
||||
abstract class AbstractPortalUIExtension implements iPortalUIExtension
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetCSSFiles(\Symfony\Component\DependencyInjection\Container $oContainer)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetCSSInline(\Symfony\Component\DependencyInjection\Container $oContainer)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetJSFiles(\Symfony\Component\DependencyInjection\Container $oContainer)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetJSInline(\Symfony\Component\DependencyInjection\Container $oContainer)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetBodyHTML(\Symfony\Component\DependencyInjection\Container $oContainer)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetMainContentHTML(\Symfony\Component\DependencyInjection\Container $oContainer)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetNavigationMenuHTML(\Symfony\Component\DependencyInjection\Container $oContainer)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implement this interface to add content to any enhanced portal page
|
||||
*
|
||||
* @api
|
||||
* @package PortalExtensibilityAPI
|
||||
*
|
||||
* @since 2.4.0 interface creation
|
||||
* @since 2.7.0 change method signatures due to Silex to Symfony migration
|
||||
*/
|
||||
interface iPortalUIExtension
|
||||
{
|
||||
const ENUM_PORTAL_EXT_UI_BODY = 'Body';
|
||||
const ENUM_PORTAL_EXT_UI_NAVIGATION_MENU = 'NavigationMenu';
|
||||
const ENUM_PORTAL_EXT_UI_MAIN_CONTENT = 'MainContent';
|
||||
|
||||
/**
|
||||
* Returns an array of CSS file urls
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\Container $oContainer
|
||||
*
|
||||
* @return array
|
||||
* @api
|
||||
*/
|
||||
public function GetCSSFiles(\Symfony\Component\DependencyInjection\Container $oContainer);
|
||||
|
||||
/**
|
||||
* Returns inline (raw) CSS
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\Container $oContainer
|
||||
*
|
||||
* @return string
|
||||
* @api
|
||||
*/
|
||||
public function GetCSSInline(\Symfony\Component\DependencyInjection\Container $oContainer);
|
||||
|
||||
/**
|
||||
* Returns an array of JS file urls
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\Container $oContainer
|
||||
*
|
||||
* @return array
|
||||
* @api
|
||||
*/
|
||||
public function GetJSFiles(\Symfony\Component\DependencyInjection\Container $oContainer);
|
||||
|
||||
/**
|
||||
* Returns raw JS code
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\Container $oContainer
|
||||
*
|
||||
* @return string
|
||||
* @api
|
||||
*/
|
||||
public function GetJSInline(\Symfony\Component\DependencyInjection\Container $oContainer);
|
||||
|
||||
/**
|
||||
* Returns raw HTML code to put at the end of the <body> tag
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\Container $oContainer
|
||||
*
|
||||
* @return string
|
||||
* @api
|
||||
*/
|
||||
public function GetBodyHTML(\Symfony\Component\DependencyInjection\Container $oContainer);
|
||||
|
||||
/**
|
||||
* Returns raw HTML code to put at the end of the #main-wrapper element
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\Container $oContainer
|
||||
*
|
||||
* @return string
|
||||
* @api
|
||||
*/
|
||||
public function GetMainContentHTML(\Symfony\Component\DependencyInjection\Container $oContainer);
|
||||
|
||||
/**
|
||||
* Returns raw HTML code to put at the end of the #topbar and #sidebar elements
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\Container $oContainer
|
||||
*
|
||||
* @return string
|
||||
* @api
|
||||
*/
|
||||
public function GetNavigationMenuHTML(\Symfony\Component\DependencyInjection\Container $oContainer);
|
||||
}
|
||||
102
application/applicationextension/rest/RestResult.php
Normal file
102
application/applicationextension/rest/RestResult.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Minimal REST response structure. Derive this structure to add response data and error codes.
|
||||
*
|
||||
* @api
|
||||
* @package RESTAPI
|
||||
* @since 2.0.1
|
||||
*/
|
||||
class RestResult
|
||||
{
|
||||
/**
|
||||
* Result: no issue has been encountered
|
||||
* @api
|
||||
*/
|
||||
const OK = 0;
|
||||
/**
|
||||
* Result: missing/wrong credentials or the user does not have enough rights to perform the requested operation
|
||||
* @api
|
||||
*/
|
||||
const UNAUTHORIZED = 1;
|
||||
/**
|
||||
* Result: the parameter 'version' is missing
|
||||
* @api
|
||||
*/
|
||||
const MISSING_VERSION = 2;
|
||||
/**
|
||||
* Result: the parameter 'json_data' is missing
|
||||
* @api
|
||||
*/
|
||||
const MISSING_JSON = 3;
|
||||
/**
|
||||
* Result: the input structure is not a valid JSON string
|
||||
* @api
|
||||
*/
|
||||
const INVALID_JSON = 4;
|
||||
/**
|
||||
* Result: the parameter 'auth_user' is missing, authentication aborted
|
||||
* @api
|
||||
*/
|
||||
const MISSING_AUTH_USER = 5;
|
||||
/**
|
||||
* Result: the parameter 'auth_pwd' is missing, authentication aborted
|
||||
* @api
|
||||
*/
|
||||
const MISSING_AUTH_PWD = 6;
|
||||
/**
|
||||
* Result: no operation is available for the specified version
|
||||
* @api
|
||||
*/
|
||||
const UNSUPPORTED_VERSION = 10;
|
||||
/**
|
||||
* Result: the requested operation is not valid for the specified version
|
||||
* @api
|
||||
*/
|
||||
const UNKNOWN_OPERATION = 11;
|
||||
/**
|
||||
* Result: the requested operation cannot be performed because it can cause data (integrity) loss
|
||||
* @api
|
||||
*/
|
||||
const UNSAFE = 12;
|
||||
/**
|
||||
* Result: the request page number is not valid. It must be an integer greater than 0
|
||||
* @api
|
||||
*/
|
||||
const INVALID_PAGE = 13;
|
||||
/**
|
||||
* Result: the operation could not be performed, see the message for troubleshooting
|
||||
* @api
|
||||
*/
|
||||
const INTERNAL_ERROR = 100;
|
||||
|
||||
/**
|
||||
* Default constructor - ok!
|
||||
* @api
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->code = RestResult::OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result code
|
||||
* @var int
|
||||
* @api
|
||||
*/
|
||||
public $code;
|
||||
/**
|
||||
* Result message
|
||||
* @var string
|
||||
* @api
|
||||
*/
|
||||
public $message;
|
||||
|
||||
/**
|
||||
* Sanitize the content of this result to hide sensitive information
|
||||
*/
|
||||
public function SanitizeContent()
|
||||
{
|
||||
// The default implementation does nothing
|
||||
}
|
||||
}
|
||||
368
application/applicationextension/rest/RestUtils.php
Normal file
368
application/applicationextension/rest/RestUtils.php
Normal file
@@ -0,0 +1,368 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Helpers for implementing REST services
|
||||
*
|
||||
* @api
|
||||
* @package RESTAPI
|
||||
*/
|
||||
class RestUtils
|
||||
{
|
||||
/**
|
||||
* Registering tracking information. Any further object modification be associated with the given comment, when the modification gets
|
||||
* recorded into the DB
|
||||
*
|
||||
* @param StdClass $oData Structured input data. Must contain 'comment'.
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
* @api
|
||||
*
|
||||
*/
|
||||
public static function InitTrackingComment($oData)
|
||||
{
|
||||
$sComment = self::GetMandatoryParam($oData, 'comment');
|
||||
CMDBObject::SetTrackInfo($sComment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a mandatory parameter from from a Rest/Json structure.
|
||||
*
|
||||
* @param string $sParamName Name of the parameter to fetch from the input data
|
||||
* @param StdClass $oData Structured input data. Must contain the entry defined by sParamName.
|
||||
*
|
||||
* @return mixed parameter value if present
|
||||
* @throws Exception If the parameter is missing
|
||||
* @api
|
||||
*/
|
||||
public static function GetMandatoryParam($oData, $sParamName)
|
||||
{
|
||||
if (isset($oData->$sParamName)) {
|
||||
return $oData->$sParamName;
|
||||
} else {
|
||||
throw new Exception("Missing parameter '$sParamName'");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read an optional parameter from a Rest/Json structure.
|
||||
*
|
||||
* @param string $sParamName Name of the parameter to fetch from the input data
|
||||
* @param mixed $default Default value if the parameter is not found in the input data
|
||||
*
|
||||
* @param StdClass $oData Structured input data.
|
||||
*
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
* @api
|
||||
*/
|
||||
public static function GetOptionalParam($oData, $sParamName, $default)
|
||||
{
|
||||
if (isset($oData->$sParamName)) {
|
||||
return $oData->$sParamName;
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read a class from a Rest/Json structure.
|
||||
*
|
||||
* @param string $sParamName Name of the parameter to fetch from the input data
|
||||
* @param StdClass $oData Structured input data. Must contain the entry defined by sParamName.
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception If the parameter is missing or the class is unknown
|
||||
* @api
|
||||
*/
|
||||
public static function GetClass($oData, $sParamName)
|
||||
{
|
||||
$sClass = self::GetMandatoryParam($oData, $sParamName);
|
||||
if (!MetaModel::IsValidClass($sClass)) {
|
||||
throw new Exception("$sParamName: '$sClass' is not a valid class'");
|
||||
}
|
||||
|
||||
return $sClass;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read a list of attribute codes from a Rest/Json structure.
|
||||
*
|
||||
* @param StdClass $oData Structured input data.
|
||||
* @param string $sParamName Name of the parameter to fetch from the input data
|
||||
*
|
||||
* @param string $sClass Name of the class
|
||||
*
|
||||
* @return array of class => list of attributes (see RestResultWithObjects::AddObject that uses it)
|
||||
* @throws Exception
|
||||
* @api
|
||||
*/
|
||||
public static function GetFieldList($sClass, $oData, $sParamName)
|
||||
{
|
||||
$sFields = self::GetOptionalParam($oData, $sParamName, '*');
|
||||
$aShowFields = array();
|
||||
if ($sFields == '*') {
|
||||
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
|
||||
$aShowFields[$sClass][] = $sAttCode;
|
||||
}
|
||||
} elseif ($sFields == '*+') {
|
||||
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sRefClass) {
|
||||
foreach (MetaModel::ListAttributeDefs($sRefClass) as $sAttCode => $oAttDef) {
|
||||
$aShowFields[$sRefClass][] = $sAttCode;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach (explode(',', $sFields) as $sAttCode) {
|
||||
$sAttCode = trim($sAttCode);
|
||||
if (($sAttCode != 'id') && (!MetaModel::IsValidAttCode($sClass, $sAttCode))) {
|
||||
throw new Exception("$sParamName: invalid attribute code '$sAttCode'");
|
||||
}
|
||||
$aShowFields[$sClass][] = $sAttCode;
|
||||
}
|
||||
}
|
||||
|
||||
return $aShowFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and interpret object search criteria from a Rest/Json structure
|
||||
*
|
||||
* @param string $sClass Name of the class
|
||||
* @param StdClass $oCriteria Hash of attribute code => value (can be a substructure or a scalar, depending on the nature of the
|
||||
* attriute)
|
||||
*
|
||||
* @return object The object found
|
||||
* @throws Exception If the input structure is not valid or it could not find exactly one object
|
||||
* @api
|
||||
*/
|
||||
protected static function FindObjectFromCriteria($sClass, $oCriteria)
|
||||
{
|
||||
$aCriteriaReport = array();
|
||||
if (isset($oCriteria->finalclass)) {
|
||||
if (!MetaModel::IsValidClass($oCriteria->finalclass)) {
|
||||
throw new Exception("finalclass: Unknown class '" . $oCriteria->finalclass . "'");
|
||||
}
|
||||
if (!MetaModel::IsParentClass($sClass, $oCriteria->finalclass)) {
|
||||
throw new Exception("finalclass: '" . $oCriteria->finalclass . "' is not a child class of '$sClass'");
|
||||
}
|
||||
$sClass = $oCriteria->finalclass;
|
||||
}
|
||||
$oSearch = new DBObjectSearch($sClass);
|
||||
foreach ($oCriteria as $sAttCode => $value) {
|
||||
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
||||
$oSearch->AddCondition($sAttCode, $realValue, '=');
|
||||
if (is_object($value) || is_array($value)) {
|
||||
$value = json_encode($value);
|
||||
}
|
||||
$aCriteriaReport[] = "$sAttCode: $value ($realValue)";
|
||||
}
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$iCount = $oSet->Count();
|
||||
if ($iCount == 0) {
|
||||
throw new Exception("No item found with criteria: " . implode(', ', $aCriteriaReport));
|
||||
} elseif ($iCount > 1) {
|
||||
throw new Exception("Several items found ($iCount) with criteria: " . implode(', ', $aCriteriaReport));
|
||||
}
|
||||
$res = $oSet->Fetch();
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find an object from a polymorph search specification (Rest/Json)
|
||||
*
|
||||
* @param mixed $key Either search criteria (substructure), or an object or an OQL string.
|
||||
* @param bool $bAllowNullValue Allow the cases such as key = 0 or key = {null} and return null then
|
||||
* @param string $sClass Name of the class
|
||||
*
|
||||
* @return DBObject The object found
|
||||
* @throws Exception If the input structure is not valid or it could not find exactly one object
|
||||
*
|
||||
* @api
|
||||
* @see DBObject::CheckChangedExtKeysValues() generic method to check that we can access the linked object isn't used in that use case because values can be literal, OQL, friendlyname
|
||||
*/
|
||||
public static function FindObjectFromKey($sClass, $key, $bAllowNullValue = false)
|
||||
{
|
||||
if (is_object($key)) {
|
||||
$res = static::FindObjectFromCriteria($sClass, $key);
|
||||
} elseif (is_numeric($key)) {
|
||||
if ($bAllowNullValue && ($key == 0)) {
|
||||
$res = null;
|
||||
} else {
|
||||
$res = MetaModel::GetObject($sClass, $key, false);
|
||||
if (is_null($res)) {
|
||||
throw new Exception("Invalid object $sClass::$key");
|
||||
}
|
||||
}
|
||||
} elseif (is_string($key)) {
|
||||
// OQL
|
||||
$oSearch = DBObjectSearch::FromOQL($key);
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$iCount = $oSet->Count();
|
||||
if ($iCount == 0) {
|
||||
throw new Exception("No item found for query: $key");
|
||||
} elseif ($iCount > 1) {
|
||||
throw new Exception("Several items found ($iCount) for query: $key");
|
||||
}
|
||||
$res = $oSet->Fetch();
|
||||
} else {
|
||||
throw new Exception("Wrong format for key");
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search objects from a polymorph search specification (Rest/Json)
|
||||
*
|
||||
* @param string $sClass Name of the class
|
||||
* @param mixed $key Either search criteria (substructure), or an object or an OQL string.
|
||||
* @param int $iLimit The limit of results to return
|
||||
* @param int $iOffset The offset of results to return
|
||||
*
|
||||
* @return DBObjectSet The search result set
|
||||
* @throws Exception If the input structure is not valid
|
||||
* @api
|
||||
*/
|
||||
public static function GetObjectSetFromKey($sClass, $key, $iLimit = 0, $iOffset = 0)
|
||||
{
|
||||
if (is_object($key)) {
|
||||
if (isset($key->finalclass)) {
|
||||
$sClass = $key->finalclass;
|
||||
if (!MetaModel::IsValidClass($sClass)) {
|
||||
throw new Exception("finalclass: Unknown class '$sClass'");
|
||||
}
|
||||
}
|
||||
|
||||
$oSearch = new DBObjectSearch($sClass);
|
||||
foreach ($key as $sAttCode => $value) {
|
||||
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
||||
$oSearch->AddCondition($sAttCode, $realValue, '=');
|
||||
}
|
||||
} elseif (is_numeric($key)) {
|
||||
$oSearch = new DBObjectSearch($sClass);
|
||||
$oSearch->AddCondition('id', $key);
|
||||
} elseif (is_string($key)) {
|
||||
// OQL
|
||||
try {
|
||||
$oSearch = DBObjectSearch::FromOQL($key);
|
||||
} catch (Exception $e) {
|
||||
throw new CoreOqlException('Query failed to execute', [
|
||||
'query' => $key,
|
||||
'exception_class' => get_class($e),
|
||||
'exception_message' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
throw new Exception("Wrong format for key");
|
||||
}
|
||||
$oObjectSet = new DBObjectSet($oSearch, array(), array(), null, $iLimit, $iOffset);
|
||||
|
||||
return $oObjectSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpret the Rest/Json value and get a valid attribute value
|
||||
*
|
||||
* @param string $sAttCode Attribute code
|
||||
* @param mixed $value Depending on the type of attribute (a scalar, or search criteria, or list of related objects...)
|
||||
* @param string $sClass Name of the class
|
||||
*
|
||||
* @return mixed The value that can be used with DBObject::Set()
|
||||
* @throws Exception If the specification of the value is not valid.
|
||||
* @api
|
||||
*/
|
||||
public static function MakeValue($sClass, $sAttCode, $value)
|
||||
{
|
||||
try {
|
||||
if (!MetaModel::IsValidAttCode($sClass, $sAttCode)) {
|
||||
throw new Exception("Unknown attribute");
|
||||
}
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef instanceof AttributeExternalKey) {
|
||||
$oExtKeyObject = static::FindObjectFromKey($oAttDef->GetTargetClass(), $value, true /* allow null */);
|
||||
$value = ($oExtKeyObject != null) ? $oExtKeyObject->GetKey() : 0;
|
||||
} elseif ($oAttDef instanceof AttributeLinkedSet) {
|
||||
if (!is_array($value)) {
|
||||
throw new Exception("A link set must be defined by an array of objects");
|
||||
}
|
||||
$sLnkClass = $oAttDef->GetLinkedClass();
|
||||
$aLinks = array();
|
||||
foreach ($value as $oValues) {
|
||||
$oLnk = static::MakeObjectFromFields($sLnkClass, $oValues);
|
||||
// Fix for N°1939
|
||||
if (($oAttDef instanceof AttributeLinkedSetIndirect) && ($oLnk->Get($oAttDef->GetExtKeyToRemote()) == 0)) {
|
||||
continue;
|
||||
}
|
||||
$aLinks[] = $oLnk;
|
||||
}
|
||||
$value = DBObjectSet::FromArray($sLnkClass, $aLinks);
|
||||
} elseif ($oAttDef instanceof AttributeTagSet) {
|
||||
if (!is_array($value)) {
|
||||
throw new Exception("A tag set must be defined by an array of tag codes");
|
||||
}
|
||||
$value = $oAttDef->FromJSONToValue($value);
|
||||
} else {
|
||||
$value = $oAttDef->FromJSONToValue($value);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
throw new Exception("$sAttCode: " . $e->getMessage(), $e->getCode());
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpret a Rest/Json structure that defines attribute values, and build an object
|
||||
*
|
||||
* @param array $aFields A hash of attribute code => value specification.
|
||||
* @param string $sClass Name of the class
|
||||
*
|
||||
* @return DBObject The newly created object
|
||||
* @throws Exception If the specification of the values is not valid
|
||||
* @api
|
||||
*/
|
||||
public static function MakeObjectFromFields($sClass, $aFields)
|
||||
{
|
||||
$oObject = MetaModel::NewObject($sClass);
|
||||
foreach ($aFields as $sAttCode => $value) {
|
||||
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
||||
try {
|
||||
$oObject->Set($sAttCode, $realValue);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception("$sAttCode: " . $e->getMessage(), $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
return $oObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpret a Rest/Json structure that defines attribute values, and update the given object
|
||||
*
|
||||
* @param array $aFields A hash of attribute code => value specification.
|
||||
* @param DBObject $oObject The object being modified
|
||||
*
|
||||
* @return DBObject The object modified
|
||||
* @throws Exception If the specification of the values is not valid
|
||||
* @api
|
||||
*/
|
||||
public static function UpdateObjectFromFields($oObject, $aFields)
|
||||
{
|
||||
$sClass = get_class($oObject);
|
||||
foreach ($aFields as $sAttCode => $value) {
|
||||
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
||||
try {
|
||||
$oObject->Set($sAttCode, $realValue);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception("$sAttCode: " . $e->getMessage(), $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
return $oObject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* A REST service provider implementing this interface will have its input JSON data sanitized for logging purposes
|
||||
*
|
||||
* @see \iRestServiceProvider
|
||||
* @since 2.7.13, 3.2.1-1
|
||||
*/
|
||||
interface iRestInputSanitizer
|
||||
{
|
||||
public function SanitizeJsonInput(string $sJsonInput): string;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implement this interface to add new operations to the REST/JSON web service
|
||||
*
|
||||
* @api
|
||||
* @package RESTExtensibilityAPI
|
||||
* @since 2.0.1
|
||||
*/
|
||||
interface iRestServiceProvider
|
||||
{
|
||||
/**
|
||||
* Enumerate services delivered by this class
|
||||
*
|
||||
* @param string $sVersion The version (e.g. 1.0) supported by the services
|
||||
*
|
||||
* @return array An array of hash 'verb' => verb, 'description' => description
|
||||
* @api
|
||||
*/
|
||||
public function ListOperations($sVersion);
|
||||
|
||||
/**
|
||||
* Enumerate services delivered by this class
|
||||
*
|
||||
* @param string $sVersion The version (e.g. 1.0) supported by the services
|
||||
* @param string $sVerb
|
||||
* @param array $aParams
|
||||
*
|
||||
* @return RestResult The standardized result structure (at least a message)
|
||||
* @api
|
||||
*/
|
||||
public function ExecOperation($sVersion, $sVerb, $aParams);
|
||||
}
|
||||
@@ -214,6 +214,15 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
*/
|
||||
protected static bool $bBlockEventDBLinksChanged = false;
|
||||
|
||||
/**
|
||||
* If set to true, the object is considered as modified, whatever the actual state is.
|
||||
* This is used when an object is modified indirectly (eg. through a linked set)
|
||||
*
|
||||
* @var bool
|
||||
*
|
||||
* @since 3.3.0 N°8210 - Remove iApplicationObjectExtension
|
||||
*/
|
||||
private bool $bIsMarkedAsModified = false;
|
||||
|
||||
/**
|
||||
* Constructor from a row of data (as a hash 'attcode' => value)
|
||||
@@ -4542,21 +4551,6 @@ HTML;
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected function PostInsertActions(): void
|
||||
{
|
||||
parent::PostInsertActions();
|
||||
|
||||
// Invoke extensions after insertion (the object must exist, have an id, etc.)
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins(iApplicationObjectExtension::class) as $oExtensionInstance) {
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBInsert()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* Attaches InlineImages to the current object
|
||||
@@ -4589,21 +4583,6 @@ HTML;
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected function PostUpdateActions(array $aChanges): void
|
||||
{
|
||||
parent::PostUpdateActions($aChanges);
|
||||
|
||||
// Invoke extensions after the update (could be before)
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins(iApplicationObjectExtension::class) as $oExtensionInstance) {
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBUpdate()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBUpdate');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sMessageIdPrefix
|
||||
*
|
||||
@@ -4639,21 +4618,6 @@ HTML;
|
||||
return $oDeletionPlan;
|
||||
}
|
||||
|
||||
final protected function PreDeleteActions(): void
|
||||
{
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBDelete()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oExtensionInstance->OnDBDelete($this, self::GetCurrentChange());
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBDelete');
|
||||
}
|
||||
|
||||
parent::PreDeleteActions();
|
||||
}
|
||||
|
||||
final protected function PostDeleteActions(): void
|
||||
{
|
||||
parent::PostDeleteActions();
|
||||
@@ -4666,25 +4630,20 @@ HTML;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Plugins
|
||||
//
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$bIsModified = $oExtensionInstance->OnIsModified($this);
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnIsModified');
|
||||
if ($bIsModified) {
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified() -> true");
|
||||
return true;
|
||||
} else {
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified() -> false");
|
||||
}
|
||||
}
|
||||
return $this->bIsMarkedAsModified;
|
||||
}
|
||||
|
||||
return false;
|
||||
/**
|
||||
* Override the default modification state of the object.
|
||||
*
|
||||
* The object is considered as modified, whatever the actual state is.
|
||||
* This is used when an object is modified indirectly (eg. through a linked set)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function MarkObjectAsModified(): void
|
||||
{
|
||||
$this->bIsMarkedAsModified = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4698,7 +4657,7 @@ HTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to bypass the checks of user rights when writing this object, could be used in {@link \iApplicationObjectExtension::OnCheckToWrite()}
|
||||
* Whether to bypass the checks of user rights when writing this object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -4727,22 +4686,6 @@ HTML;
|
||||
{
|
||||
parent::DoCheckToWrite();
|
||||
|
||||
// Plugins
|
||||
//
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnCheckToWrite()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$aNewIssues = $oExtensionInstance->OnCheckToWrite($this);
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToWrite');
|
||||
if (is_array($aNewIssues) && (count($aNewIssues) > 0)) // Some extensions return null instead of an empty array
|
||||
{
|
||||
$this->m_aCheckIssues = array_merge($this->m_aCheckIssues, $aNewIssues);
|
||||
}
|
||||
}
|
||||
|
||||
// User rights
|
||||
//
|
||||
if (!$this->bAllowWrite)
|
||||
@@ -4779,22 +4722,6 @@ HTML;
|
||||
{
|
||||
parent::DoCheckToDelete($oDeletionPlan);
|
||||
|
||||
// Plugins
|
||||
//
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnCheckToDelete()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$aNewIssues = $oExtensionInstance->OnCheckToDelete($this);
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToDelete');
|
||||
if (is_array($aNewIssues) && count($aNewIssues) > 0)
|
||||
{
|
||||
$this->m_aDeleteIssues = array_merge($this->m_aDeleteIssues, $aNewIssues);
|
||||
}
|
||||
}
|
||||
|
||||
// User rights
|
||||
//
|
||||
if (! $this->bAllowDelete)
|
||||
@@ -5809,7 +5736,7 @@ JS
|
||||
{
|
||||
$this->NotifyAttachedObjectsOnLinkClassModification();
|
||||
$this->RemoveObjectAwaitingEventDbLinksChanged(get_class($this), $this->GetKey());
|
||||
$this->FireEvent(EVENT_DB_AFTER_WRITE, ['is_new' => $bIsNew, 'changes' => $aChanges, 'stimulus_applied' => $sStimulusBeingApplied]);
|
||||
$this->FireEvent(EVENT_DB_AFTER_WRITE, ['is_new' => $bIsNew, 'changes' => $aChanges, 'stimulus_applied' => $sStimulusBeingApplied, 'cmdb_change' => self::GetCurrentChange()]);
|
||||
}
|
||||
|
||||
//////////////
|
||||
@@ -5847,7 +5774,7 @@ JS
|
||||
final protected function FireEventAfterDelete(): void
|
||||
{
|
||||
$this->NotifyAttachedObjectsOnLinkClassModification();
|
||||
$this->FireEvent(EVENT_DB_AFTER_DELETE);
|
||||
$this->FireEvent(EVENT_DB_AFTER_DELETE, ['cmdb_change' => self::GetCurrentChange()]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"laminas/laminas-mail": "^2.11",
|
||||
"laminas/laminas-servicemanager": "^3.5",
|
||||
"league/oauth2-google": "^4.0.1",
|
||||
"nikic/php-parser": "~5.6.0",
|
||||
"nikic/php-parser": "dev-master",
|
||||
"pear/archive_tar": "~1.4.14",
|
||||
"pelago/emogrifier": "^7.2.0",
|
||||
"psr/log": "^3.0.0",
|
||||
@@ -41,6 +41,10 @@
|
||||
"symfony/stopwatch": "~6.4.0",
|
||||
"symfony/web-profiler-bundle": "~6.4.0"
|
||||
},
|
||||
"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",
|
||||
|
||||
30
composer.lock
generated
30
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "a0021a80fcc9bccc3f577530893e2974",
|
||||
"content-hash": "be4951ced82be6e0ac8c18fa6ddaafc9",
|
||||
"packages": [
|
||||
{
|
||||
"name": "apereo/phpcas",
|
||||
@@ -1020,16 +1020,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v5.6.0",
|
||||
"version": "dev-master",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56"
|
||||
"url": "https://github.com/Combodo/PHP-Parser.git",
|
||||
"reference": "8ffc1239ff48ed2476b2672dbcc939fcdc5b0f7a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/221b0d0fdf1369c71047ad1d18bb5880017bbc56",
|
||||
"reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56",
|
||||
"url": "https://api.github.com/repos/Combodo/PHP-Parser/zipball/8ffc1239ff48ed2476b2672dbcc939fcdc5b0f7a",
|
||||
"reference": "8ffc1239ff48ed2476b2672dbcc939fcdc5b0f7a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1042,13 +1042,14 @@
|
||||
"ircmaxell/php-yacc": "^0.0.7",
|
||||
"phpunit/phpunit": "^9.0"
|
||||
},
|
||||
"default-branch": true,
|
||||
"bin": [
|
||||
"bin/php-parse"
|
||||
],
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.0-dev"
|
||||
"dev-master": "5.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -1056,7 +1057,11 @@
|
||||
"PhpParser\\": "lib/PhpParser"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"PhpParser\\": "test/PhpParser/"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
@@ -1071,10 +1076,9 @@
|
||||
"php"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.6.0"
|
||||
"source": "https://github.com/Combodo/PHP-Parser/tree/master"
|
||||
},
|
||||
"time": "2025-07-27T20:03:57+00:00"
|
||||
"time": "2025-09-09T09:14:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/random_compat",
|
||||
@@ -5243,7 +5247,9 @@
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {},
|
||||
"stability-flags": {
|
||||
"nikic/php-parser": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
|
||||
@@ -8161,7 +8161,7 @@ class AttributeURL extends AttributeString
|
||||
* @since 3.0.3 moved from Config to AttributeURL constant
|
||||
*/
|
||||
public const DEFAULT_VALIDATION_PATTERN = /** @lang RegExp */
|
||||
'(https?|ftp)\://([a-zA-Z0-9+!*(),;?&=\$_.-]+(\:[a-zA-Z0-9+!*(),;?&=\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\:[0-9]{2,5})?(/([a-zA-Z0-9:%+\$_-]\.?)+)*/?(\?[a-zA-Z+&\$_.-][a-zA-Z0-9;:[\]@&%=+/\$_.,-]*)?(#[a-zA-Z0-9_.-][a-zA-Z0-9+\$_.-]*)?';
|
||||
'(https?|ftp)\://([a-zA-Z0-9+!*(),;?&=\$_.-]+(\:[a-zA-Z0-9+!*(),;?&=\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\:[0-9]{2,5})?(/([a-zA-Z0-9:%@+\$_-]\.?)+)*/?(\?[a-zA-Z+&\$_.-][a-zA-Z0-9;:[\]@&%=+/\$_.,-]*)?(#[a-zA-Z0-9_.-][a-zA-Z0-9+\$_.-]*)?';
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
|
||||
@@ -226,27 +226,23 @@ class CMDBSource
|
||||
*/
|
||||
public static function InitServerAndPort($sDbHost, &$sServer, &$iPort)
|
||||
{
|
||||
$aConnectInfo = explode(':', $sDbHost);
|
||||
if ($sDbHost != null) {
|
||||
$aConnectInfo = explode(':', $sDbHost);
|
||||
|
||||
$bUsePersistentConnection = false;
|
||||
if (strcasecmp($aConnectInfo[0], 'p') === 0)
|
||||
{
|
||||
$bUsePersistentConnection = true;
|
||||
$sServer = $aConnectInfo[0].':'.$aConnectInfo[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sServer = $aConnectInfo[0];
|
||||
}
|
||||
$bUsePersistentConnection = false;
|
||||
if (strcasecmp($aConnectInfo[0], 'p') === 0) {
|
||||
$bUsePersistentConnection = true;
|
||||
$sServer = $aConnectInfo[0].':'.$aConnectInfo[1];
|
||||
} else {
|
||||
$sServer = $aConnectInfo[0];
|
||||
}
|
||||
|
||||
$iConnectInfoCount = count($aConnectInfo);
|
||||
if ($bUsePersistentConnection && ($iConnectInfoCount == 3))
|
||||
{
|
||||
$iPort = (int)($aConnectInfo[2]);
|
||||
}
|
||||
else if (!$bUsePersistentConnection && ($iConnectInfoCount == 2))
|
||||
{
|
||||
$iPort = (int)($aConnectInfo[1]);
|
||||
$iConnectInfoCount = count($aConnectInfo);
|
||||
if ($bUsePersistentConnection && ($iConnectInfoCount == 3)) {
|
||||
$iPort = (int)($aConnectInfo[2]);
|
||||
} else if (!$bUsePersistentConnection && ($iConnectInfoCount == 2)) {
|
||||
$iPort = (int)($aConnectInfo[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -1816,6 +1819,7 @@ class Config
|
||||
return (array_key_exists($sPropCode, $this->m_aSettings));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string identifier that can be used for example to name WebStorage/SessionStorage keys (they
|
||||
* are related to a whole domain, and a domain can host multiple itop)
|
||||
@@ -2017,10 +2021,7 @@ class Config
|
||||
$bLoadConfig = false;
|
||||
}
|
||||
|
||||
$this->m_aAddons = array(
|
||||
// Default AddOn, always present can be moved to an official iTop Module later if needed
|
||||
'user rights' => 'addons/userrights/userrightsprofile.class.inc.php',
|
||||
);
|
||||
$this->m_aAddons = [];
|
||||
|
||||
foreach ($this->m_aSettings as $sPropCode => $aSettingInfo)
|
||||
{
|
||||
@@ -2148,19 +2149,13 @@ class Config
|
||||
array('file' => $sConfigFile, 'expected' => '$MySettings'));
|
||||
}
|
||||
|
||||
if (!array_key_exists('addons', $MyModules))
|
||||
{
|
||||
throw new ConfigException('Missing item in configuration file',
|
||||
array('file' => $sConfigFile, 'expected' => '$MyModules[\'addons\']'));
|
||||
}
|
||||
if (!array_key_exists('user rights', $MyModules['addons']))
|
||||
if (!array_key_exists('addons', $MyModules) || !array_key_exists('user rights', $MyModules['addons']))
|
||||
{
|
||||
// Add one, by default
|
||||
$MyModules['addons']['user rights'] = '/addons/userrights/userrightsnull.class.inc.php';
|
||||
$MyModules['addons']['user rights'] = 'addons/userrights/userrightsprofile.class.inc.php';
|
||||
$this->m_aAddons = $MyModules['addons'];
|
||||
}
|
||||
|
||||
$this->m_aAddons = $MyModules['addons'];
|
||||
|
||||
foreach ($MySettings as $sPropCode => $rawvalue)
|
||||
{
|
||||
if ($this->IsProperty($sPropCode))
|
||||
@@ -2264,11 +2259,21 @@ class Config
|
||||
$this->m_aModuleSettings[$sModule][$sProperty] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.3.0 N°8190
|
||||
*/
|
||||
public function GetAddons()
|
||||
{
|
||||
return $this->m_aAddons;
|
||||
if (array_key_exists("user rights", $this->m_aAddons)) {
|
||||
return $this->m_aAddons;
|
||||
} else {
|
||||
return array_merge($this->m_aAddons,['user rights' => 'addons/userrights/userrightsprofile.class.inc.php']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.3.0 N°8190
|
||||
*/
|
||||
public function SetAddons($aAddons)
|
||||
{
|
||||
$this->m_aAddons = $aAddons;
|
||||
@@ -2702,15 +2707,6 @@ class Config
|
||||
{
|
||||
fwrite($hFile, "\t'addons' => {$aParserValue['value']},\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
fwrite($hFile, "\t'addons' => array (\n");
|
||||
foreach ($this->m_aAddons as $sKey => $sFile)
|
||||
{
|
||||
fwrite($hFile, "\t\t'$sKey' => '$sFile',\n");
|
||||
}
|
||||
fwrite($hFile, "\t),\n");
|
||||
}
|
||||
fwrite($hFile, ");\n");
|
||||
fwrite($hFile, '?'.'>'); // Avoid perturbing the syntax highlighting !
|
||||
|
||||
@@ -2856,20 +2852,8 @@ class Config
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($aModuleInfo['installer']))
|
||||
{
|
||||
$sModuleInstallerClass = $aModuleInfo['installer'];
|
||||
if (!class_exists($sModuleInstallerClass))
|
||||
{
|
||||
throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not a PHP class - Module: ".$aModuleInfo['label']);
|
||||
}
|
||||
if (!is_subclass_of($sModuleInstallerClass, 'ModuleInstallerAPI'))
|
||||
{
|
||||
throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not derived from 'ModuleInstallerAPI' - Module: ".$aModuleInfo['label']);
|
||||
}
|
||||
$aCallSpec = array($sModuleInstallerClass, 'BeforeWritingConfig');
|
||||
call_user_func_array($aCallSpec, array($this));
|
||||
}
|
||||
|
||||
RunTimeEnvironment::CallInstallerHandler($aModuleInfo, "BeforeWritingConfig", [$this]);
|
||||
}
|
||||
}
|
||||
$this->SetAddOns($aAddOns);
|
||||
@@ -3037,4 +3021,6 @@ class ConfigPlaceholdersResolver
|
||||
IssueLog::Error($sErrorMessage, self::class, array($sSourceName, $sKey, $sDefault, $sWholeMask));
|
||||
throw new ConfigException($sErrorMessage);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1748,9 +1748,9 @@ abstract class DBObject implements iDisplay
|
||||
* @throws \CoreException
|
||||
* @since 3.0.0 N°4106 Method should not be overloaded anymore for performances reasons. It will be set final in 3.1.0 (N°4107)
|
||||
* @since 3.0.0 N°580 New $sType parameter
|
||||
*
|
||||
* @since 3.3.0 N°4107 Should never be overriden
|
||||
*/
|
||||
public function GetName($sType = FriendlyNameType::SHORT)
|
||||
public final function GetName($sType = FriendlyNameType::SHORT)
|
||||
{
|
||||
return utils::EscapeHtml($this->GetRawName($sType));
|
||||
}
|
||||
|
||||
@@ -7027,7 +7027,7 @@ abstract class MetaModel
|
||||
* @param array $aParams
|
||||
* @param bool $bAllowAllData
|
||||
*
|
||||
* @return \DBObject
|
||||
* @return \DBObject|null
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public static function GetObjectFromOQL($sQuery, $aParams = null, $bAllowAllData = false)
|
||||
@@ -7709,7 +7709,6 @@ abstract class MetaModel
|
||||
'iLoginUIExtension',
|
||||
'iPreferencesExtension',
|
||||
'iApplicationUIExtension',
|
||||
'iApplicationObjectExtension',
|
||||
'iPopupMenuExtension',
|
||||
'iPageUIBlockExtension',
|
||||
'iBackofficeLinkedScriptsExtension',
|
||||
|
||||
@@ -39,6 +39,23 @@
|
||||
//
|
||||
// .site-nav a { color:#BADA55!important; }
|
||||
|
||||
$ibo-shame--switch--width: 36px !default;
|
||||
$ibo-shame--switch--height: 20px !default;
|
||||
|
||||
$ibo-shame--slider--background-color: $ibo-color-secondary-600 !default;
|
||||
|
||||
$ibo-shame--slider--before--height: 15px !default;
|
||||
$ibo-shame--slider--before--width: 15px !default;
|
||||
$ibo-shame--slider--before--background-color: $ibo-color-secondary-300 !default;
|
||||
|
||||
$ibo-shame--slider--is-checked--background-color: $ibo-color-primary-600 !default;
|
||||
|
||||
$ibo-shame--slider--is-focus--box-shadow: 0 0 1px $ibo-color-primary-600 !default;
|
||||
|
||||
$ibo-shame--slider--is-round--border-radius: 20px !default;
|
||||
$ibo-shame--slider--is-round--before--border-radius: 7px !default;
|
||||
|
||||
|
||||
// N°2847 - Recolor svg illustrations with iTop's primary color
|
||||
.ibo-svg-illustration--container > svg *[fill="#6c63ff"]{
|
||||
fill: $ibo-svg-illustration--fill;
|
||||
@@ -56,8 +73,8 @@
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 36px;
|
||||
height: 20px;
|
||||
width: $ibo-shame--switch--width;
|
||||
height: $ibo-shame--switch--height;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
@@ -74,27 +91,27 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: $ibo-color-secondary-600;
|
||||
background-color: $ibo-shame--slider--background-color;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
height: $ibo-shame--slider--before--height;
|
||||
width: $ibo-shame--slider--before--width;
|
||||
left: 3px;
|
||||
bottom: 3px;
|
||||
background-color: $ibo-color-secondary-300;
|
||||
background-color: $ibo-shame--slider--before--background-color;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: $ibo-color-primary-600;
|
||||
background-color: $ibo-shame--slider--is-checked--background-color;
|
||||
}
|
||||
|
||||
input:focus + .slider {
|
||||
box-shadow: 0 0 1px $ibo-color-primary-600;
|
||||
box-shadow: $ibo-shame--slider--is-focus--box-shadow;
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
@@ -103,9 +120,9 @@ input:checked + .slider:before {
|
||||
|
||||
/* Rounded sliders */
|
||||
.slider.round {
|
||||
border-radius: 20px;
|
||||
border-radius: $ibo-shame--slider--is-round--border-radius;
|
||||
}
|
||||
|
||||
.slider.round:before {
|
||||
border-radius: 7px;
|
||||
border-radius: $ibo-shame--slider--is-round--before--border-radius;
|
||||
}
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
.ibo-bulk--bulk-modify--incompatible-attribute {
|
||||
$ibo-bulk--bulk-modify--incompatible-attribute--color--margin-right: $ibo-vendors-selectize--item--icon--margin-right !default;
|
||||
$ibo-bulk--bulk-modify--incompatible-attribute--color: $ibo-color-information-500 !default;
|
||||
|
||||
.ibo-bulk--bulk-modify--incompatible-attribute {
|
||||
&:before{
|
||||
margin-right: $ibo-vendors-selectize--item--icon--margin-right;
|
||||
margin-right: $ibo-bulk--bulk-modify--incompatible-attribute--color--margin-right;
|
||||
@extend %fa-solid-base;
|
||||
content: '\f05a';
|
||||
color: $ibo-color-information-500;
|
||||
color: $ibo-bulk--bulk-modify--incompatible-attribute--color;
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,16 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-linked-set--bulk-tooltip-info--content: '\f05a' !default;
|
||||
$ibo-linked-set--bulk-tooltip-info--color: $ibo-color-information-500 !default;
|
||||
$ibo-linked-set--bulk-tooltip-info--margin-right: $ibo-vendors-selectize--item--icon--margin-right !default;
|
||||
|
||||
.ibo-linked-set--bulk-tooltip-info {
|
||||
font-size: $ibo-font-size-100;
|
||||
@extend %ibo-font-ral-nor-100;
|
||||
&:before{
|
||||
margin-right: $ibo-vendors-selectize--item--icon--margin-right;
|
||||
margin-right: $ibo-linked-set--bulk-tooltip-info--margin-right;
|
||||
@extend %fa-solid-base;
|
||||
content: '\f05a';
|
||||
color: $ibo-color-information-500;
|
||||
content: $ibo-linked-set--bulk-tooltip-info--content;
|
||||
color: $ibo-linked-set--bulk-tooltip-info--color;
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
@import "panel/all";
|
||||
@import "pill/all";
|
||||
@import "dashlet/all";
|
||||
@import "add-to-dashboard";
|
||||
@import "prop-within-details";
|
||||
@import "caselog-entry-form-within-activity-panel";
|
||||
@import "tab-container-within-panel";
|
||||
@import "medallion-with-blocklist";
|
||||
|
||||
@@ -3,13 +3,17 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-medallion-with-blocklist--icon--image--margin-x: auto !default;
|
||||
$ibo-medallion-with-blocklist--icon--image--margin-y: 0 !default;
|
||||
$ibo-medallion-with-blocklist--icon--description--margin-top: $ibo-spacing-400 !default;
|
||||
|
||||
.ibo-block-list--medallion{
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
> .ibo-medallion-icon--image{
|
||||
margin: 0 auto;
|
||||
margin: $ibo-medallion-with-blocklist--icon--image--margin-y $ibo-medallion-with-blocklist--icon--image--margin-x;
|
||||
~ .ibo-medallion-icon--description{
|
||||
margin-top: 12px;
|
||||
margin-top: $ibo-medallion-with-blocklist--icon--description--margin-top;
|
||||
}
|
||||
}
|
||||
> .ibo-medallion-icon--description{
|
||||
|
||||
@@ -9,8 +9,4 @@
|
||||
|
||||
.ibo-details .ibo-prop--apply {
|
||||
display: table-column;
|
||||
}
|
||||
|
||||
.ibo-details {
|
||||
margin-top: 5px;
|
||||
}
|
||||
@@ -15,6 +15,13 @@ $ibo-panel-with-tab-container--margin-bottom: -1 * $ibo-panel--body--padding-bot
|
||||
|
||||
$ibo-panel-with-tab-container--tab-toggler--font-size--is-sticking: $ibo-font-size-100 !default;
|
||||
|
||||
$ibo-tab-container-within-panel--tabs-list--padding-top: $ibo-spacing-800 !default;
|
||||
// I'm not sure where these values come from, might need to be replaced by variables
|
||||
$ibo-tab-container-within-panel--tabs-list--min-width: calc(32px + 90px + 32px) !default;
|
||||
$ibo-tab-container-within-panel--tabs-header--height: $ibo-spacing-800 !default;
|
||||
$ibo-tab-container-within-panel--tabs--list--is-sticking--z-index: 10 !default;
|
||||
$ibo-tab-container-within-panel--tabs--list--is-sticking--is-not-vertical--padding-left: $ibo-spacing-0 !default;
|
||||
|
||||
// Note: We use the child ">" selector to ensure this applies only to the child tab container, not another one that would be nested
|
||||
.ibo-panel {
|
||||
> .ibo-panel--body {
|
||||
@@ -35,15 +42,15 @@ $ibo-panel-with-tab-container--tab-toggler--font-size--is-sticking: $ibo-font-si
|
||||
flex-direction: row;
|
||||
|
||||
> .ibo-tab-container--tabs-list {
|
||||
padding-top: 50px;
|
||||
padding-top: $ibo-tab-container-within-panel--tabs-list--padding-top;
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
padding-left: unset;
|
||||
margin-right: unset;
|
||||
min-width: calc(32px + 90px + 32px);
|
||||
min-width: $ibo-tab-container-within-panel--tabs-list--min-width;
|
||||
|
||||
> .ibo-tab-container--tab-header {
|
||||
height: 50px;
|
||||
height: $ibo-tab-container-within-panel--tabs-header--height;
|
||||
width: 100%;
|
||||
justify-content: left;
|
||||
|
||||
@@ -68,12 +75,12 @@ $ibo-panel-with-tab-container--tab-toggler--font-size--is-sticking: $ibo-font-si
|
||||
> .ibo-tab-container {
|
||||
> .ibo-tab-container--tabs-list.ibo-is-sticking {
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
z-index: $ibo-tab-container-within-panel--tabs--list--is-sticking--z-index;
|
||||
}
|
||||
|
||||
&:not(.ibo-is-vertical){
|
||||
> .ibo-tab-container--tabs-list.ibo-is-sticking {
|
||||
padding-left: 0;
|
||||
padding-left: $ibo-tab-container-within-panel--tabs--list--is-sticking--is-not-vertical--padding-left;
|
||||
|
||||
.ibo-tab-container--tab-toggler,
|
||||
.ibo-tab-container--extra-tabs-list-toggler {
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-datatable-within-panel--panel-body--width: 100% !default;
|
||||
|
||||
|
||||
.ibo-panel .ibo-panel--body{
|
||||
.ibo-datatable{
|
||||
width: 100%
|
||||
width: $ibo-datatable-within-panel--panel-body--width;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-object-details-with-tab-container--tab-list--padding-left: calc(#{$ibo-object-details--icon--spacing--as-medallion} + #{$ibo-object-details--icon--size} + #{$ibo-object-details--icon--spacing--as-medallion} - #{$ibo-tab-container--tab-toggler--padding-x}) !default;
|
||||
|
||||
// Note: We use the child ">" selector to ensure this applies only the child tab container, not another one that would be nested
|
||||
.ibo-object-details.ibo-has-medallion-icon {
|
||||
> .ibo-panel--body {
|
||||
@@ -10,7 +12,7 @@
|
||||
> .ibo-tab-container:not(.ibo-is-vertical) {
|
||||
> .ibo-tab-container--tabs-list {
|
||||
// Align tab toggler's title with the panel's title
|
||||
padding-left: calc(#{$ibo-object-details--icon--spacing--as-medallion} + #{$ibo-object-details--icon--size} + #{$ibo-object-details--icon--spacing--as-medallion} - #{$ibo-tab-container--tab-toggler--padding-x});
|
||||
padding-left: $ibo-object-details-with-tab-container--tab-list--padding-left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,12 @@ $ibo-dashlet--width--is-inline: auto !default;
|
||||
$ibo-dashlet--elements-spacing-x: $ibo-spacing-600 !default;
|
||||
$ibo-dashlet--elements-spacing-y: $ibo-spacing-600 !default;
|
||||
|
||||
$ibo-dashlet-blocker--z-index: 9 !default; /* To be above calendar links & all, but below .close-box (9) */
|
||||
$ibo-dashlet-blocker--top: 0 !default;
|
||||
$ibo-dashlet-blocker--left: 0 !default;
|
||||
$ibo-dashlet-blocker--width: 100% !default;
|
||||
$ibo-dashlet-blocker--height: 100% !default;
|
||||
|
||||
/* Rules */
|
||||
.ibo-dashlet {
|
||||
position: relative;
|
||||
@@ -23,18 +29,13 @@ $ibo-dashlet--elements-spacing-y: $ibo-spacing-600 !default;
|
||||
.ibo-dashlet--is-inline {
|
||||
width: $ibo-dashlet--width--is-inline;
|
||||
}
|
||||
.ibo-details > .ibo-prop--apply {
|
||||
display: table-column;
|
||||
}
|
||||
.ibo-details{
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.ibo-dashlet-blocker{
|
||||
position: absolute;
|
||||
z-index: 9; /* To be above calendar links & all, but below .close-box (9) */
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: $ibo-dashlet-blocker--z-index;
|
||||
top: $ibo-dashlet-blocker--top;
|
||||
left: $ibo-dashlet-blocker--left;
|
||||
width: $ibo-dashlet-blocker--width;
|
||||
height: $ibo-dashlet-blocker--height;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
@@ -24,6 +24,8 @@ $ibo-datatable--sort-order--color: $ibo-color-primary-600 !default;
|
||||
|
||||
$ibo-fieldsorter--selected--background-color: $ibo-color-blue-200 !default;
|
||||
|
||||
$ibo-datatable--selected-result-count--padding-right: 0.2em !default;
|
||||
$ibo-datatable--selected-result-count--padding-left: 0.1em !default;
|
||||
|
||||
/* CSS variables (can be changed directly from the browser) */
|
||||
:root {
|
||||
@@ -134,8 +136,8 @@ $ibo-fieldsorter--selected--background-color: $ibo-color-blue-200 !default;
|
||||
}
|
||||
|
||||
.ibo-datatable--selected-count, .ibo-datatable--result-count{
|
||||
padding-right: 0.2em;
|
||||
padding-left: 0.1em;
|
||||
padding-right: $ibo-datatable--selected-result-count--padding-right;
|
||||
padding-left: $ibo-datatable--selected-result-count--padding-left;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-input-image--image-view--min-height: 96px !default;
|
||||
$ibo-input-image--image-view--min-height: $ibo-size-500 !default;
|
||||
$ibo-input-image--image-view--background-color: $ibo-color-grey-200 !default;
|
||||
$ibo-input-image--image-view--border-radius: $ibo-border-radius-500 !default;
|
||||
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
$ibo-input-richtext-placeholder--height: $ibo-size-600 !default;
|
||||
$ibo-input-richtext-placeholder--width: 100% !default;
|
||||
|
||||
.ibo-input-richtext-placeholder{
|
||||
height: 200px;
|
||||
width: 100%;
|
||||
height: $ibo-input-richtext-placeholder--height;
|
||||
width: $ibo-input-richtext-placeholder--width;
|
||||
visibility: hidden;
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-input-select-icon--menu--icon--max-height: 100% !default;
|
||||
$ibo-input-select-icon--menu--icon--max-width: 100% !default;
|
||||
$ibo-input-select-icon--icon--padding-right: $ibo-spacing-200 !default;
|
||||
|
||||
$ibo-input-select-icon--menu--z-index: 21 !default;
|
||||
@@ -16,8 +18,8 @@ $ibo-input-select-icon--menu--icon--margin-right: 10px !default;
|
||||
display: inline-flex;
|
||||
text-align: left;
|
||||
>img{
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
max-height: $ibo-input-select-icon--menu--icon--max-height;
|
||||
max-width: $ibo-input-select-icon--menu--icon--max-width;
|
||||
padding-right: $ibo-input-select-icon--icon--padding-right;
|
||||
}
|
||||
>span{
|
||||
|
||||
@@ -20,6 +20,7 @@ $ibo-input-select-selectize--item--active--background-color: $ibo-color-blue-100
|
||||
|
||||
$ibo-input-select-wrapper--width: 100% !default;
|
||||
|
||||
$ibo-input-select-wrapper--after--content: "\f0d7" !default;
|
||||
$ibo-input-select-wrapper--after--right: 8px !default;
|
||||
$ibo-input-select-wrapper--after--height: 28px !default;
|
||||
$ibo-input-select-wrapper--after--margin-left: -16px !default;
|
||||
@@ -32,6 +33,7 @@ $ibo-input-select--action-button--height: 28px !default;
|
||||
$ibo-input-select--action-button--width: 23px !default;
|
||||
$ibo-input-select--action-button--margin-top: $ibo-spacing-0 !default;
|
||||
$ibo-input-select--action-button--margin-right: 3px !default;
|
||||
$ibo-input-select--action-button--font-size: $ibo-font-size-100 !default;
|
||||
$ibo-input-select--action-button--background-color: inherit !default;
|
||||
$ibo-input-select--action-button--color: $ibo-color-grey-800 !default;
|
||||
$ibo-input-select--action-button--padding-x: $ibo-spacing-100 !default;
|
||||
@@ -57,7 +59,7 @@ $ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-60
|
||||
min-width: $ibo-input-select-selectize--value--min-midth !important;
|
||||
|
||||
input {
|
||||
border-width: 0px;
|
||||
border-width: 0;
|
||||
color: inherit;
|
||||
border-color: $ibo-color-white-100;
|
||||
padding-left: $ibo-input-select--padding-x;
|
||||
@@ -121,7 +123,7 @@ $ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-60
|
||||
.ibo-input-select-wrapper::after{
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
content: "\f0d7";
|
||||
content: $ibo-input-select-wrapper--after--content;
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-weight: 600;
|
||||
|
||||
@@ -145,7 +147,7 @@ $ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-60
|
||||
.ibo-input-select-wrapper--with-buttons:not(.ibo-input-select-autocomplete-wrapper)::after {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
content: "\f0d7";
|
||||
content: $ibo-input-select-wrapper--after--content;
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
@@ -168,7 +170,7 @@ $ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-60
|
||||
margin-top: $ibo-input-select--action-button--margin-top;
|
||||
margin-right: $ibo-input-select--action-button--margin-right;
|
||||
|
||||
font-size: $ibo-font-size-100;
|
||||
font-size: $ibo-input-select--action-button--font-size;
|
||||
background-color: $ibo-input-select--action-button--background-color;
|
||||
color: $ibo-input-select--action-button--color;
|
||||
padding: $ibo-input-select--action-button--padding-y $ibo-input-select--action-button--padding-x;
|
||||
|
||||
@@ -8,6 +8,9 @@ $ibo-input-text--min-height: 12rem !default;
|
||||
$ibo-input-text--padding-x: $ibo-spacing-400 !default;
|
||||
$ibo-input-text--padding-y: 10px !default;
|
||||
|
||||
$ibo-input-text--export--width: 100% !default;
|
||||
$ibo-input-text--export--min-height: 15em !default;
|
||||
|
||||
.ibo-input-text {
|
||||
width: $ibo-input-text--width;
|
||||
min-height: $ibo-input-text--min-height;
|
||||
@@ -23,6 +26,6 @@ $ibo-input-text--padding-y: 10px !default;
|
||||
}
|
||||
|
||||
.ibo-input-text--export {
|
||||
width: 100%;
|
||||
min-height: 15em;
|
||||
width: $ibo-input-text--export--width;
|
||||
min-height: $ibo-input-text--export--min-height;
|
||||
}
|
||||
@@ -9,6 +9,8 @@ $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--before--left: 3px !default;
|
||||
$ibo-toggler--slider--before--bottom: 3px !default;
|
||||
$ibo-toggler--slider--before--height: 15px !default;
|
||||
$ibo-toggler--slider--before--width: 15px !default;
|
||||
$ibo-toggler--slider--before--border-radius: $ibo-border-radius-full !default;
|
||||
@@ -46,8 +48,8 @@ $ibo-toggler--label--margin-left: 4px !default;
|
||||
.ibo-toggler--slider:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 3px;
|
||||
bottom: 3px;
|
||||
left: $ibo-toggler--slider--before--left;
|
||||
bottom: $ibo-toggler--slider--before--bottom;
|
||||
height: $ibo-toggler--slider--before--height;
|
||||
width: $ibo-toggler--slider--before--width;
|
||||
border-radius: $ibo-toggler--slider--before--border-radius;
|
||||
|
||||
@@ -27,6 +27,9 @@ $ibo-input--disabled--background-color: $ibo-color-grey-300 !default;
|
||||
$ibo-input--placeholder--color: $ibo-color-grey-700 !default;
|
||||
|
||||
$ibo-input-wrapper--is-error--border-color: $ibo-color-red-600 !default;
|
||||
$ibo-input-wrapper--vanilla--is-error--border: 0 !default;
|
||||
$ibo-input-wrapper--vanilla--is-error--background-color: $ibo-color-transparent !default;
|
||||
|
||||
$ibo-field-validation: $ibo-color-red-700 !default;
|
||||
|
||||
$ibo-input--margin-x: $ibo-spacing-200 !default;
|
||||
@@ -62,8 +65,8 @@ textarea.ibo-input{
|
||||
border-color: $ibo-input-wrapper--is-error--border-color;
|
||||
}
|
||||
.ibo-input-vanilla input{
|
||||
border: 0;
|
||||
background-color: #11ffee00;
|
||||
border: $ibo-input-wrapper--vanilla--is-error--border;
|
||||
background-color: $ibo-input-wrapper--vanilla--is-error--background-color;
|
||||
}
|
||||
}
|
||||
input.ibo-input-vanilla{
|
||||
|
||||
@@ -10,6 +10,10 @@ $ibo-popover-menu--padding: $ibo-spacing-0 !default;
|
||||
|
||||
$ibo-popover-menu--toggler-visual-hint--margin-left: 0.5rem !default;
|
||||
|
||||
$ibo-popover-menu--section--height: 100% !default;
|
||||
$ibo-popover-menu--section--margin-x: $ibo-spacing-0 !default;
|
||||
$ibo-popover-menu--section--margin-y: $ibo-spacing-0 !default;
|
||||
|
||||
$ibo-popover-menu--section-border-radius: $ibo-popover-menu--border-radius !default;
|
||||
|
||||
.ibo-popover-menu {
|
||||
@@ -35,8 +39,8 @@ $ibo-popover-menu--section-border-radius: $ibo-popover-menu--border-radius !defa
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: flex-start;
|
||||
margin: $ibo-spacing-0 $ibo-spacing-0;
|
||||
width: 100%;
|
||||
margin: $ibo-popover-menu--section--margin-y $ibo-popover-menu--section--margin-x;
|
||||
width: $ibo-popover-menu--section--height;
|
||||
white-space: nowrap;
|
||||
overflow: hidden; /* To avoid first/last entries of the menu to have no border-radius on hover */
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
@import "navigation-menu";
|
||||
@import "top-bar";
|
||||
@import "content";
|
||||
@import "details";
|
||||
@import "tab-container/tab-container";
|
||||
@import "tab-container/tab";
|
||||
@import "multi-column/multi-column";
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
$ibo-v-spacer--padding-top: 1em !default;
|
||||
$ibo-side-content--background-color: $ibo-content-block--background-color !default;
|
||||
$ibo-side-content--border-left: $ibo-content-block--border !default;
|
||||
|
||||
/* Note: We have to wrap it in the ID in order to overload its rules, otherwise, the ID takes over the simple CSS class... */
|
||||
#ibo-center-container {
|
||||
@@ -16,10 +19,10 @@
|
||||
}
|
||||
|
||||
.ibo-v-spacer {
|
||||
padding-top: 1em;
|
||||
padding-top: $ibo-v-spacer--padding-top;
|
||||
}
|
||||
|
||||
#ibo-side-content {
|
||||
background-color: $ibo-content-block--background-color;
|
||||
border-left: $ibo-content-block--border;
|
||||
background-color: $ibo-side-content--background-color;
|
||||
border-left: $ibo-side-content--border-left;
|
||||
}
|
||||
|
||||
9
css/backoffice/layout/_details.scss
Normal file
9
css/backoffice/layout/_details.scss
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
$ibo-details--margin-top: 5px !default;
|
||||
|
||||
.ibo-details {
|
||||
margin-top: $ibo-details--margin-top;
|
||||
}
|
||||
@@ -218,7 +218,8 @@ $ibo-navigation-menu--user-info--height--is-expanded: 100% !default;
|
||||
.ibo-navigation-menu--body{
|
||||
width: $ibo-navigation-menu--body--width-expanded;
|
||||
|
||||
.ibo-navigation-menu--toggler-bar{
|
||||
/* Values are hardcoded to ease the animation and there's no point in overloading them */
|
||||
.ibo-navigation-menu--toggler-bar{
|
||||
&:nth-child(1){
|
||||
top: 4px;
|
||||
left: 7px;
|
||||
@@ -412,6 +413,7 @@ $ibo-navigation-menu--user-info--height--is-expanded: 100% !default;
|
||||
transition: all 0.2s linear;
|
||||
background-color: $ibo-navigation-menu--body--text-color;
|
||||
|
||||
/* Values are hardcoded to ease the animation and there's no point in overloading them */
|
||||
&:nth-child(1){
|
||||
top: 0;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ $ibo-activity-entry--medallion--has-no-image--background-color: $ibo-color-blue-
|
||||
$ibo-activity-entry--medallion--has-no-image--text-color: $ibo-color-white-100 !default;
|
||||
$ibo-activity-entry--medallion--has-no-image--border: 1px solid $ibo-color-grey-200 !default;
|
||||
|
||||
$ibo-activity-entry--information--margin-to-side: $ibo-spacing-0 !default;
|
||||
$ibo-activity-entry--information--margin-to-other-side: $ibo-activity-entry--medallion--diameter + $ibo-activity-entry--medallion--margin-with-information !default;
|
||||
|
||||
$ibo-activity-entry--main-information--padding-x: $ibo-spacing-500 !default;
|
||||
@@ -88,7 +89,7 @@ $ibo-activity-panel--load-all-entries--is-hover--margin-left: ($ibo-activity-pan
|
||||
margin-left: $ibo-activity-entry--medallion--margin-with-information;
|
||||
}
|
||||
.ibo-activity-entry--information{
|
||||
margin-right: 0;
|
||||
margin-right: $ibo-activity-entry--information--margin-to-side;
|
||||
margin-left: $ibo-activity-entry--information--margin-to-other-side;
|
||||
}
|
||||
.ibo-activity-entry--main-information{
|
||||
@@ -109,7 +110,7 @@ $ibo-activity-panel--load-all-entries--is-hover--margin-left: ($ibo-activity-pan
|
||||
&:not(.ibo-is-current-user){
|
||||
.ibo-activity-entry--information{
|
||||
margin-right: $ibo-activity-entry--information--margin-to-other-side;
|
||||
margin-left: 0;
|
||||
margin-left: $ibo-activity-entry--information--margin-to-side;
|
||||
}
|
||||
/* Bubble tip on the left for last entry of the group */
|
||||
&:last-child{
|
||||
|
||||
@@ -104,6 +104,7 @@ $ibo-activity-panel--add-caselog-entry-button--icon--line-height: 33px !default;
|
||||
$ibo-activity-panel--entry-forms-confirmation-explanation--spacing: $ibo-spacing-500 !default;
|
||||
$ibo-activity-panel--entry-forms-confirmation-preference-input--spacing: 0.5rem !default;
|
||||
|
||||
$ibo-activity-panel--closed-cover--z-index: 2 !default;
|
||||
$ibo-activity-panel--closed-cover--background-color: $ibo-activity-panel--header--background-color !default;
|
||||
$ibo-activity-panel--open-icon--margin-left: 0.75rem !default;
|
||||
|
||||
@@ -433,14 +434,12 @@ $ibo-activity-panel--open-icon--margin-left: 0.75rem !default;
|
||||
.ibo-activity-panel--closed-cover {
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 2; // Above the compose button and all
|
||||
// padding-top: 64px;
|
||||
z-index: $ibo-activity-panel--closed-cover--z-index; // Above the compose button and all
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
@extend %ibo-fully-centered-content;
|
||||
// align-items: flex-start;
|
||||
background-color: $ibo-activity-panel--closed-cover--background-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ $ibo-caselog-entry-form--actions--margin-top: $ibo-spacing-300 !default;
|
||||
$ibo-caselog-entry-form--actions--margin-bottom: $ibo-caselog-entry-form--actions--margin-top !default;
|
||||
|
||||
$ibo-caselog-entry-form--lock-indicator--margin-top: $ibo-caselog-entry-form--padding-bottom !default;
|
||||
$ibo-caselog-entry-form--lock-icon--size: 32px !default;
|
||||
$ibo-caselog-entry-form--lock-icon--size: $ibo-size-350 !default;
|
||||
$ibo-caselog-entry-form--lock-icon--text-color: $ibo-color-grey-50 !default;
|
||||
$ibo-caselog-entry-form--lock-icon--background-color: $ibo-color-grey-800 !default;
|
||||
$ibo-caselog-entry-form--lock-icon--border-radius: $ibo-border-radius-full !default;
|
||||
|
||||
@@ -25,6 +25,10 @@ $ibo-dashboard-editor--delete-dashlet-icon--right: 9px !default;
|
||||
$ibo-dashboard-editor--delete-dashlet-icon--padding: $ibo-spacing-100 6px !default;
|
||||
$ibo-dashboard-editor--delete-dashlet-icon--z-index: 21 !default;
|
||||
|
||||
$ibo-dashlet--properties--padding-bottom: 20px !default;
|
||||
$ibo-dashlet--properties--table--width: 100% !default;
|
||||
$ibo-dashlet--properties--table--cell--margin-bottom: 14px !default;
|
||||
|
||||
|
||||
.ibo-dashboard-editor--pane{
|
||||
flex-grow: 1;
|
||||
@@ -47,13 +51,13 @@ $ibo-dashboard-editor--delete-dashlet-icon--z-index: 21 !default;
|
||||
.ibo-dashlet--properties{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 20px;
|
||||
padding-bottom: $ibo-dashlet--properties--padding-bottom;
|
||||
table{
|
||||
width: 100%;
|
||||
width: $ibo-dashlet--properties--table--width;
|
||||
text-align: left;
|
||||
|
||||
td, th {
|
||||
margin-bottom: 14px;
|
||||
margin-bottom: $ibo-dashlet--properties--table--cell--margin-bottom;
|
||||
|
||||
.ibo-field {
|
||||
@extend %ibo-font-size-100;
|
||||
|
||||
@@ -7,8 +7,38 @@
|
||||
$ibo-dashboard--grid--width: 100% !default;
|
||||
$ibo-dashboard--grid--elements-spacing-x: $ibo-dashlet--elements-spacing-x !default;
|
||||
$ibo-dashboard--grid--elements-spacing-y: $ibo-dashlet--elements-spacing-y !default;
|
||||
$ibo-dashboard--grid--edit-mode--margin: 1px !default;
|
||||
$ibo-dashboard--grid--edit-mode--border-color: $ibo-color-grey-400 !default;
|
||||
$ibo-dashboard--grid--edit-mode--border: 2px $ibo-dashboard--grid--edit-mode--border-color dashed !default;
|
||||
$ibo-dashboard--grid--edit-mode--width: 100% !default;
|
||||
$ibo-dashboard--grid--edit-mode--min-height: 40px !default;
|
||||
|
||||
$ibo-dashboard--top-bar-padding-bottom: 20px !default;
|
||||
|
||||
$ibo-dashboard--selector--margin-left: $ibo-spacing-400 !default;
|
||||
$ibo-dashboard--selector--margin-right: 1 !default;
|
||||
$ibo-dashboard--selector--hover--background-color: $ibo-color-secondary-100 !default;
|
||||
$ibo-dashboard--selector--hover--border-radius: $ibo-button--border-radius !default;
|
||||
$ibo-dashboard--selector--selector-label--margin-x: 10px !default;
|
||||
|
||||
$ibo-dashboard--switch--width: 30px !default;
|
||||
$ibo-dashboard--switch--height: $ibo-size-300 !default;
|
||||
|
||||
$ibo-dashboard--slider--before--content: "\f007" !default;
|
||||
$ibo-dashboard--slider--before--font-size: $ibo-font-size-50 !default;
|
||||
$ibo-dashboard--slider--before--color: $ibo-color-secondary-800 !default;
|
||||
$ibo-dashboard--slider--before--right: 5px !default;
|
||||
$ibo-dashboard--slider--before--bottom: 3px !default;
|
||||
|
||||
$ibo-dashboard--slider--after--content: "\f1ad" !default;
|
||||
$ibo-dashboard--slider--after--font-size: $ibo-font-size-150 !default;
|
||||
$ibo-dashboard--slider--after--color: $ibo-color-primary-600 !default;
|
||||
$ibo-dashboard--slider--after--left: 6px !default;
|
||||
$ibo-dashboard--slider--after--bottom: 1px !default;
|
||||
|
||||
|
||||
|
||||
|
||||
/* Rules */
|
||||
.ibo-dashboard--top-bar {
|
||||
@extend %ibo-full-height-content;
|
||||
@@ -30,18 +60,18 @@ $ibo-dashboard--top-bar-padding-bottom: 20px !default;
|
||||
.ibo-dashboard--selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 12px;
|
||||
margin-right: 1px;
|
||||
margin-left: $ibo-dashboard--selector--margin-left;
|
||||
margin-right: $ibo-dashboard--selector--margin-right;
|
||||
|
||||
&:hover {
|
||||
background-color: $ibo-color-secondary-100;
|
||||
border-radius: $ibo-button--border-radius;
|
||||
background-color: $ibo-dashboard--selector--hover--background-color;
|
||||
border-radius: $ibo-dashboard--selector--hover--border-radius;
|
||||
}
|
||||
|
||||
.selector-label {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
margin-left: $ibo-dashboard--selector--selector-label--margin-x;
|
||||
margin-right: $ibo-dashboard--selector--selector-label--margin-x;
|
||||
vertical-align: super;
|
||||
}
|
||||
}
|
||||
@@ -75,7 +105,7 @@ $ibo-dashboard--top-bar-padding-bottom: 20px !default;
|
||||
margin: calc(-1 * #{$ibo-dashboard--grid--elements-spacing-y} / 2) calc(-1 * #{$ibo-dashboard--grid--elements-spacing-x} / 2); /* Because of the margin all around the dashlets, we need to compensate it */
|
||||
min-width: 0;
|
||||
|
||||
/* Compensate negative margin on inner borders to simulate egal dashlets spacing between columns */
|
||||
/* Compensate negative margin on inner borders to simulate equal dashlets spacing between columns */
|
||||
&:not(:last-child) {
|
||||
margin-right: 0;
|
||||
}
|
||||
@@ -85,10 +115,10 @@ $ibo-dashboard--top-bar-padding-bottom: 20px !default;
|
||||
}
|
||||
|
||||
&.edit_mode {
|
||||
margin: 1px;
|
||||
border: 2px #ccc dashed;
|
||||
width: 100%;
|
||||
min-height: 40px;
|
||||
margin: $ibo-dashboard--grid--edit-mode--margin;
|
||||
border: $ibo-dashboard--grid--edit-mode--border;
|
||||
width: $ibo-dashboard--grid--edit-mode--width;
|
||||
min-height: $ibo-dashboard--grid--edit-mode--min-height;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,8 +128,8 @@ $ibo-dashboard--top-bar-padding-bottom: 20px !default;
|
||||
.ibo-dashboard--switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 30px;
|
||||
height: 24px;
|
||||
width: $ibo-dashboard--switch--width;
|
||||
height: $ibo-dashboard--switch--height;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
@@ -119,31 +149,29 @@ $ibo-dashboard--top-bar-padding-bottom: 20px !default;
|
||||
}
|
||||
|
||||
.ibo-dashboard--slider:before {
|
||||
@extend %fa-solid-base;
|
||||
content: $ibo-dashboard--slider--before--content;
|
||||
font-size: $ibo-dashboard--slider--before--font-size;
|
||||
color: $ibo-dashboard--slider--before--color;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
bottom: 3px;
|
||||
color: $ibo-color-secondary-800;
|
||||
content: "\f007";
|
||||
font-family: "Font Awesome 5 Free", serif;
|
||||
font-size: $ibo-font-size-50;
|
||||
font-weight: 900;
|
||||
right: $ibo-dashboard--slider--before--right;
|
||||
bottom: $ibo-dashboard--slider--before--bottom;
|
||||
}
|
||||
|
||||
.ibo-dashboard--slider:after {
|
||||
@extend %fa-solid-base;
|
||||
content: $ibo-dashboard--slider--after--content;
|
||||
font-size: $ibo-dashboard--slider--after--font-size;
|
||||
color: $ibo-dashboard--slider--after--color;
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
bottom: 1px;
|
||||
color: $ibo-color-primary-600;
|
||||
content: "\f1ad";
|
||||
font-family: "Font Awesome 5 Free", serif;
|
||||
font-size: $ibo-font-size-150;
|
||||
font-weight: 900;
|
||||
left: $ibo-dashboard--slider--after--left;
|
||||
bottom: $ibo-dashboard--slider--after--bottom;
|
||||
}
|
||||
|
||||
input:checked + .ibo-dashboard--slider:before {
|
||||
content: "\f1ad";
|
||||
content: $ibo-dashboard--slider--after--content;
|
||||
}
|
||||
|
||||
input:checked + .ibo-dashboard--slider:after {
|
||||
content: "\f007";
|
||||
content: $ibo-dashboard--slider--before--content;
|
||||
}
|
||||
|
||||
@@ -4,10 +4,15 @@
|
||||
*/
|
||||
|
||||
$ibo-column--min-width: 300px !default;
|
||||
$ibo-mini-column--min-width: 30px !default;
|
||||
$ibo-column--padding-x: abs($ibo-multi-column--margin-x) !default;
|
||||
$ibo-column--padding-y: $ibo-spacing-0 !default;
|
||||
|
||||
$ibo-mini-column--min-width: 30px !default;
|
||||
$ibo-mini-column--padding-x: $ibo-column--padding-x !default;
|
||||
$ibo-mini-column--padding-y: $ibo-column--padding-y !default;
|
||||
$ibo-mini-column--button-margin-left: $ibo-spacing-0 !default;
|
||||
$ibo-mini-column--button-margin-right: $ibo-spacing-0 !default;
|
||||
|
||||
$ibo-column--margin-bottom--is-last-element: $ibo-spacing-800 !default;
|
||||
|
||||
.ibo-column {
|
||||
@@ -26,14 +31,14 @@ $ibo-column--margin-bottom--is-last-element: $ibo-spacing-800 !default;
|
||||
min-width: $ibo-mini-column--min-width;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
padding: $ibo-column--padding-y $ibo-column--padding-x;
|
||||
padding: $ibo-mini-column--padding-y $ibo-mini-column--padding-x;
|
||||
flex-basis: 10%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
>.ibo-button{
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
margin-left: $ibo-mini-column--button-margin-left;
|
||||
margin-right: $ibo-mini-column--button-margin-right;
|
||||
}
|
||||
&:not(:last-child) {
|
||||
margin-bottom: $ibo-column--margin-bottom--is-last-element;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-multi-column--margin-x: -16px !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-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 {
|
||||
|
||||
@@ -16,6 +16,8 @@ $ibo-object-details--icon--spacing--as-medallion--is-sticking: $ibo-object-detai
|
||||
$ibo-object-details--status-dot--size: 10px !default;
|
||||
$ibo-object-details--status-dot--spacing: $ibo-spacing-300 !default;
|
||||
$ibo-object-details--status-dot--border-radius: $ibo-border-radius-full !default;
|
||||
$ibo-object-details--status--class--before--content: "(" !default;
|
||||
$ibo-object-details--status--class--after--content: ")" !default;
|
||||
|
||||
$ibo-object-details--tag--sibling-spacing: $ibo-spacing-400 !default;
|
||||
$ibo-object-details--tag--color: $ibo-panel--subtitle--color !default;
|
||||
@@ -86,11 +88,11 @@ $ibo-object-details--header-right--padding-right--is-sticking: $ibo-spacing-300
|
||||
display: inline-flex; /* To avoid having spaces around the class name due to the indentation */
|
||||
|
||||
&::before {
|
||||
content: "(";
|
||||
content: $ibo-object-details--status--class--before--content;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: ")";
|
||||
content: $ibo-object-details--status--class--after--content;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
|
||||
$ibo-wizard-container--padding: 10px $ibo-spacing-500 !default;
|
||||
$ibo-wizard-container--background-color: $ibo-color-blue-200 !default;
|
||||
$ibo-wizard-container--border-radius: $ibo-border-radius-300 !default;
|
||||
$ibo-wizard-container--border-color: $ibo-color-blue-600 !default;
|
||||
$ibo-wizard-container--border-width: 3px !default;
|
||||
|
||||
.ibo-wizard-container {
|
||||
padding: $ibo-wizard-container--padding;
|
||||
background: $ibo-wizard-container--background-color;
|
||||
border-radius: $ibo-border-radius-300;
|
||||
border-radius: $ibo-wizard-container--border-radius;
|
||||
border-left: $ibo-wizard-container--border-width solid $ibo-wizard-container--border-color;
|
||||
}
|
||||
@@ -63,11 +63,10 @@ $ibo-attachment--tab-header--drop-in--icon--color: $ibo-color-blue-600 !default;
|
||||
background-color: $ibo-attachment--tab-header--drop-in--background-color;
|
||||
color: $ibo-attachment--tab-header--drop-in--color;
|
||||
> a::after{
|
||||
padding-left: $ibo-attachment--tab-header--drop-in--icon--padding-left;
|
||||
font-family: "Font Awesome 5 Free";
|
||||
content: $ibo-attachment--tab-header--drop-in--icon--content;
|
||||
font-weight: 900;
|
||||
color: $ibo-color-blue-600;
|
||||
@extend %fa-solid-base;
|
||||
padding-left: $ibo-attachment--tab-header--drop-in--icon--padding-left;
|
||||
color: $ibo-attachment--tab-header--drop-in--icon--color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,18 +19,19 @@ $ibo-audit--audit-line--csv-download--height: 2.5em !default;
|
||||
$ibo-audit--audit-line--status-indicator--diameter: 12px !default;
|
||||
$ibo-audit--audit-line--status-indicator--margin-right: 5px !default;
|
||||
|
||||
|
||||
/* Use semantic colors to ease accessibility */
|
||||
$ibo-audit--status--color: (
|
||||
'red': (
|
||||
$ibo-color-red-700,
|
||||
$ibo-color-danger-700,
|
||||
),
|
||||
'orange': (
|
||||
$ibo-color-orange-700,
|
||||
$ibo-color-warning-700,
|
||||
),
|
||||
'green': (
|
||||
$ibo-color-green-800,
|
||||
$ibo-color-success-800,
|
||||
),
|
||||
);
|
||||
|
||||
@each $sColor, $aAttributes in $ibo-audit--status--color {
|
||||
$bg-color: nth($aAttributes, 1);
|
||||
.ibo-audit--audit-category--panel .ibo-panel--body {
|
||||
|
||||
9
css/backoffice/pages/_csv-import.scss
vendored
9
css/backoffice/pages/_csv-import.scss
vendored
@@ -7,6 +7,7 @@ $ibo-csv-import--cell-message--padding-top: 3px !default;
|
||||
$ibo-csv-import--cell-modified--color: $ibo-color-blue-700 !default;
|
||||
$ibo-csv-import--cell-error--color: $ibo-color-red-700 !default;
|
||||
$ibo-csv-import--row--border-color: $ibo-color-grey-400 !default;
|
||||
$ibo-csv-import--row--border: 1px $ibo-csv-import--row--border-color solid !default;
|
||||
$ibo-csv-import--row-error--background-color: $ibo-color-red-200 !default;
|
||||
$ibo-csv-import--download-file--color: $ibo-color-primary-400 !default;
|
||||
$ibo-csv-import--download-file--font-size: 4em !default;
|
||||
@@ -31,20 +32,20 @@ div.ibo-csv-import--cell-message {
|
||||
}
|
||||
|
||||
tr.ibo-csv-import--row-unchanged td {
|
||||
border-bottom: 1px $ibo-csv-import--row--border-color solid;
|
||||
border-bottom: $ibo-csv-import--row--border;
|
||||
}
|
||||
|
||||
.wizContainer table tr.ibo-csv-import--row-error td {
|
||||
border-bottom: 1px $ibo-csv-import--row--border-color solid;
|
||||
border-bottom: $ibo-csv-import--row--border;
|
||||
background-color: $ibo-csv-import--row-error--background-color;
|
||||
}
|
||||
|
||||
tr.ibo-csv-import--row-modified td {
|
||||
border-bottom: 1px $ibo-csv-import--row--border-color solid;
|
||||
border-bottom: $ibo-csv-import--row--border;
|
||||
}
|
||||
|
||||
tr.ibo-csv-import--row-added td {
|
||||
border-bottom: 1px $ibo-csv-import--row--border-color solid;
|
||||
border-bottom: $ibo-csv-import--row--border;
|
||||
}
|
||||
|
||||
.ibo-csv-import--download-file {
|
||||
|
||||
@@ -11,6 +11,9 @@ $ibo-data-synchro-source--replicas-table--cell--arrow--min-width: 100px !default
|
||||
|
||||
$ibo-data-synchro-source--replicas-status--warning--margin: $ibo-spacing-0 5px $ibo-spacing-0 $ibo-spacing-300 !default;
|
||||
|
||||
$ibo-data-synchro-source--replicas-status-separator--border-color: $ibo-color-white-100 !default;
|
||||
$ibo-data-synchro-source--replicas-status-separator--border: 2px solid $ibo-data-synchro-source--replicas-status-separator--border-color !default;
|
||||
|
||||
$ibo-data-synchro-source--replicas-status--color: (
|
||||
'grey': (
|
||||
$ibo-color-grey-400,
|
||||
@@ -42,6 +45,10 @@ $ibo-data-synchro-source--replicas-status--color: (
|
||||
),
|
||||
) !default;
|
||||
|
||||
$ibo-data-synchro-source--synoptics--cell--arrow--border-color: $ibo-color-grey-50 !default;
|
||||
$ibo-data-synchro-source--synoptics--cell--arrow--border: 2px solid $ibo-data-synchro-source--synoptics--cell--arrow--border-color !default;
|
||||
|
||||
|
||||
@each $sColor, $aAttributes in $ibo-data-synchro-source--replicas-status--color {
|
||||
$bg-color: nth($aAttributes, 1);
|
||||
$color: nth($aAttributes, 2);
|
||||
@@ -50,8 +57,9 @@ $ibo-data-synchro-source--replicas-status--color: (
|
||||
background-color: $bg-color;
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-data-synchro-source--replicas-status-separator {
|
||||
border-top: 2px solid $ibo-color-white-100;
|
||||
border-top: $ibo-data-synchro-source--replicas-status-separator--border-color;
|
||||
}
|
||||
|
||||
.ibo-data-synchro-source--replicas-status.ibo-is-light{
|
||||
@@ -67,7 +75,7 @@ $ibo-data-synchro-source--replicas-status--color: (
|
||||
text-align: center;
|
||||
&.arrow {
|
||||
min-width: $ibo-data-synchro-source--replicas-table--cell--arrow--min-width;
|
||||
border-top: 2px solid $ibo-color-grey-50;
|
||||
border-top: $ibo-data-synchro-source--synoptics--cell--arrow--border;
|
||||
}
|
||||
}
|
||||
.ibo-data-synchro-source--replicas-status--warning{
|
||||
|
||||
@@ -29,12 +29,14 @@ $ibo-datamodel-viewer--schema--self-referencing--hover--fill: $ibo-datamodel-vie
|
||||
$ibo-datamodel-viewer--schema--tooltip--fill: $ibo-color-white-100 !default;
|
||||
$ibo-datamodel-viewer--schema--tooltip--background-color: $ibo-color-grey-900 !default;
|
||||
$ibo-datamodel-viewer--schema--tooltip--border-color: $ibo-color-grey-700 !default;
|
||||
$ibo-datamodel-viewer--schema--tooltip--border: 1px solid $ibo-datamodel-viewer--schema--tooltip--border-color !default;
|
||||
$ibo-datamodel-viewer--schema--tooltip--border-radius: $ibo-border-radius-300 !default;
|
||||
|
||||
$ibo-datamodel-viewer--schema--tooltip--icon--font-size: $ibo-font-size-100 !default;
|
||||
$ibo-datamodel-viewer--schema--tooltip--span--margin: 3px !default;
|
||||
|
||||
$ibo-datamodel-viewer--schema--tooltip-top--border-color: $ibo-color-grey-700 !default;
|
||||
$ibo-datamodel-viewer--schema--tooltip-top--border: 1px solid $ibo-datamodel-viewer--schema--tooltip-top--border-color !default;
|
||||
$ibo-datamodel-viewer--schema--tooltip-top--padding: 3px !default;
|
||||
|
||||
$ibo-datamodel-viewer--lifecycle-image--margin-bottom: $ibo-spacing-500 !default;
|
||||
@@ -116,7 +118,7 @@ $ibo-datamodel-viewer--lifecycle-image--margin-bottom: $ibo-spacing-500 !default
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
background: $ibo-datamodel-viewer--schema--tooltip--fill;
|
||||
border: 1px solid $ibo-datamodel-viewer--schema--tooltip--border-color;
|
||||
border: $ibo-datamodel-viewer--schema--tooltip--border;
|
||||
border-radius: $ibo-datamodel-viewer--schema--tooltip--border-radius;
|
||||
pointer-events: none;
|
||||
fill: $ibo-datamodel-viewer--schema--tooltip--background-color;
|
||||
@@ -132,7 +134,7 @@ $ibo-datamodel-viewer--lifecycle-image--margin-bottom: $ibo-spacing-500 !default
|
||||
|
||||
#tooltipD3_top {
|
||||
@extend %ibo-font-ral-bol-100;
|
||||
border-bottom: 1px solid $ibo-datamodel-viewer--schema--tooltip-top--border-color;
|
||||
border-bottom: $ibo-datamodel-viewer--schema--tooltip-top--border;
|
||||
padding: $ibo-datamodel-viewer--schema--tooltip-top--padding;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,10 @@ $ibo-display-graph--search-box--criterion--content--padding-x: 15px !default;
|
||||
|
||||
$ibo-display-graph--search-box--criterion--content--checkbox--margin-right: 10px !default;
|
||||
|
||||
$ibo-display-graph--graph-grouping-threshold--padding-right: $ibo-spacing-0 !default;
|
||||
|
||||
$ibo-display-graph--impacted-placeholder-height: $ibo-size-650 !default;
|
||||
|
||||
.itop-simple-graph {
|
||||
margin-top: $ibo-simple-graph--margin-top;
|
||||
border: 1px dotted transparent;
|
||||
@@ -83,7 +87,7 @@ $ibo-display-graph--search-box--criterion--content--checkbox--margin-right: 10px
|
||||
}
|
||||
#graph_grouping_threshold{
|
||||
width: auto;
|
||||
padding-right: 0;
|
||||
padding-right: $ibo-display-graph--graph-grouping-threshold--padding-right;
|
||||
}
|
||||
.ibo-display-graph--search-box {
|
||||
.sf_criterion_area{
|
||||
@@ -121,5 +125,5 @@ $ibo-display-graph--search-box--criterion--content--checkbox--margin-right: 10px
|
||||
}
|
||||
}
|
||||
#impacted_objects_lists_placeholder, #impacted_groups_placeholder{
|
||||
height: 250px;
|
||||
height: $ibo-display-graph--impacted-placeholder-height;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
$ibo-input-select--notification-item--mixed-value--font-size: $ibo-font-size-100 !default;
|
||||
$ibo-input-select--notification-item--mixed-value--color: $ibo-color-primary-800 !default;
|
||||
$ibo-input-select--notification-item--mixed-value--margin-left: 4px !default;
|
||||
|
||||
@@ -12,7 +13,7 @@ $ibo-input-select--notification-item--mixed-value--margin-left: 4px !default;
|
||||
}
|
||||
|
||||
.ibo-input-select--notification-item--mixed-value{
|
||||
font-size: $ibo-font-size-100;
|
||||
font-size: $ibo-input-select--notification-item--mixed-value--font-size;
|
||||
color: $ibo-input-select--notification-item--mixed-value--color;
|
||||
margin-left: $ibo-input-select--notification-item--mixed-value--margin-left;
|
||||
}
|
||||
@@ -4,11 +4,15 @@
|
||||
*/
|
||||
|
||||
$ibo-notifications--view-all--container--grid-gap: $ibo-spacing-600 !default;
|
||||
$ibo-notifications--view-all--container--object-summary--title--font-size: $ibo-font-size-250 !default;
|
||||
$ibo-notifications--view-all--container--object-summary--toolbar--min-width: $ibo-size-500 !default;
|
||||
$ibo-notifications--view-all--container--object-summary--panel--body--max-height: 400px !default;
|
||||
|
||||
$ibo-notifications--view-all--item--unread--highlight--background-color: $ibo-color-red-600 !default;
|
||||
$ibo-notifications--view-all--item--read--highlight--background-color: $ibo-color-grey-200 !default;
|
||||
|
||||
$ibo-notifications--view-all--container--read-unread--action--margin-left: $ibo-spacing-0 !default;
|
||||
|
||||
$ibo-notifications--view-all--container--large--grid-template-columns: repeat(3, 1fr) !default;
|
||||
$ibo-notifications--view-all--container--medium--grid-template-columns: repeat(2, 1fr) !default;
|
||||
$ibo-notifications--view-all--container--small--grid-template-columns: repeat(1, 1fr) !default;
|
||||
@@ -20,10 +24,10 @@ $ibo-notifications--view-all--empty--svg--max-width: 30% !default;
|
||||
display: grid;
|
||||
grid-gap: $ibo-notifications--view-all--container--grid-gap;
|
||||
.ibo-object-summary .ibo-panel--title{
|
||||
font-size: $ibo-font-size-250;
|
||||
font-size: $ibo-notifications--view-all--container--object-summary--title--font-size;
|
||||
}
|
||||
.ibo-object-summary .ibo-panel--toolbar{
|
||||
min-width: 102px;
|
||||
min-width: $ibo-notifications--view-all--container--object-summary--toolbar--min-width;
|
||||
}
|
||||
.ibo-object-summary > .ibo-panel--body{
|
||||
box-shadow: none;
|
||||
@@ -59,7 +63,7 @@ $ibo-notifications--view-all--empty--svg--max-width: 30% !default;
|
||||
|
||||
.ibo-notifications--view-all--container {
|
||||
.ibo-notifications--view-all--read-action, .ibo-notifications--view-all--unread-action {
|
||||
margin-left: 0 !important;
|
||||
margin-left: $ibo-notifications--view-all--container--read-unread--action--margin-left !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
$ibo-oauth-wizard--illustration--svg--max-height: $ibo-size-700 !default;
|
||||
|
||||
.ibo-oauth-wizard .ibo-panel--body{
|
||||
.ibo-oauth-wizard--form--container{
|
||||
display: flex;
|
||||
@@ -8,7 +14,7 @@
|
||||
|
||||
}
|
||||
.ibo-oauth-wizard--illustration svg{
|
||||
max-height: 400px;
|
||||
max-height: $ibo-oauth-wizard--illustration--svg--max-height;
|
||||
}
|
||||
}
|
||||
#ibo-oauth-wizard--conf--result{
|
||||
|
||||
@@ -9,6 +9,8 @@ $ibo-preferences--user-preferences--picture-placeholder--image--diameter: 54px !
|
||||
$ibo-preferences--user-preferences--picture-placeholder--image--border-radius: $ibo-border-radius-full !default;
|
||||
$ibo-preferences--user-preferences--picture-placeholder--image--margin: $ibo-spacing-400 !default;
|
||||
$ibo-preferences--user-preferences--picture-placeholder--image--background-color: $ibo-color-grey-300 !default;
|
||||
$ibo-preferences--user-preferences--picture-placeholder--image--border-color: $ibo-preferences--user-preferences--picture-placeholder--image--background-color !default;
|
||||
$ibo-preferences--user-preferences--picture-placeholder--image--border: solid 3px $ibo-preferences--user-preferences--picture-placeholder--image--border-color !default;
|
||||
|
||||
$ibo-preferences--user-preferences--picture-placeholder--image--active--border-color: $ibo-color-blue-800;
|
||||
$ibo-preferences--user-preferences--picture-placeholder--image--hover--border-color: $ibo-color-blue-600;
|
||||
@@ -18,6 +20,8 @@ $ibo-keyboard-shortcut--shortcut--width: 30% !default;
|
||||
$ibo-keyboard-shortcut--input--color: $ibo-color-grey-800 !default;
|
||||
$ibo-keyboard-shortcut--input--background-color: transparent !default;
|
||||
$ibo-keyboard-shortcut--input--border-color: $ibo-color-grey-500 !default;
|
||||
$ibo-keyboard-shortcut--input--border: 1px solid $ibo-keyboard-shortcut--input--border-color !default;
|
||||
$ibo-keyboard-shortcut--input--border-bottom: 2px solid $ibo-keyboard-shortcut--input--border-color !default;
|
||||
$ibo-keyboard-shortcut--input--border-radius: $ibo-border-radius-300 !default;
|
||||
$ibo-keyboard-shortcut--input--padding-y: $ibo-spacing-100 !default;
|
||||
$ibo-keyboard-shortcut--input--padding-x: $ibo-spacing-200 !default;
|
||||
@@ -26,6 +30,8 @@ $ibo-keyboard-shortcut--input--margin-bottom: 5px !default;
|
||||
$ibo-keyboard-shortcut--input--is-focus--color: $ibo-color-primary-800 !default;
|
||||
$ibo-keyboard-shortcut--input--is-focus--border-color: $ibo-color-primary-600 !default;
|
||||
|
||||
$ibo-favorite-organizations--toolbar--padding-top: $ibo-button--padding-y/2 !default;
|
||||
|
||||
#ibo-main-content >.ibo-panel{
|
||||
margin-left: $ibo-preferences--panel--margin-x;
|
||||
margin-right: $ibo-preferences--panel--margin-x;
|
||||
@@ -41,7 +47,7 @@ $ibo-keyboard-shortcut--input--is-focus--border-color: $ibo-color-primary-600 !d
|
||||
width: $ibo-preferences--user-preferences--picture-placeholder--image--diameter;
|
||||
border-radius: $ibo-preferences--user-preferences--picture-placeholder--image--border-radius;
|
||||
margin: $ibo-preferences--user-preferences--picture-placeholder--image--margin;
|
||||
border: solid 3px $ibo-preferences--user-preferences--picture-placeholder--image--background-color;
|
||||
border: $ibo-preferences--user-preferences--picture-placeholder--image--border;
|
||||
|
||||
> img{
|
||||
border-radius: $ibo-preferences--user-preferences--picture-placeholder--image--border-radius;
|
||||
@@ -72,8 +78,8 @@ $ibo-keyboard-shortcut--input--is-focus--border-color: $ibo-color-primary-600 !d
|
||||
|
||||
color: $ibo-keyboard-shortcut--input--color;
|
||||
background-color: $ibo-keyboard-shortcut--input--background-color;
|
||||
border: 1px solid $ibo-keyboard-shortcut--input--border-color;
|
||||
border-bottom: 2px solid $ibo-keyboard-shortcut--input--border-color;
|
||||
border: $ibo-keyboard-shortcut--input--border;
|
||||
border-bottom: $ibo-keyboard-shortcut--input--border-bottom;
|
||||
border-radius: $ibo-keyboard-shortcut--input--border-radius;
|
||||
|
||||
padding: $ibo-keyboard-shortcut--input--padding-y $ibo-keyboard-shortcut--input--padding-x;
|
||||
@@ -92,5 +98,5 @@ $ibo-keyboard-shortcut--input--is-focus--border-color: $ibo-color-primary-600 !d
|
||||
}
|
||||
|
||||
#ibo-favorite-organizations .ibo-datatable--toolbar {
|
||||
padding-top: $ibo-button--padding-y/2;
|
||||
padding-top: $ibo-favorite-organizations--toolbar--padding-top;
|
||||
}
|
||||
@@ -6,14 +6,15 @@
|
||||
$ibo-page-banner--background-color: $ibo-color-red-600 !default;
|
||||
$ibo-page-banner--text-color: $ibo-color-red-100 !default;
|
||||
$ibo-page-banner--text-content: "THIS IS NOT PRODUCTION INSTANCE" !default;
|
||||
|
||||
$ibo-page-banner--padding: 0.2 !default;
|
||||
$ibo-page-banner--font-size: 1rem !default;
|
||||
|
||||
#ibo-page-banner::before {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0.2rem;
|
||||
padding: $ibo-page-banner--padding;
|
||||
text-align: center;
|
||||
font-size: 1rem;
|
||||
font-size: $ibo-page-banner--font-size;
|
||||
background: $ibo-page-banner--background-color;
|
||||
color: $ibo-page-banner--text-color;
|
||||
content: $ibo-page-banner--text-content;
|
||||
|
||||
@@ -19,6 +19,9 @@ $ibo-hyperlink-text-decoration--on-hover: $common-hyperlink-text-decoration--on-
|
||||
$ibo-hyperlink-color--on-active: $common-hyperlink-color--on-active !default;
|
||||
$ibo-hyperlink-text-decoration--on-active: $common-hyperlink-text-decoration--on-active !default;
|
||||
|
||||
$ibo-paragraph--spacing-top: 0.25em !default;
|
||||
$ibo-paragraph--spacing-bottom: 0.25em !default;
|
||||
|
||||
$ibo-figure--spacing-x: $common-figure--spacing-x !default; /* Mind that this matches Bulma rule for figure */
|
||||
$ibo-figure--spacing-y: $common-figure--spacing-y !default;
|
||||
|
||||
@@ -122,8 +125,8 @@ $ibo-figure--spacing-y: $common-figure--spacing-y !default;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0.25em;
|
||||
margin-bottom: 0.25em;
|
||||
margin-top: $ibo-paragraph--spacing-top;
|
||||
margin-bottom: $ibo-paragraph--spacing-bottom;
|
||||
}
|
||||
|
||||
figure {
|
||||
|
||||
@@ -3,12 +3,15 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-selectable--content: ' ' !default;
|
||||
$ibo-selectable--background-color: $common-selectable--background-color !default;
|
||||
|
||||
$ibo-selectable--hover--content: '\f058' !default;
|
||||
$ibo-selectable--hover--color: $common-selectable--hover--color !default;
|
||||
$ibo-selectable--hover--background-color: $common-selectable--hover--background-color !default;
|
||||
$ibo-selectable--hover--background-opacity: $common-selectable--hover--background-opacity !default;
|
||||
|
||||
$ibo-selected--content: '\f058' !default;
|
||||
$ibo-selected--color: $common-selected--color !default;
|
||||
$ibo-selected--background-color: $common-selected--background-color !default;
|
||||
$ibo-selected--background-opacity: $common-selected--background-opacity !default;
|
||||
@@ -17,21 +20,21 @@ $ibo-selected--hover--background-color: $common-selected--hover--background-colo
|
||||
$ibo-selected--hover--background-opacity: $common-selected--hover--background-opacity !default;
|
||||
|
||||
@mixin ibo-selectable {
|
||||
content: ' ';
|
||||
content: $ibo-selectable--content;
|
||||
@extend %fa-solid-base;
|
||||
background-color: $ibo-selectable--background-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
@mixin ibo-selectable-hover {
|
||||
@extend %fa-regular-base;
|
||||
content: '\f058';
|
||||
content: $ibo-selectable--hover--content;
|
||||
color: $ibo-selectable--hover--color;
|
||||
background-color: transparentize($ibo-selectable--hover--background-color, $ibo-selectable--hover--background-opacity);
|
||||
}
|
||||
|
||||
@mixin ibo-selected {
|
||||
@extend %fa-solid-base;
|
||||
content: '\f058';
|
||||
content: $ibo-selected--content;
|
||||
color: $ibo-selected--color;
|
||||
background-color: transparentize($ibo-selected--background-color, $ibo-selected--background-opacity);
|
||||
}
|
||||
|
||||
@@ -5,15 +5,16 @@
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\WebPage\WebPage;
|
||||
use Combodo\iTop\Service\Events\EventData;
|
||||
use Combodo\iTop\Service\Events\EventService;
|
||||
use Combodo\iTop\Service\Events\iEventServiceSetup;
|
||||
|
||||
class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExtension
|
||||
class AttachmentPlugIn implements iApplicationUIExtension, iEventServiceSetup
|
||||
{
|
||||
const ENUM_GUI_ALL = 'all';
|
||||
const ENUM_GUI_BACKOFFICE = 'backoffice';
|
||||
const ENUM_GUI_PORTALS = 'portals';
|
||||
|
||||
protected static $m_bIsModified = false;
|
||||
|
||||
public function OnDisplayProperties($oObject, WebPage $oPage, $bEditMode = false)
|
||||
{
|
||||
if ($this->GetAttachmentsPosition() == 'properties')
|
||||
@@ -158,45 +159,39 @@ class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExt
|
||||
return array();
|
||||
}
|
||||
|
||||
public function OnIsModified($oObject)
|
||||
public function RegisterEventsAndListeners() : void
|
||||
{
|
||||
return self::$m_bIsModified;
|
||||
EventService::RegisterListener(EVENT_DB_AFTER_WRITE, [$this, 'OnDBAfterWrite']);
|
||||
EventService::RegisterListener(EVENT_DB_AFTER_DELETE, [$this, 'OnDBAfterDelete']);
|
||||
}
|
||||
|
||||
public function OnCheckToWrite($oObject)
|
||||
public function OnDBAfterWrite(EventData $oEventData)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
$oObject = $oEventData->Get('object');
|
||||
$oCMDBChange = $oEventData->Get('cmdb_change');
|
||||
$bIsNew = $oEventData->Get('is_new');
|
||||
|
||||
public function OnCheckToDelete($oObject)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function OnDBUpdate($oObject, $oChange = null)
|
||||
{
|
||||
if ($this->IsTargetObject($oObject))
|
||||
{
|
||||
// Get all current attachments
|
||||
$oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE item_class = :class AND item_id = :item_id");
|
||||
$oSet = new DBObjectSet($oSearch, array(), array('class' => get_class($oObject), 'item_id' => $oObject->GetKey()));
|
||||
while ($oAttachment = $oSet->Fetch())
|
||||
{
|
||||
$oAttachment->SetItem($oObject, true /*updateonchange*/);
|
||||
if($bIsNew){
|
||||
self::UpdateAttachments($oObject, $oCMDBChange);
|
||||
}
|
||||
else{
|
||||
// Get all current attachments
|
||||
$oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE item_class = :class AND item_id = :item_id");
|
||||
$oSet = new DBObjectSet($oSearch, array(), array('class' => get_class($oObject), 'item_id' => $oObject->GetKey()));
|
||||
while ($oAttachment = $oSet->Fetch())
|
||||
{
|
||||
$oAttachment->SetItem($oObject, true /*updateonchange*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function OnDBInsert($oObject, $oChange = null)
|
||||
public function OnDBAfterDelete(EventData $oEventData)
|
||||
{
|
||||
if ($this->IsTargetObject($oObject))
|
||||
{
|
||||
self::UpdateAttachments($oObject, $oChange);
|
||||
}
|
||||
}
|
||||
$oObject = $oEventData->Get('object');
|
||||
|
||||
public function OnDBDelete($oObject, $oChange = null)
|
||||
{
|
||||
if ($this->IsTargetObject($oObject))
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE item_class = :class AND item_id = :item_id");
|
||||
@@ -291,7 +286,7 @@ class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExt
|
||||
* @see ObjectFormManager::FinalizeAttachments() for the portal version
|
||||
*
|
||||
* @param $oObject
|
||||
* @param $oChange
|
||||
* @param $oCMDBChange
|
||||
*
|
||||
* @return void
|
||||
* @throws \ArchivedObjectException
|
||||
@@ -303,10 +298,8 @@ class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExt
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
protected static function UpdateAttachments($oObject, $oChange = null)
|
||||
protected static function UpdateAttachments($oObject, $oCMDBChange = null)
|
||||
{
|
||||
self::$m_bIsModified = false;
|
||||
|
||||
if (utils::ReadParam('attachment_plugin', 'not-in-form') == 'not-in-form')
|
||||
{
|
||||
// Workaround to an issue in iTop < 2.0
|
||||
@@ -363,9 +356,10 @@ class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExt
|
||||
{
|
||||
foreach ($aActions as $oChangeOp)
|
||||
{
|
||||
self::RecordHistory($oChange, $oObject, $oChangeOp);
|
||||
self::RecordHistory($oCMDBChange, $oObject, $oChangeOp);
|
||||
}
|
||||
self::$m_bIsModified = true;
|
||||
|
||||
$oObject->MarkObjectAsModified();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -556,11 +550,11 @@ class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExt
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
private static function RecordHistory($oChange, $oTargetObject, $oMyChangeOp)
|
||||
private static function RecordHistory($oCMDBChange, $oTargetObject, $oMyChangeOp)
|
||||
{
|
||||
if (!is_null($oChange))
|
||||
if (!is_null($oCMDBChange))
|
||||
{
|
||||
$oMyChangeOp->Set("change", $oChange->GetKey());
|
||||
$oMyChangeOp->Set("change", $oCMDBChange->GetKey());
|
||||
}
|
||||
$oMyChangeOp->Set("objclass", get_class($oTargetObject));
|
||||
$oMyChangeOp->Set("objkey", $oTargetObject->GetKey());
|
||||
@@ -648,6 +642,8 @@ class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExt
|
||||
|
||||
return $bReadonly;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,362 +4,20 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\Alert\Alert;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Form\Form;
|
||||
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\Title\TitleUIBlockFactory;
|
||||
use Combodo\iTop\Application\WebPage\iTopWebPage;
|
||||
use Combodo\iTop\Config\Validator\iTopConfigAstValidator;
|
||||
use Combodo\iTop\Config\Validator\iTopConfigSyntaxValidator;
|
||||
use Combodo\iTop\Config\Controller\ConfigEditorController;
|
||||
use Combodo\iTop\Config\Validator\iTopConfigValidator;
|
||||
|
||||
require_once(APPROOT.'application/startup.inc.php');
|
||||
|
||||
const CONFIG_ERROR = 0;
|
||||
const CONFIG_WARNING = 1;
|
||||
const CONFIG_INFO = 2;
|
||||
|
||||
|
||||
/**
|
||||
* @param $sContents
|
||||
* @param $oP
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
function TestConfig($sContents, $oP)
|
||||
{
|
||||
/// 1- first check if there is no malicious code
|
||||
$oiTopConfigValidator = new iTopConfigAstValidator();
|
||||
$oiTopConfigValidator->Validate($sContents);
|
||||
|
||||
/// 2 - only after we are sure that there is no malicious cade, we can perform a syntax check!
|
||||
$oiTopConfigValidator = new iTopConfigSyntaxValidator();
|
||||
$oiTopConfigValidator->Validate($sContents);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sSafeContent
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function DBPasswordInNewConfigIsOk($sSafeContent)
|
||||
{
|
||||
$bIsWindows = (array_key_exists('WINDIR', $_SERVER) || array_key_exists('windir', $_SERVER));
|
||||
|
||||
if ($bIsWindows && (preg_match("@'db_pwd' => '[^%!\"]+',@U", $sSafeContent) === 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function CheckAsyncTasksRetryConfig(Config $oTempConfig, iTopWebPage $oP)
|
||||
{
|
||||
$iWarnings = 0;
|
||||
foreach (get_declared_classes() as $sPHPClass) {
|
||||
$oRefClass = new ReflectionClass($sPHPClass);
|
||||
if ($oRefClass->isSubclassOf('AsyncTask') && !$oRefClass->isAbstract()) {
|
||||
$aMessages = AsyncTask::CheckRetryConfig($oTempConfig, $oRefClass->getName());
|
||||
|
||||
if (count($aMessages) !== 0) {
|
||||
foreach ($aMessages as $sMessage) {
|
||||
$oAlert = AlertUIBlockFactory::MakeForWarning('', $sMessage);
|
||||
$oP->AddUiBlock($oAlert);
|
||||
$iWarnings++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $iWarnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Exception $e
|
||||
*
|
||||
* @return \Combodo\iTop\Application\UI\Base\Component\Alert\Alert
|
||||
*/
|
||||
function GetAlertFromException(Exception $e): Alert
|
||||
{
|
||||
switch ($e->getCode()) {
|
||||
case CONFIG_WARNING:
|
||||
$oAlert = AlertUIBlockFactory::MakeForWarning('', $e->getMessage());
|
||||
break;
|
||||
case CONFIG_INFO:
|
||||
$oAlert = AlertUIBlockFactory::MakeForInformation('', $e->getMessage());
|
||||
break;
|
||||
case CONFIG_ERROR:
|
||||
default:
|
||||
$oAlert = AlertUIBlockFactory::MakeForDanger('', $e->getMessage());
|
||||
}
|
||||
|
||||
return $oAlert;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Main program
|
||||
//
|
||||
LoginWebPage::DoLogin(); // Check user rights and prompt if needed
|
||||
ApplicationMenu::CheckMenuIdEnabled('ConfigEditor');
|
||||
|
||||
//$sOperation = utils::ReadParam('operation', 'menu');
|
||||
//$oAppContext = new ApplicationContext();
|
||||
|
||||
$oP = new iTopWebPage(Dict::S('config-edit-title'));
|
||||
$oP->set_base(utils::GetAbsoluteUrlAppRoot().'pages/');
|
||||
$sAceDir = 'node_modules/ace-builds/src-min/';
|
||||
$oP->LinkScriptFromAppRoot($sAceDir.'ace.js');
|
||||
$oP->LinkScriptFromAppRoot($sAceDir.'mode-php.js');
|
||||
$oP->LinkScriptFromAppRoot($sAceDir.'theme-eclipse.js');
|
||||
$oP->LinkScriptFromAppRoot($sAceDir.'ext-searchbox.js');
|
||||
$oConfigEditorController = new ConfigEditorController();
|
||||
$oConfigEditorController->SetDefaultOperation('Edit');
|
||||
$oConfigEditorController->HandleOperation();
|
||||
|
||||
try {
|
||||
$sOperation = utils::ReadParam('operation', '');
|
||||
$iEditorTopMargin = 2;
|
||||
if (UserRights::IsAdministrator() && ExecutionKPI::IsEnabled()) {
|
||||
$iEditorTopMargin += 6;
|
||||
}
|
||||
$oP->AddUiBlock(TitleUIBlockFactory::MakeForPage(Dict::S('config-edit-title')));
|
||||
|
||||
if (MetaModel::GetConfig()->Get('demo_mode')) {
|
||||
throw new Exception(Dict::S('config-not-allowed-in-demo'), CONFIG_INFO);
|
||||
}
|
||||
|
||||
if (MetaModel::GetModuleSetting('itop-config', 'config_editor', '') == 'disabled') {
|
||||
throw new Exception(Dict::S('config-interactive-not-allowed'), CONFIG_WARNING);
|
||||
}
|
||||
|
||||
$sConfigFile = APPROOT.'conf/'.utils::GetCurrentEnvironment().'/config-itop.php';
|
||||
|
||||
$iEditorTopMargin += 9;
|
||||
$sConfigContent = file_get_contents($sConfigFile);
|
||||
$sConfigChecksum = md5($sConfigContent);
|
||||
$sConfig = str_replace("\r\n", "\n", $sConfigContent);
|
||||
$sOriginalConfig = $sConfig;
|
||||
|
||||
if (!empty($sOperation)) {
|
||||
$iEditorTopMargin += 5;
|
||||
$sConfig = utils::ReadParam('new_config', '', false, 'raw_data');
|
||||
}
|
||||
|
||||
try {
|
||||
if ($sOperation == 'revert') {
|
||||
throw new Exception(Dict::S('config-reverted'), CONFIG_WARNING);
|
||||
}
|
||||
|
||||
if ($sOperation == 'save') {
|
||||
$sTransactionId = utils::ReadParam('transaction_id', '', false, 'transaction_id');
|
||||
if (!utils::IsTransactionValid($sTransactionId, true)) {
|
||||
throw new Exception(Dict::S('config-error-transaction'), CONFIG_ERROR);
|
||||
}
|
||||
|
||||
$sChecksum = utils::ReadParam('checksum');
|
||||
if ($sChecksum !== $sConfigChecksum) {
|
||||
throw new Exception(Dict::S('config-error-file-changed'), CONFIG_ERROR);
|
||||
}
|
||||
|
||||
if ($sConfig === $sOriginalConfig) {
|
||||
throw new Exception(Dict::S('config-no-change'), CONFIG_INFO);
|
||||
}
|
||||
TestConfig($sConfig, $oP); // throws exceptions
|
||||
|
||||
@chmod($sConfigFile, 0770); // Allow overwriting the file
|
||||
$sTmpFile = tempnam(SetupUtils::GetTmpDir(), 'itop-cfg-');
|
||||
// Don't write the file as-is since it would allow to inject any kind of PHP code.
|
||||
// Instead, write the interpreted version of the file
|
||||
// Note:
|
||||
// The actual raw PHP code will anyhow be interpreted exactly twice: once in TestConfig() above
|
||||
// and a second time during the load of the Config object below.
|
||||
// If you are really concerned about an iTop administrator crafting some malicious
|
||||
// PHP code inside the config file, then turn off the interactive configuration
|
||||
// editor by adding the configuration parameter:
|
||||
// 'itop-config' => array(
|
||||
// 'config_editor' => 'disabled',
|
||||
// )
|
||||
file_put_contents($sTmpFile, $sConfig);
|
||||
$oTempConfig = new Config($sTmpFile, true);
|
||||
$oTempConfig->WriteToFile($sConfigFile);
|
||||
@unlink($sTmpFile);
|
||||
@chmod($sConfigFile, 0440); // Read-only
|
||||
|
||||
if (DBPasswordInNewConfigIsOk($sConfig)) {
|
||||
$oAlert = AlertUIBlockFactory::MakeForSuccess('', Dict::S('config-saved'));
|
||||
} else {
|
||||
$oAlert = AlertUIBlockFactory::MakeForInformation('', Dict::S('config-saved-warning-db-password'));
|
||||
}
|
||||
$oP->AddUiBlock($oAlert);
|
||||
|
||||
$iWarnings = CheckAsyncTasksRetryConfig($oTempConfig, $oP);
|
||||
|
||||
// Read the config from disk after save
|
||||
$sConfigContent = file_get_contents($sConfigFile);
|
||||
$sConfigChecksum = md5($sConfigContent);
|
||||
$sConfig = str_replace("\r\n", "\n", $sConfigContent);
|
||||
$sOriginalConfig = $sConfig;
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$oAlert = GetAlertFromException($e);
|
||||
$oP->AddUiBlock($oAlert);
|
||||
}
|
||||
|
||||
// (remove EscapeHtml) N°5914 - Wrong encoding in modules configuration editor
|
||||
$oP->AddUiBlock(new Html('<p>'.Dict::S('config-edit-intro').'</p>'));
|
||||
|
||||
$oForm = new Form();
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('operation', 'save', 'operation'));
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', utils::GetNewTransactionId()));
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('checksum', $sConfigChecksum));
|
||||
|
||||
//--- Cancel button
|
||||
$oCancelButton = ButtonUIBlockFactory::MakeForCancel(Dict::S('config-cancel'), 'cancel_button', null, true, 'cancel_button');
|
||||
$oCancelButton->SetOnClickJsCode("return ResetConfig();");
|
||||
$oForm->AddSubBlock($oCancelButton);
|
||||
|
||||
//--- Submit button
|
||||
$oSubmitButton = ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('config-apply'), null, Dict::S('config-apply'), true, 'submit_button');
|
||||
$oForm->AddSubBlock($oSubmitButton);
|
||||
|
||||
//--- Config editor
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('prev_config', $sOriginalConfig, 'prev_config'));
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('new_config', $sOriginalConfig));
|
||||
$oForm->AddHtml("<div id =\"new_config\" style=\"position: absolute; top: ".$iEditorTopMargin."em; bottom: 0; left: 5px; right: 5px;\"></div>");
|
||||
$oP->AddUiBlock($oForm);
|
||||
|
||||
$oP->add_script(
|
||||
<<<'JS'
|
||||
var EditorUtils = (function() {
|
||||
var STORAGE_RANGE_KEY = 'cfgEditorRange';
|
||||
var STORAGE_LINE_KEY = 'cfgEditorFirstline';
|
||||
var _editorSavedRange = null;
|
||||
var _editorSavedFirstLine = null;
|
||||
|
||||
var saveEditorDisplay = function(editor) {
|
||||
_initObjectValues(editor);
|
||||
_persistObjectValues();
|
||||
};
|
||||
|
||||
var _initObjectValues = function(editor) {
|
||||
_editorSavedRange = editor.getSelectionRange();
|
||||
_editorSavedFirstLine = editor.renderer.getFirstVisibleRow();
|
||||
};
|
||||
|
||||
var _persistObjectValues = function() {
|
||||
sessionStorage.setItem(EditorUtils.STORAGE_RANGE_KEY, JSON.stringify(_editorSavedRange));
|
||||
sessionStorage.setItem(EditorUtils.STORAGE_LINE_KEY, _editorSavedFirstLine);
|
||||
};
|
||||
|
||||
var restoreEditorDisplay = function(editor) {
|
||||
_restoreObjectValues();
|
||||
_setEditorDisplay(editor);
|
||||
};
|
||||
|
||||
var _restoreObjectValues = function() {
|
||||
if ((sessionStorage.getItem(STORAGE_RANGE_KEY) == null)
|
||||
|| (sessionStorage.getItem(STORAGE_LINE_KEY) == null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_editorSavedRange = JSON.parse(sessionStorage.getItem(EditorUtils.STORAGE_RANGE_KEY));
|
||||
_editorSavedFirstLine = sessionStorage.getItem(EditorUtils.STORAGE_LINE_KEY);
|
||||
sessionStorage.removeItem(STORAGE_RANGE_KEY);
|
||||
sessionStorage.removeItem(STORAGE_LINE_KEY);
|
||||
};
|
||||
|
||||
var _setEditorDisplay = function(editor) {
|
||||
if ((_editorSavedRange == null) || (_editorSavedFirstLine == null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
editor.selection.setRange(_editorSavedRange);
|
||||
editor.renderer.scrollToRow(_editorSavedFirstLine);
|
||||
};
|
||||
|
||||
var getEditorForm = function(editor) {
|
||||
var editorContainer = $(editor.container);
|
||||
return editorContainer.closest("form");
|
||||
};
|
||||
|
||||
var updateConfigEditorButtonState = function(editor) {
|
||||
var isSameContent = (editor.getValue() == $('#prev_config').val());
|
||||
var hasNoError = $.isEmptyObject(editor.getSession().getAnnotations());
|
||||
$('#cancel_button').prop('disabled', isSameContent);
|
||||
$('#submit_button').prop('disabled', isSameContent || !hasNoError);
|
||||
};
|
||||
|
||||
return {
|
||||
STORAGE_RANGE_KEY: STORAGE_RANGE_KEY,
|
||||
STORAGE_LINE_KEY : STORAGE_LINE_KEY,
|
||||
saveEditorDisplay : saveEditorDisplay,
|
||||
restoreEditorDisplay : restoreEditorDisplay,
|
||||
getEditorForm : getEditorForm,
|
||||
updateConfigEditorButtonState : updateConfigEditorButtonState
|
||||
};
|
||||
})();
|
||||
JS
|
||||
);
|
||||
$oP->add_ready_script(<<<'JS'
|
||||
var editor = ace.edit("new_config");
|
||||
|
||||
var configurationSource = $('input[name="new_config"]');
|
||||
editor.getSession().setValue(configurationSource.val());
|
||||
|
||||
editor.getSession().on('change', function()
|
||||
{
|
||||
configurationSource.val(editor.getSession().getValue());
|
||||
EditorUtils.updateConfigEditorButtonState(editor);
|
||||
});
|
||||
editor.getSession().on("changeAnnotation", function()
|
||||
{
|
||||
EditorUtils.updateConfigEditorButtonState(editor);
|
||||
});
|
||||
|
||||
editor.setTheme("ace/theme/eclipse");
|
||||
editor.getSession().setMode("ace/mode/php");
|
||||
editor.commands.addCommand({
|
||||
name: 'save',
|
||||
bindKey: {win: "Ctrl-S", "mac": "Cmd-S"},
|
||||
exec: function(editor) {
|
||||
var editorForm = EditorUtils.getEditorForm(editor);
|
||||
var submitButton = $('#submit_button');
|
||||
|
||||
if (submitButton.is(":enabled")) {
|
||||
editorForm.trigger('submit');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var editorForm = EditorUtils.getEditorForm(editor);
|
||||
editorForm.on('submit', function() {
|
||||
EditorUtils.saveEditorDisplay(editor);
|
||||
});
|
||||
|
||||
|
||||
EditorUtils.restoreEditorDisplay(editor);
|
||||
editor.focus();
|
||||
JS
|
||||
);
|
||||
|
||||
$sConfirmCancel = addslashes(Dict::S('config-confirm-cancel'));
|
||||
$oP->add_script(<<<JS
|
||||
function ResetConfig()
|
||||
{
|
||||
$("#operation").attr('value', 'revert');
|
||||
if (confirm('$sConfirmCancel'))
|
||||
{
|
||||
$('input[name="new_config"]').val(prevConfig.val());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
JS
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
$oAlert = GetAlertFromException($e);
|
||||
$oP->AddUiBlock($oAlert);
|
||||
}
|
||||
|
||||
$oP->output();
|
||||
|
||||
@@ -22,6 +22,8 @@ SetupWebPage::AddModule(
|
||||
'src/Validator/ConfigNodesVisitor.php',
|
||||
'src/Validator/iTopConfigAstValidator.php',
|
||||
'src/Validator/iTopConfigSyntaxValidator.php',
|
||||
'src/Validator/iTopConfigValidator.php',
|
||||
'src/Controller/ConfigEditorController.php',
|
||||
),
|
||||
'webservice' => array(),
|
||||
'dictionary' => array(
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Config\Controller;
|
||||
|
||||
use Combodo\iTop\Application\TwigBase\Controller\Controller;
|
||||
use Combodo\iTop\Config\Validator\iTopConfigValidator;
|
||||
use Config;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
use SetupUtils;
|
||||
use utils;
|
||||
|
||||
class ConfigEditorController extends Controller
|
||||
{
|
||||
public const ROUTE_NAMESPACE = 'config_editor';
|
||||
public const MODULE_NAME = "itop-config";
|
||||
protected array $aWarnings = [];
|
||||
protected array $aInfo = [];
|
||||
protected array $aErrors = [];
|
||||
protected array $aSuccesses = [];
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(MODULESROOT.static::MODULE_NAME.'/templates', static::MODULE_NAME);
|
||||
}
|
||||
|
||||
public function OperationEdit() : void
|
||||
{
|
||||
$bShowEditor = true;
|
||||
$sConfigChecksum = '';
|
||||
$sCurrentConfig = '';
|
||||
|
||||
try {
|
||||
$sOperation = utils::ReadParam('edit_operation');
|
||||
if (MetaModel::GetConfig()->Get('demo_mode')) {
|
||||
throw new Exception(Dict::S('config-not-allowed-in-demo'), iTopConfigValidator::CONFIG_INFO);
|
||||
}
|
||||
|
||||
if (MetaModel::GetModuleSetting('itop-config', 'config_editor', '') == 'disabled') {
|
||||
throw new Exception(Dict::S('config-interactive-not-allowed'), iTopConfigValidator::CONFIG_WARNING);
|
||||
}
|
||||
|
||||
$sConfigFile = APPROOT.'conf/'.utils::GetCurrentEnvironment().'/config-itop.php';
|
||||
|
||||
$sCurrentConfig = file_get_contents($sConfigFile);
|
||||
$sConfigChecksum = md5($sCurrentConfig);
|
||||
|
||||
try {
|
||||
if ($sOperation == 'revert') {
|
||||
$this->AddAlert(Dict::S('config-reverted'), iTopConfigValidator::CONFIG_WARNING);
|
||||
}
|
||||
else if ($sOperation == 'save') {
|
||||
$sTransactionId = utils::ReadParam('transaction_id', '', false, 'transaction_id');
|
||||
if (!utils::IsTransactionValid($sTransactionId)) {
|
||||
throw new Exception(Dict::S('config-error-transaction'), iTopConfigValidator::CONFIG_ERROR);
|
||||
}
|
||||
$sChecksum = utils::ReadParam('checksum');
|
||||
if ($sChecksum !== $sConfigChecksum) {
|
||||
throw new Exception(Dict::S('config-error-file-changed'), iTopConfigValidator::CONFIG_ERROR);
|
||||
}
|
||||
|
||||
$sNewConfig = utils::ReadParam('new_config', '', false, 'raw_data');
|
||||
$sNewConfig = str_replace("\r\n", "\n", $sNewConfig);
|
||||
if ($sNewConfig === $sCurrentConfig) {
|
||||
throw new Exception(Dict::S('config-no-change'), iTopConfigValidator::CONFIG_INFO);
|
||||
}
|
||||
$oValidator = new iTopConfigValidator();
|
||||
|
||||
$oValidator->Validate($sNewConfig);// throws exceptions
|
||||
|
||||
@chmod($sConfigFile, 0770); // Allow overwriting the file
|
||||
$sTmpFile = tempnam(SetupUtils::GetTmpDir(), 'itop-cfg-');
|
||||
// Don't write the file as-is since it would allow to inject any kind of PHP code.
|
||||
// Instead, write the interpreted version of the file
|
||||
// Note:
|
||||
// The actual raw PHP code will anyhow be interpreted exactly twice: once in TestConfig() above
|
||||
// and a second time during the load of the Config object below.
|
||||
// If you are really concerned about an iTop administrator crafting some malicious
|
||||
// PHP code inside the config file, then turn off the interactive configuration
|
||||
// editor by adding the configuration parameter:
|
||||
// 'itop-config' => array(
|
||||
// 'config_editor' => 'disabled',
|
||||
// )
|
||||
file_put_contents($sTmpFile, $sNewConfig);
|
||||
$oTempConfig = new Config($sTmpFile, true);
|
||||
$oTempConfig->WriteToFile($sConfigFile);
|
||||
@unlink($sTmpFile);
|
||||
@chmod($sConfigFile, 0440); // Read-only
|
||||
|
||||
if ($oValidator->DBPasswordIsOk($oTempConfig->Get('db_pwd'))) {
|
||||
$this->AddAlert(Dict::S('config-saved'), iTopConfigValidator::CONFIG_SUCCESS);
|
||||
} else {
|
||||
$this->AddAlert(Dict::S('config-saved-warning-db-password'), iTopConfigValidator::CONFIG_INFO);
|
||||
}
|
||||
|
||||
$this->AddAlert($oValidator->CheckAsyncTasksRetryConfig($oTempConfig), iTopConfigValidator::CONFIG_WARNING);
|
||||
|
||||
|
||||
// Read the config from disk after save
|
||||
$sCurrentConfig = file_get_contents($sConfigFile);
|
||||
$sConfigChecksum = md5($sCurrentConfig);
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$this->AddAlertFromException($e);
|
||||
}
|
||||
|
||||
$this->AddAceScripts();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$bShowEditor = false;
|
||||
$this->AddAlertFromException($e);
|
||||
}
|
||||
|
||||
// display page
|
||||
$this->DisplayPage([
|
||||
'aErrors' => $this->aErrors,
|
||||
'aWarnings' => $this->aWarnings,
|
||||
'aNotices' => $this->aInfo,
|
||||
'aSuccesses' => $this->aSuccesses,
|
||||
'bShowEditor' => $bShowEditor,
|
||||
'sTransactionId' => utils::GetNewTransactionId(),
|
||||
'sChecksum' => $sConfigChecksum,
|
||||
'sPrevConfig' => $sCurrentConfig,
|
||||
'sNewConfig' => $sCurrentConfig,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function AddAceScripts(): void
|
||||
{
|
||||
$sAceDir = 'node_modules/ace-builds/src-min/';
|
||||
$this->AddLinkedScript(utils::GetAbsoluteUrlAppRoot().$sAceDir.'ace.js');
|
||||
$this->AddLinkedScript(utils::GetAbsoluteUrlAppRoot().$sAceDir.'mode-php.js');
|
||||
$this->AddLinkedScript(utils::GetAbsoluteUrlAppRoot().$sAceDir.'theme-eclipse.js');
|
||||
$this->AddLinkedScript(utils::GetAbsoluteUrlAppRoot().$sAceDir.'ext-searchbox.js');
|
||||
}
|
||||
|
||||
|
||||
public function AddAlertFromException(Exception $e): void
|
||||
{
|
||||
$this->AddAlert($e->getMessage(), $e->getCode());
|
||||
}
|
||||
|
||||
public function AddAlert(array|string $sMessage, $iLevel): void
|
||||
{
|
||||
if (is_array($sMessage)) {
|
||||
foreach ($sMessage as $sSingleMessage) {
|
||||
$this->AddAlert($sSingleMessage, $iLevel);
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch ($iLevel) {
|
||||
case iTopConfigValidator::CONFIG_SUCCESS :
|
||||
$this->aSuccesses[] = $sMessage;
|
||||
break;
|
||||
case iTopConfigValidator::CONFIG_WARNING :
|
||||
$this->aWarnings[] = $sMessage;
|
||||
break;
|
||||
case iTopConfigValidator::CONFIG_INFO :
|
||||
$this->aInfo[] = $sMessage;
|
||||
break;
|
||||
default :
|
||||
$this->aErrors[] = $sMessage;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,10 +31,10 @@ class iTopConfigAstValidator
|
||||
$aInitialNodes = $oParser->parse($sConfig);
|
||||
} catch (\Error $e) {
|
||||
$sMessage = 'Invalid configuration: '. \Dict::Format('config-parse-error', $e->getMessage(), $e->getLine());
|
||||
throw new \Exception($sMessage, 0, $e);
|
||||
throw new \Exception($sMessage, iTopConfigValidator::CONFIG_ERROR, $e);
|
||||
}catch (\Exception $e) {
|
||||
$sMessage = 'Invalid configuration: '. \Dict::Format('config-parse-error', $e->getMessage(), $e->getLine());
|
||||
throw new \Exception($sMessage, 0, $e);
|
||||
throw new \Exception($sMessage, iTopConfigValidator::CONFIG_ERROR, $e);
|
||||
}
|
||||
|
||||
$oTraverser = new NodeTraverser();
|
||||
|
||||
@@ -17,8 +17,7 @@ class iTopConfigSyntaxValidator
|
||||
*/
|
||||
public function Validate($sRawConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
try {
|
||||
ini_set('display_errors', 1);
|
||||
ob_start();
|
||||
// in PHP < 7.0.0 syntax errors are in output
|
||||
@@ -27,29 +26,24 @@ class iTopConfigSyntaxValidator
|
||||
eval('if(0){'.trim($sConfig).'}');
|
||||
$sNoise = trim(ob_get_contents());
|
||||
}
|
||||
catch (\Error $e)
|
||||
{
|
||||
catch (\Error $e) {
|
||||
// ParseError only thrown in PHP7
|
||||
throw new \Exception('Error in configuration: '.$e->getMessage().' at line '.$e->getLine());
|
||||
throw new \Exception('Error in configuration: '.$e->getMessage().' at line '.$e->getLine(), iTopConfigValidator::CONFIG_ERROR);
|
||||
}
|
||||
finally
|
||||
{
|
||||
finally {
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
if (strlen($sNoise) > 0)
|
||||
{
|
||||
if (preg_match("/(Error|Parse error|Notice|Warning): (.+) in \S+ : eval\(\)'d code on line (\d+)/i", strip_tags($sNoise), $aMatches))
|
||||
{
|
||||
if (strlen($sNoise) > 0) {
|
||||
if (preg_match("/(Error|Parse error|Notice|Warning): (.+) in \S+ : eval\(\)'d code on line (\d+)/i", strip_tags($sNoise), $aMatches)) {
|
||||
$sMessage = $aMatches[2];
|
||||
$sLine = $aMatches[3];
|
||||
$sMessage = \Dict::Format('config-parse-error', $sMessage, $sLine);
|
||||
throw new \Exception($sMessage);
|
||||
throw new \Exception($sMessage, iTopConfigValidator::CONFIG_ERROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
|
||||
throw new \Exception('Syntax error in configuration file: <tt>'.$sNoise.'</tt>');
|
||||
throw new \Exception('Syntax error in configuration file: <tt>'.$sNoise.'</tt>', iTopConfigValidator::CONFIG_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user