mirror of
https://github.com/Combodo/iTop.git
synced 2026-03-27 20:04:13 +01:00
Compare commits
16 Commits
feature/un
...
feature/92
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55faa8d504 | ||
|
|
7f8ec25977 | ||
|
|
41f8437c23 | ||
|
|
df8b25d4b4 | ||
|
|
511dabe2b0 | ||
|
|
0c517f254c | ||
|
|
c56c7a1f9d | ||
|
|
fb2f0f1447 | ||
|
|
b3223eb9b6 | ||
|
|
458a996c29 | ||
|
|
c61b21559c | ||
|
|
ed33238750 | ||
|
|
272678b8cd | ||
|
|
170014e8f0 | ||
|
|
006f666089 | ||
|
|
2a16143e53 |
@@ -810,6 +810,7 @@ HTML
|
||||
foreach ($aNotificationClasses as $sNotifClass) {
|
||||
$aNotifSearches[$sNotifClass] = DBObjectSearch::FromOQL("SELECT $sNotifClass AS Ev JOIN Trigger AS T ON Ev.trigger_id = T.id WHERE T.id IN (:triggers) AND Ev.object_id = :id");
|
||||
$aNotifSearches[$sNotifClass]->SetInternalParams($aParams);
|
||||
$aNotifSearches[$sNotifClass]->AllowAllData();
|
||||
$oNotifSet = new DBObjectSet($aNotifSearches[$sNotifClass], []);
|
||||
$iNotifsCount += $oNotifSet->Count();
|
||||
}
|
||||
@@ -823,6 +824,7 @@ HTML
|
||||
'menu' => false,
|
||||
'panel_title' => MetaModel::GetName($sNotifClass),
|
||||
'panel_icon' => MetaModel::GetClassIcon($sNotifClass, false),
|
||||
'display_unauthorized_objects' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,6 +724,10 @@ class DisplayBlock
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->m_oFilter->IsAllDataAllowed() && ($aExtraParams['display_unauthorized_objects'] ?? false) === true) {
|
||||
$this->m_oFilter->AllowAllData();
|
||||
}
|
||||
|
||||
$aExtraParams['query_params'] = $this->m_oFilter->GetInternalParams();
|
||||
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, $aOrderBy, $aQueryParams);
|
||||
}
|
||||
@@ -1381,7 +1385,10 @@ JS
|
||||
|
||||
// Check the classes that can be read (i.e authorized) by this user...
|
||||
foreach ($aClasses as $sAlias => $sClassName) {
|
||||
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $this->m_oSet) != UR_ALLOWED_NO) {
|
||||
if (
|
||||
(UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $this->m_oSet) !== UR_ALLOWED_NO)
|
||||
|| ($aExtraParams['display_unauthorized_objects'] ?? false) === true
|
||||
) {
|
||||
$aAuthorizedClasses[$sAlias] = $sClassName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ require_once(APPROOT.'/application/displayblock.class.inc.php');
|
||||
|
||||
class UISearchFormForeignKeys
|
||||
{
|
||||
private $m_sRemoteClass;
|
||||
private $m_iInputId;
|
||||
|
||||
public function __construct($sTargetClass, $iInputId = null)
|
||||
{
|
||||
$this->m_sRemoteClass = $sTargetClass;
|
||||
@@ -40,7 +43,7 @@ class UISearchFormForeignKeys
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function ShowModalSearchForeignKeys($oPage, $sTitle)
|
||||
public function ShowModalSearchForeignKeys($oPage)
|
||||
{
|
||||
|
||||
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
|
||||
@@ -60,8 +63,6 @@ class UISearchFormForeignKeys
|
||||
]
|
||||
));
|
||||
$sEmptyList = Dict::S('UI:Message:EmptyList:UseSearchForm');
|
||||
$sCancel = Dict::S('UI:Button:Cancel');
|
||||
$sAdd = Dict::S('UI:Button:Add');
|
||||
|
||||
$oPage->add(
|
||||
<<<HTML
|
||||
@@ -73,39 +74,6 @@ class UISearchFormForeignKeys
|
||||
</form>
|
||||
HTML
|
||||
);
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
$('#dlg_{$this->m_iInputId}').dialog({
|
||||
width: $(window).width()*0.8,
|
||||
height: $(window).height()*0.8,
|
||||
autoOpen: false,
|
||||
modal: true,
|
||||
resizeStop: oForeignKeysWidget{$this->m_iInputId}.UpdateSizes,
|
||||
buttons: [
|
||||
{
|
||||
text: Dict.S('UI:Button:Cancel'),
|
||||
class: "cancel ibo-is-alternative ibo-is-neutral",
|
||||
click: function() {
|
||||
$('#dlg_{$this->m_iInputId}').dialog('close');
|
||||
}
|
||||
},
|
||||
{
|
||||
text: Dict.S('UI:Button:Add'),
|
||||
id: 'btn_ok_{$this->m_iInputId}',
|
||||
class: "ok ibo-is-regular ibo-is-primary",
|
||||
click: function() {
|
||||
oForeignKeysWidget{$this->m_iInputId}.DoAddObjects(this.id);
|
||||
}
|
||||
},
|
||||
],
|
||||
|
||||
});
|
||||
$('#dlg_{$this->m_iInputId}').dialog('option', {title:'$sTitle'});
|
||||
$('#SearchFormToAdd_{$this->m_iInputId} form').on('submit.uilinksWizard', oForeignKeysWidget{$this->m_iInputId}.SearchObjectsToAdd);
|
||||
$('#SearchFormToAdd_{$this->m_iInputId}').on('resize', oForeignKeysWidget{$this->m_iInputId}.UpdateSizes);
|
||||
JS
|
||||
);
|
||||
}
|
||||
|
||||
public function GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter)
|
||||
@@ -119,31 +87,4 @@ JS
|
||||
IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for objects to be linked to the current object (i.e "remote" objects)
|
||||
*
|
||||
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
|
||||
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of m_sRemoteClass
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function ListResultsSearchForeignKeys(WebPage $oP, $sRemoteClass = '')
|
||||
{
|
||||
if ($sRemoteClass != '') {
|
||||
// assert(MetaModel::IsParentClass($this->m_sRemoteClass, $sRemoteClass));
|
||||
$oFilter = new DBObjectSearch($sRemoteClass);
|
||||
} else {
|
||||
// No remote class specified use the one defined in the linkedset
|
||||
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
|
||||
}
|
||||
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$oBlock->Display(
|
||||
$oP,
|
||||
"ResultsToAdd_{$this->m_iInputId}",
|
||||
['menu' => false, 'cssCount' => "#count_{$this->m_iInputId}", 'selection_mode' => true, 'table_id' => "add_{$this->m_iInputId}"]
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -122,6 +122,11 @@ class utils
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_VARIABLE_NAME = 'variable_name';
|
||||
/**
|
||||
* @var string For module codes (e.g. `itop-portal-base`, `combodo-webhook-integration`, `some-module-code-x.y`, ...)
|
||||
* @since 3.2.3 3.3.0 N°8554
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_MODULE_CODE = 'module_code';
|
||||
/**
|
||||
* @var string
|
||||
* @since 2.7.10 3.0.0
|
||||
@@ -393,6 +398,7 @@ class utils
|
||||
* @since 2.7.10 N°6606 use the utils::ENUM_SANITIZATION_* const
|
||||
* @since 2.7.10 N°6606 new case for ENUM_SANITIZATION_FILTER_PHP_CLASS
|
||||
* @since 3.2.1-1 N°8242 Allow value to be an array for every filter
|
||||
* @since 3.2.3 3.3.0 N°8554 new case for ENUM_SANITIZATION_FILTER_MODULE_CODE
|
||||
*
|
||||
* @link https://www.php.net/manual/en/filter.filters.sanitize.php PHP sanitization filters
|
||||
*/
|
||||
@@ -480,7 +486,7 @@ class utils
|
||||
);
|
||||
break;
|
||||
|
||||
// For XML / HTML node id selector
|
||||
// For XML / HTML node selector
|
||||
case static::ENUM_SANITIZATION_FILTER_ELEMENT_SELECTOR:
|
||||
$retValue = filter_var(
|
||||
$value,
|
||||
@@ -493,6 +499,15 @@ class utils
|
||||
$retValue = preg_replace('/[^a-zA-Z0-9_]/', '', $value);
|
||||
break;
|
||||
|
||||
case static::ENUM_SANITIZATION_FILTER_MODULE_CODE:
|
||||
// Module codes allow all alphabets letters, numbers, dash and dot characters
|
||||
$retValue = filter_var(
|
||||
$value,
|
||||
FILTER_VALIDATE_REGEXP,
|
||||
['options' => ['regexp' => '/^[\p{L}\d.-]+$/u']]
|
||||
);
|
||||
break;
|
||||
|
||||
// For URL
|
||||
case static::ENUM_SANITIZATION_FILTER_URL:
|
||||
$retValue = filter_var($value, FILTER_SANITIZE_URL);
|
||||
@@ -1440,6 +1455,12 @@ class utils
|
||||
|
||||
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
|
||||
/** @var \DBObjectSet $param */
|
||||
|
||||
// Check if the user has the right to read the objects of this list, otherwise do not propose any action (eg. configure this list, export, etc.)
|
||||
if (UserRights::IsActionAllowed($param->GetFilter()->GetClass(), UR_ACTION_READ, $param) !== UR_ALLOWED_YES) {
|
||||
break;
|
||||
}
|
||||
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink(true);
|
||||
$sDataTableId = is_null($sDataTableId) ? '' : $sDataTableId;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"pear/archive_tar": "~1.4.14",
|
||||
"pelago/emogrifier": "^7.2.0",
|
||||
"psr/log": "^3.0.0",
|
||||
"scssphp/scssphp": "^1.12.1",
|
||||
"scssphp/scssphp": "dev-combodo/1.x",
|
||||
"symfony/console": "~6.4.0",
|
||||
"symfony/dotenv": "~6.4.0",
|
||||
"symfony/framework-bundle": "~6.4.0",
|
||||
@@ -43,6 +43,10 @@
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/EsupPortail/phpCAS"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/combodo-itop-libs/scssphp"
|
||||
}
|
||||
],
|
||||
"suggest": {
|
||||
|
||||
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": "ceac38f6033afe07b7ab977fa39fe348",
|
||||
"content-hash": "eebbdc6c10a479b0e62fc18d88496f5c",
|
||||
"packages": [
|
||||
{
|
||||
"name": "apereo/phpcas",
|
||||
@@ -1588,16 +1588,16 @@
|
||||
},
|
||||
{
|
||||
"name": "scssphp/scssphp",
|
||||
"version": "v1.13.0",
|
||||
"version": "dev-combodo/1.x",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/scssphp/scssphp.git",
|
||||
"reference": "63d1157457e5554edf00b0c1fabab4c1511d2520"
|
||||
"url": "https://github.com/combodo-itop-libs/scssphp.git",
|
||||
"reference": "dde81c0a39d02e8e6fc81b70269747734e16d526"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/63d1157457e5554edf00b0c1fabab4c1511d2520",
|
||||
"reference": "63d1157457e5554edf00b0c1fabab4c1511d2520",
|
||||
"url": "https://api.github.com/repos/combodo-itop-libs/scssphp/zipball/dde81c0a39d02e8e6fc81b70269747734e16d526",
|
||||
"reference": "dde81c0a39d02e8e6fc81b70269747734e16d526",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1626,8 +1626,8 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": false,
|
||||
"forward-command": false
|
||||
"forward-command": false,
|
||||
"bin-links": false
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -1635,7 +1635,11 @@
|
||||
"ScssPhp\\ScssPhp\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"ScssPhp\\ScssPhp\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
@@ -1661,10 +1665,9 @@
|
||||
"stylesheet"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/scssphp/scssphp/issues",
|
||||
"source": "https://github.com/scssphp/scssphp/tree/v1.13.0"
|
||||
"source": "https://github.com/combodo-itop-libs/scssphp/tree/combodo/1.x"
|
||||
},
|
||||
"time": "2024-08-17T21:02:11+00:00"
|
||||
"time": "2026-03-23T15:26:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "soundasleep/html2text",
|
||||
@@ -5097,7 +5100,8 @@
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"apereo/phpcas": 20
|
||||
"apereo/phpcas": 20,
|
||||
"scssphp/scssphp": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
|
||||
@@ -234,10 +234,11 @@ abstract class Action extends cmdbAbstractObject
|
||||
}
|
||||
|
||||
$oActionFilter = DBObjectSearch::FromOQL($sActionQueryOql, $aActionQueryParams);
|
||||
$oActionFilter->AllowAllData();
|
||||
$oSet = new DBObjectSet($oActionFilter, ['date' => false]);
|
||||
|
||||
$sPanelTitle = Dict::Format('Action:last_executions_tab_panel_title', $sActionQueryLimit);
|
||||
$oExecutionsListBlock = DataTableUIBlockFactory::MakeForResult($oPage, 'action_executions_list', $oSet, ['panel_title' => $sPanelTitle]);
|
||||
$oExecutionsListBlock = DataTableUIBlockFactory::MakeForResult($oPage, 'action_executions_list', $oSet, ['panel_title' => $sPanelTitle, 'display_unauthorized_objects' => true]);
|
||||
|
||||
$oPage->AddUiBlock($oExecutionsListBlock);
|
||||
}
|
||||
|
||||
@@ -8990,7 +8990,10 @@ class AttributeStopWatch extends AttributeDefinition
|
||||
switch ($sThresholdCode) {
|
||||
case 'deadline':
|
||||
if ($value) {
|
||||
if (is_int($value)) {
|
||||
if (is_numeric($value)) {
|
||||
if (!is_int($value)) {
|
||||
$value = intval($value);
|
||||
}
|
||||
$sDate = date(AttributeDateTime::GetInternalFormat(), $value);
|
||||
$sRet = AttributeDeadline::FormatDeadline($sDate);
|
||||
} else {
|
||||
|
||||
@@ -409,7 +409,7 @@
|
||||
</php_parent>
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>core/cmdb,view_in_gui</category>
|
||||
<category>core/cmdb,grant_by_profile,silo</category>
|
||||
<abstract>false</abstract>
|
||||
<key_type>autoincrement</key_type>
|
||||
<db_table>priv_event_newsroom</db_table>
|
||||
@@ -888,7 +888,7 @@
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>Event</parent>
|
||||
<properties>
|
||||
<category>core/cmdb,view_in_gui</category>
|
||||
<category>core/cmdb,grant_by_profile,silo</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="message" xsi:type="AttributeText"/>
|
||||
|
||||
@@ -26,7 +26,7 @@ class Event extends DBObject implements iDisplay
|
||||
{
|
||||
$aParams =
|
||||
[
|
||||
"category" => "core/cmdb,view_in_gui",
|
||||
"category" => "core/cmdb,grant_by_profile,silo",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
@@ -120,7 +120,7 @@ class EventNotification extends Event
|
||||
{
|
||||
$aParams =
|
||||
[
|
||||
"category" => "core/cmdb,view_in_gui",
|
||||
"category" => "core/cmdb,grant_by_profile,silo",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
@@ -154,7 +154,7 @@ class EventNotificationEmail extends EventNotification
|
||||
{
|
||||
$aParams =
|
||||
[
|
||||
"category" => "core/cmdb,view_in_gui",
|
||||
"category" => "core/cmdb,grant_by_profile,silo",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
@@ -190,7 +190,7 @@ class EventIssue extends Event
|
||||
{
|
||||
$aParams =
|
||||
[
|
||||
"category" => "core/cmdb,view_in_gui",
|
||||
"category" => "core/cmdb,grant_by_profile,silo",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
@@ -284,7 +284,7 @@ class EventWebService extends Event
|
||||
{
|
||||
$aParams =
|
||||
[
|
||||
"category" => "core/cmdb,view_in_gui",
|
||||
"category" => "core/cmdb,grant_by_profile,silo",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
@@ -319,7 +319,7 @@ class EventRestService extends Event
|
||||
{
|
||||
$aParams =
|
||||
[
|
||||
"category" => "core/cmdb,view_in_gui",
|
||||
"category" => "core/cmdb,grant_by_profile,silo",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
@@ -354,7 +354,7 @@ class EventLoginUsage extends Event
|
||||
{
|
||||
$aParams =
|
||||
[
|
||||
"category" => "core/cmdb,view_in_gui",
|
||||
"category" => "core/cmdb,grant_by_profile,silo",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
@@ -392,7 +392,7 @@ class EventOnObject extends Event
|
||||
{
|
||||
$aParams =
|
||||
[
|
||||
"category" => "core/cmdb,view_in_gui",
|
||||
"category" => "core/cmdb,grant_by_profile,silo",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
|
||||
@@ -350,15 +350,18 @@ class ormDocument
|
||||
if (!is_object($oObj)) {
|
||||
// If access to the document is not granted, check if the access to the host object is allowed
|
||||
$oObj = MetaModel::GetObject($sClass, $id, false, true);
|
||||
$bHasHostRights = false;
|
||||
if ($oObj instanceof Attachment) {
|
||||
$sItemClass = $oObj->Get('item_class');
|
||||
$sItemId = $oObj->Get('item_id');
|
||||
$oHost = MetaModel::GetObject($sItemClass, $sItemId, false, false);
|
||||
if (!is_object($oHost)) {
|
||||
$oObj = null;
|
||||
if (is_object($oHost)) {
|
||||
$bHasHostRights = true;
|
||||
}
|
||||
}
|
||||
if (!is_object($oObj)) {
|
||||
|
||||
// We could neither read the object nor get a host object matching our rights
|
||||
if ($bHasHostRights !== true) {
|
||||
throw new Exception("Invalid id ($id) for class '$sClass' - the object does not exist or you are not allowed to view it");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,8 +242,8 @@ try {
|
||||
throw new SecurityException(Dict::S('iTopHub:FailAuthent'));
|
||||
}
|
||||
// First step: prepare the datamodel, if it fails, roll-back
|
||||
$aSelectedExtensionCodes = utils::ReadParam('extension_codes', []);
|
||||
$aSelectedExtensionDirs = utils::ReadParam('extension_dirs', []);
|
||||
$aSelectedExtensionCodes = utils::ReadParam('extension_codes', [], false, utils::ENUM_SANITIZATION_FILTER_MODULE_CODE);
|
||||
$aSelectedExtensionDirs = utils::ReadParam('extension_dirs', [], false, utils::ENUM_SANITIZATION_FILTER_MODULE_CODE);
|
||||
|
||||
$oRuntimeEnv = new HubRunTimeEnvironment('production', false); // use a temp environment: production-build
|
||||
$oRuntimeEnv->MoveSelectedExtensions(APPROOT.'/data/downloaded-extensions/', $aSelectedExtensionDirs);
|
||||
|
||||
@@ -1386,19 +1386,27 @@ class ObjectController extends BrickController
|
||||
if ($oField instanceof DateTimeField) {
|
||||
$oField->SetDateTimePickerWidgetParent($sDateTimePickerWidgetParent);
|
||||
}
|
||||
$sFieldRendererClass = BsLinkedSetFieldRenderer::GetFieldRendererClass($oField);
|
||||
|
||||
// View data
|
||||
$sValue = $oAttDef->GetAsHTML($oNewLink->Get($sAttCode));
|
||||
$aObjectData['attributes']['lnk__'.$sAttCode] = [
|
||||
'object_class' => $sLinkClass,
|
||||
'object_id' => $oNewLink->GetKey(),
|
||||
'prefix' => 'lnk__',
|
||||
'attribute_code' => $sAttCode,
|
||||
'attribute_type' => get_class($oAttDef),
|
||||
'value_html' => $sValue,
|
||||
];
|
||||
|
||||
// If the field has a renderer we adjust view data
|
||||
$sFieldRendererClass = BsLinkedSetFieldRenderer::GetFieldRendererClass($oField);
|
||||
if ($sFieldRendererClass !== null) {
|
||||
$oFieldRenderer = new $sFieldRendererClass($oField);
|
||||
$oFieldOutput = $oFieldRenderer->Render();
|
||||
$sValue = $oFieldOutput->GetHtml();
|
||||
$aObjectData['attributes']['lnk__'.$sAttCode]['value_html'] = $oFieldOutput->GetHtml();
|
||||
$aObjectData['attributes']['lnk__'.$sAttCode]['css_inline'] = $oFieldOutput->GetCss();
|
||||
$aObjectData['attributes']['lnk__'.$sAttCode]['js_inline'] = $oFieldOutput->GetJs();
|
||||
}
|
||||
$aObjectData['attributes']['lnk__'.$sAttCode] = [
|
||||
'att_code' => $sAttCode,
|
||||
'value' => $sValue,
|
||||
'css_inline' => $oFieldOutput->GetCss(),
|
||||
'js_inline' => $oFieldOutput->GetJs(),
|
||||
];
|
||||
}
|
||||
|
||||
$aData['items'][] = $aObjectData;
|
||||
|
||||
@@ -186,6 +186,7 @@
|
||||
<group id="AdminSysReadOnly" _delta="define">
|
||||
<classes>
|
||||
<class id="ItopFenceLogin"/>
|
||||
<class id="ModuleInstallation"/>
|
||||
</classes>
|
||||
</group>
|
||||
<group id="AdminSys" _delta="define">
|
||||
@@ -195,6 +196,11 @@
|
||||
<class id="RessourceHybridAuthMenu"/>
|
||||
</classes>
|
||||
</group>
|
||||
<group id="Event" _delta="define">
|
||||
<classes>
|
||||
<class id="Event"/>
|
||||
</classes>
|
||||
</group>
|
||||
</groups>
|
||||
<profiles>
|
||||
<profile id="117" _delta="define">
|
||||
@@ -290,6 +296,16 @@
|
||||
<action id="stimulus:ev_close">allow</action>
|
||||
</actions>
|
||||
</group>
|
||||
<group id="Event">
|
||||
<actions>
|
||||
<action id="action:read">allow</action>
|
||||
<action id="action:bulk read">allow</action>
|
||||
<action id="action:write">allow</action>
|
||||
<action id="action:bulk write">allow</action>
|
||||
<action id="action:delete">allow</action>
|
||||
<action id="action:bulk delete">allow</action>
|
||||
</actions>
|
||||
</group>
|
||||
</groups>
|
||||
</profile>
|
||||
<profile id="3" _delta="define">
|
||||
|
||||
@@ -332,6 +332,12 @@ CombodoModal._ConvertButtonDefinition = function (aButtonsDefinitions) {
|
||||
class: typeof(element.classes) !== 'undefined' ? element.classes.join(' ') : '',
|
||||
click: element.callback_on_click
|
||||
}
|
||||
|
||||
// id is optional, and we don't want to set it if not defined
|
||||
if (typeof element.id !== 'undefined' && element.id !== null) {
|
||||
aButton.id = element.id;
|
||||
}
|
||||
|
||||
aConverted.push(aButton);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -35,116 +35,63 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, oSearchWidgetElmt, sF
|
||||
this.sAttCode = sAttCode;
|
||||
this.oSearchWidgetElmt = oSearchWidgetElmt;
|
||||
this.emptyHtml = ''; // content to be displayed when the search results are empty (when opening the dialog)
|
||||
this.emptyOnClose = true; // Workaround for the JQuery dialog being very slow when opening and closing if the content contains many INPUT tags
|
||||
this.ajax_request = null;
|
||||
// this.bSelectMode = bSelectMode; // true if the edited field is a SELECT, false if it's an autocomplete
|
||||
// this.bSearchMode = bSearchMode; // true if selecting a value in the context of a search form
|
||||
var me = this;
|
||||
|
||||
this.Init = function()
|
||||
{
|
||||
// make sure that the form is clean
|
||||
$('#linkedset_'+this.id+' .selection').each( function() { this.checked = false; });
|
||||
$('#'+this.id+'_btnRemove').prop('disabled', false);
|
||||
|
||||
$('<div id="dlg_'+me.id+'"></div>').appendTo(document.body);
|
||||
|
||||
// me.trace(dialog);
|
||||
|
||||
//TODO : check and remove all unneded code bellow this line!!
|
||||
|
||||
$('#'+this.id+'_linksToRemove').val('');
|
||||
|
||||
$('#linkedset_'+me.id).on('remove', function() {
|
||||
// prevent having the dlg div twice
|
||||
$('#dlg_'+me.id).remove();
|
||||
});
|
||||
|
||||
$('#'+this.iInputId).closest('form').on('submit', function() {
|
||||
return me.OnFormSubmit();
|
||||
});
|
||||
};
|
||||
|
||||
this.StopPendingRequest = function()
|
||||
{
|
||||
if (me.ajax_request)
|
||||
{
|
||||
me.ajax_request.abort();
|
||||
me.ajax_request = null;
|
||||
}
|
||||
};
|
||||
|
||||
this.ShowModalSearchForeignKeys = function()
|
||||
{
|
||||
// // Query the server to get the form to search for target objects
|
||||
// if (me.bSelectMode)
|
||||
// {
|
||||
// $('#fstatus_'+me.id).html('<img src="../images/indicator.gif" />');
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// $('#label_'+me.id).addClass('dlg_loading');
|
||||
// }
|
||||
$('#label_'+me.id).addClass('dlg_loading');
|
||||
var theMap = {
|
||||
sAttCode: me.sAttCode,
|
||||
iInputId: me.id,
|
||||
sTitle: me.sTitle,
|
||||
sTargetClass: me.sTargetClass,
|
||||
// bSearchMode: me.bSearchMode,
|
||||
operation: 'ShowModalSearchForeignKeys'
|
||||
};
|
||||
const oModalParams = {
|
||||
content: {
|
||||
endpoint: AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'),
|
||||
data: {
|
||||
sAttCode: me.sAttCode,
|
||||
iInputId: me.id,
|
||||
sTargetClass: me.sTargetClass,
|
||||
operation: 'ShowModalSearchForeignKeys'
|
||||
},
|
||||
},
|
||||
title: me.sTitle,
|
||||
id: 'dlg_'+me.id,
|
||||
size: 'lg',
|
||||
buttons: [
|
||||
{
|
||||
text: Dict.S('UI:Button:Cancel'),
|
||||
callback_on_click: function() {
|
||||
$(this).dialog("close");
|
||||
},
|
||||
classes: ['cancel', 'ibo-is-alternative', 'ibo-is-neutral'],
|
||||
},
|
||||
{
|
||||
text: Dict.S('UI:Button:Add'),
|
||||
id: "btn_ok_"+me.id,
|
||||
classes: ['ok', 'ibo-is-regular', 'ibo-is-primary'],
|
||||
callback_on_click: function() {
|
||||
me.DoAddObjects();
|
||||
}
|
||||
}
|
||||
],
|
||||
callback_on_content_loaded: function(oModalContentElement){
|
||||
// Update initial buttons state
|
||||
me.UpdateButtons();
|
||||
},
|
||||
|
||||
extra_options: {
|
||||
callback_on_modal_close: function () {
|
||||
$(this).remove(); // destroy then remove dialog object
|
||||
}
|
||||
}
|
||||
}
|
||||
const oModal = CombodoModal.OpenModal(oModalParams);
|
||||
|
||||
|
||||
// Make sure that we cancel any pending request before issuing another
|
||||
// since responses may arrive in arbitrary order
|
||||
me.StopPendingRequest();
|
||||
|
||||
// Run the query and get the result back directly in HTML
|
||||
me.ajax_request = $.post( AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'), theMap,
|
||||
function(data)
|
||||
{
|
||||
// $('#dlg_'+me.id).html(data);
|
||||
$('#dlg_'+me.id).empty().append($(data)); // $(data).filter(':not(script)'));
|
||||
$('#dlg_'+me.id).dialog('open');
|
||||
me.UpdateSizes();
|
||||
me.UpdateButtons();
|
||||
me.ajax_request = null;
|
||||
me.ListResultsSearchForeignKeys();
|
||||
},
|
||||
'html'
|
||||
);
|
||||
};
|
||||
|
||||
this.UpdateSizes = function()
|
||||
{
|
||||
var dlg = $('#dlg_'+me.id);
|
||||
// Adjust the dialog's size to fit into the screen
|
||||
if (dlg.width() > ($(window).width()-40))
|
||||
{
|
||||
dlg.width($(window).width()-40);
|
||||
}
|
||||
if (dlg.height() > ($(window).height()-70))
|
||||
{
|
||||
dlg.height($(window).height()-70);
|
||||
}
|
||||
var searchForm = dlg.find('div.display_block:first'); // Top search form, enclosing display_block
|
||||
var results = $('#SearchResultsToAdd_'+me.id);
|
||||
var oPadding = {};
|
||||
var aKeys = ['top', 'right', 'bottom', 'left'];
|
||||
for(k in aKeys)
|
||||
{
|
||||
oPadding[aKeys[k]] = 0;
|
||||
if (dlg.css('padding-'+aKeys[k]))
|
||||
{
|
||||
oPadding[aKeys[k]] = parseInt(dlg.css('padding-'+aKeys[k]).replace('px', ''));
|
||||
}
|
||||
}
|
||||
//var width = dlg.innerWidth() - oPadding['right'] - oPadding['left'] - 22; // 5 (margin-left) + 5 (padding-left) + 5 (padding-right) + 5 (margin-right) + 2 for rounding !
|
||||
var height = dlg.innerHeight()-oPadding['top']-oPadding['bottom']-22;
|
||||
var form_height = searchForm.outerHeight();
|
||||
results.height(height - form_height - 40); // Leave some space for the buttons
|
||||
// Bind events
|
||||
oModal.on('change', '#count_'+me.id, function(){
|
||||
me.UpdateButtons();
|
||||
});
|
||||
};
|
||||
|
||||
this.UpdateButtons = function()
|
||||
@@ -160,63 +107,6 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, oSearchWidgetElmt, sF
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
this.ListResultsSearchForeignKeys = function ()
|
||||
{
|
||||
var theMap = {
|
||||
sTargetClass: me.sTargetClass,
|
||||
iInputId: me.id,
|
||||
sFilter: me.sfilter,
|
||||
// bSearchMode: me.bSearchMode
|
||||
};
|
||||
|
||||
// Gather the parameters from the search form
|
||||
$('#fs_'+me.id+' :input').each( function() {
|
||||
if (this.name !== '')
|
||||
{
|
||||
var val = $(this).val(); // supports multiselect as well
|
||||
if (val !== null)
|
||||
{
|
||||
theMap[this.name] = val;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
theMap['sRemoteClass'] = theMap['class']; // swap 'class' (defined in the form) and 'remoteClass'
|
||||
theMap.operation = 'ListResultsSearchForeignKeys'; // Override what is defined in the form itself
|
||||
theMap.sAttCode = me.sAttCode;
|
||||
var sSearchAreaId = '#SearchResultsToAdd_'+me.id;
|
||||
//$(sSearchAreaId).html('<div style="text-align:center;width:100%;height:24px;vertical-align:middle;"><img src="../images/indicator.gif" /></div>');
|
||||
$(sSearchAreaId).block();
|
||||
me.UpdateButtons();
|
||||
|
||||
// Make sure that we cancel any pending request before issuing another
|
||||
// since responses may arrive in arbitrary order
|
||||
me.StopPendingRequest();
|
||||
|
||||
// Run the query and display the results
|
||||
me.ajax_request = $.post(AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'), theMap,
|
||||
function(data)
|
||||
{
|
||||
$(sSearchAreaId).html(data);
|
||||
$('#fr_'+me.id+' input:radio').on('click', function() { me.UpdateButtons(); });
|
||||
me.UpdateButtons();
|
||||
me.ajax_request = null;
|
||||
$('#count_'+me.id).on('change', function(){
|
||||
me.UpdateButtons();
|
||||
});
|
||||
me.UpdateSizes();
|
||||
},
|
||||
'html'
|
||||
);
|
||||
|
||||
return false; // Don't submit the form, stay in the current page !
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
@@ -286,56 +176,4 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, oSearchWidgetElmt, sF
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
// Workaround for a ui.jquery limitation: if the content of
|
||||
// the dialog contains many INPUTs, closing and opening the
|
||||
// dialog is very slow. So empty it each time.
|
||||
this.OnClose = function()
|
||||
{
|
||||
me.StopPendingRequest();
|
||||
// called by the dialog, so in the context 'this' points to the jQueryObject
|
||||
if (me.emptyOnClose)
|
||||
{
|
||||
$('#SearchResultsToAdd_'+me.id).html(me.emptyHtml);
|
||||
}
|
||||
$('#label_'+me.id).removeClass('dlg_loading');
|
||||
$('#label_'+me.id).focus();
|
||||
me.ajax_request = null;
|
||||
};
|
||||
|
||||
this.DoSelectObjectClass = function()
|
||||
{
|
||||
// Retrieving selected value
|
||||
var oSelectedClass = $('#ac_create_'+me.id+' select');
|
||||
if(oSelectedClass.length !== 1) return;
|
||||
|
||||
// Setting new target class
|
||||
me.sTargetClass = oSelectedClass.val();
|
||||
|
||||
// Opening real creation form
|
||||
$('#ac_create_'+me.id).dialog('close');
|
||||
me.CreateObject();
|
||||
};
|
||||
|
||||
this.Update = function()
|
||||
{
|
||||
if ($('#'+me.id).prop('disabled'))
|
||||
{
|
||||
$('#v_'+me.id).html('');
|
||||
$('#label_'+me.id).prop('disabled', true);
|
||||
$('#label_'+me.id).css({'background': 'transparent'});
|
||||
$('#mini_add_'+me.id).hide();
|
||||
$('#mini_tree_'+me.id).hide();
|
||||
$('#mini_search_'+me.id).hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#label_'+me.id).prop('disabled', false);
|
||||
$('#label_'+me.id).css({'background': '#fff url(../images/ac-background.gif) no-repeat right'});
|
||||
$('#mini_add_'+me.id).show();
|
||||
$('#mini_tree_'+me.id).show();
|
||||
$('#mini_search_'+me.id).show();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1654,17 +1654,17 @@
|
||||
},
|
||||
{
|
||||
"name": "scssphp/scssphp",
|
||||
"version": "v1.13.0",
|
||||
"version_normalized": "1.13.0.0",
|
||||
"version": "dev-combodo/1.x",
|
||||
"version_normalized": "dev-combodo/1.x",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/scssphp/scssphp.git",
|
||||
"reference": "63d1157457e5554edf00b0c1fabab4c1511d2520"
|
||||
"url": "https://github.com/combodo-itop-libs/scssphp.git",
|
||||
"reference": "dde81c0a39d02e8e6fc81b70269747734e16d526"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/63d1157457e5554edf00b0c1fabab4c1511d2520",
|
||||
"reference": "63d1157457e5554edf00b0c1fabab4c1511d2520",
|
||||
"url": "https://api.github.com/repos/combodo-itop-libs/scssphp/zipball/dde81c0a39d02e8e6fc81b70269747734e16d526",
|
||||
"reference": "dde81c0a39d02e8e6fc81b70269747734e16d526",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1687,15 +1687,15 @@
|
||||
"ext-iconv": "Can be used as fallback when ext-mbstring is not available",
|
||||
"ext-mbstring": "For best performance, mbstring should be installed as it is faster than ext-iconv"
|
||||
},
|
||||
"time": "2024-08-17T21:02:11+00:00",
|
||||
"time": "2026-03-23T15:26:59+00:00",
|
||||
"bin": [
|
||||
"bin/pscss"
|
||||
],
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": false,
|
||||
"forward-command": false
|
||||
"forward-command": false,
|
||||
"bin-links": false
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
@@ -1704,7 +1704,11 @@
|
||||
"ScssPhp\\ScssPhp\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"ScssPhp\\ScssPhp\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
@@ -1730,8 +1734,7 @@
|
||||
"stylesheet"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/scssphp/scssphp/issues",
|
||||
"source": "https://github.com/scssphp/scssphp/tree/v1.13.0"
|
||||
"source": "https://github.com/combodo-itop-libs/scssphp/tree/combodo/1.x"
|
||||
},
|
||||
"install-path": "../scssphp/scssphp"
|
||||
},
|
||||
|
||||
@@ -292,9 +292,9 @@
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'scssphp/scssphp' => array(
|
||||
'pretty_version' => 'v1.13.0',
|
||||
'version' => '1.13.0.0',
|
||||
'reference' => '63d1157457e5554edf00b0c1fabab4c1511d2520',
|
||||
'pretty_version' => 'dev-combodo/1.x',
|
||||
'version' => 'dev-combodo/1.x',
|
||||
'reference' => 'dde81c0a39d02e8e6fc81b70269747734e16d526',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../scssphp/scssphp',
|
||||
'aliases' => array(),
|
||||
|
||||
@@ -5052,7 +5052,7 @@ EOL;
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function multiplyMedia(Environment $env = null, $childQueries = null)
|
||||
protected function multiplyMedia(?Environment $env = null, $childQueries = null)
|
||||
{
|
||||
if (
|
||||
! isset($env) ||
|
||||
@@ -5144,7 +5144,7 @@ EOL;
|
||||
*
|
||||
* @return \ScssPhp\ScssPhp\Compiler\Environment
|
||||
*/
|
||||
protected function pushEnv(Block $block = null)
|
||||
protected function pushEnv(?Block $block = null)
|
||||
{
|
||||
$env = new Environment();
|
||||
$env->parent = $this->env;
|
||||
@@ -5208,7 +5208,7 @@ EOL;
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function set($name, $value, $shadow = false, Environment $env = null, $valueUnreduced = null)
|
||||
protected function set($name, $value, $shadow = false, ?Environment $env = null, $valueUnreduced = null)
|
||||
{
|
||||
$name = $this->normalizeName($name);
|
||||
|
||||
@@ -5314,7 +5314,7 @@ EOL;
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function get($name, $shouldThrow = true, Environment $env = null, $unreduced = false)
|
||||
public function get($name, $shouldThrow = true, ?Environment $env = null, $unreduced = false)
|
||||
{
|
||||
$normalizedName = $this->normalizeName($name);
|
||||
$specialContentKey = static::$namespaces['special'] . 'content';
|
||||
@@ -5379,7 +5379,7 @@ EOL;
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function has($name, Environment $env = null)
|
||||
protected function has($name, ?Environment $env = null)
|
||||
{
|
||||
return ! \is_null($this->get($name, false, $env));
|
||||
}
|
||||
|
||||
@@ -272,7 +272,7 @@ abstract class Formatter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerator = null)
|
||||
public function format(OutputBlock $block, ?SourceMapGenerator $sourceMapGenerator = null)
|
||||
{
|
||||
$this->sourceMapGenerator = null;
|
||||
|
||||
|
||||
@@ -578,7 +578,7 @@ class Number extends Node implements \ArrayAccess, \JsonSerializable
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function output(Compiler $compiler = null)
|
||||
public function output(?Compiler $compiler = null)
|
||||
{
|
||||
$dimension = round($this->dimension, self::PRECISION);
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ class Parser
|
||||
* @param bool $cssOnly
|
||||
* @param LoggerInterface|null $logger
|
||||
*/
|
||||
public function __construct($sourceName, $sourceIndex = 0, $encoding = 'utf-8', Cache $cache = null, $cssOnly = false, LoggerInterface $logger = null)
|
||||
public function __construct($sourceName, $sourceIndex = 0, $encoding = 'utf-8', ?Cache $cache = null, $cssOnly = false, ?LoggerInterface $logger = null)
|
||||
{
|
||||
$this->sourceName = $sourceName ?: '(stdin)';
|
||||
$this->sourceIndex = $sourceIndex;
|
||||
|
||||
@@ -59,7 +59,7 @@ final class Warn
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function setCallback(callable $callback = null)
|
||||
public static function setCallback(?callable $callback = null)
|
||||
{
|
||||
$previousCallback = self::$callback;
|
||||
self::$callback = $callback;
|
||||
|
||||
@@ -34,7 +34,6 @@ try {
|
||||
require_once(APPROOT.'/application/startup.inc.php');
|
||||
|
||||
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
|
||||
LoginWebPage::DoLoginEx();
|
||||
|
||||
IssueLog::Trace('----- Request: '.utils::GetRequestUri(), LogChannels::WEB_REQUEST);
|
||||
|
||||
@@ -45,6 +44,7 @@ try {
|
||||
|
||||
switch ($operation) {
|
||||
case 'download_document':
|
||||
LoginWebPage::DoLoginEx();
|
||||
$id = utils::ReadParam('id', '');
|
||||
$sField = utils::ReadParam('field', '');
|
||||
if ($sClass == 'Attachment') {
|
||||
@@ -64,6 +64,7 @@ try {
|
||||
break;
|
||||
|
||||
case 'download_inlineimage':
|
||||
LoginWebPage::DoLoginEx();
|
||||
$id = utils::ReadParam('id', '');
|
||||
$sSecret = utils::ReadParam('s', '');
|
||||
$iCacheSec = 31556926; // One year ahead: an inline image cannot change
|
||||
|
||||
@@ -173,10 +173,9 @@ try {
|
||||
case 'ShowModalSearchForeignKeys':
|
||||
$oPage->SetContentType('text/html');
|
||||
$iInputId = utils::ReadParam('iInputId', '');
|
||||
$sTitle = utils::ReadParam('sTitle', '', false, 'raw_data');
|
||||
$sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class');
|
||||
$oWidget = new UISearchFormForeignKeys($sTargetClass, $iInputId);
|
||||
$oWidget->ShowModalSearchForeignKeys($oPage, $sTitle);
|
||||
$oWidget->ShowModalSearchForeignKeys($oPage);
|
||||
break;
|
||||
|
||||
// ui.searchformforeignkeys
|
||||
@@ -187,16 +186,6 @@ try {
|
||||
$oWidget->GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter);
|
||||
break;
|
||||
|
||||
// ui.searchformforeignkeys
|
||||
case 'ListResultsSearchForeignKeys':
|
||||
$oPage->SetContentType('text/html');
|
||||
$sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class');
|
||||
$iInputId = utils::ReadParam('iInputId', '');
|
||||
$sRemoteClass = utils::ReadParam('sRemoteClass', '', false, 'class');
|
||||
$oWidget = new UISearchFormForeignKeys($sTargetClass, $iInputId);
|
||||
$oWidget->ListResultsSearchForeignKeys($oPage, $sRemoteClass);
|
||||
break;
|
||||
|
||||
// ui.linkswidget
|
||||
case 'addObjects':
|
||||
$oPage->SetContentType('text/html');
|
||||
@@ -2513,8 +2502,7 @@ EOF
|
||||
$oKPI->ComputeAndReport('Data fetch and format');
|
||||
$oPage->output();
|
||||
} catch (Exception $e) {
|
||||
// note: transform to cope with XSS attacks
|
||||
echo utils::EscapeHtml($e->GetMessage());
|
||||
echo utils::EscapeHtml(Dict::S('UI:PageTitle:FatalError'));
|
||||
IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString());
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ if (is_array($aModuleDelegatedAuthenticationEndpointsList) && !in_array($sPage,
|
||||
LoginWebPage::DoLoginEx();
|
||||
}
|
||||
if (is_null($aModuleDelegatedAuthenticationEndpointsList) && !UserRights::IsLoggedIn()) {
|
||||
require_once(APPROOT.'/application/startup.inc.php');
|
||||
// check if user is not logged in, if not log a warning in the log file as the page is executed without login, which is not recommended for security reason
|
||||
IssueLog::Debug("The '$sPage' page is executed without logging in. This call will be blocked in the future and will likely cause unwanted behaviour in the '$sModule' module. Please define a delegated authentication endpoint for the module, as described at https://www.itophub.io/wiki/page?id=latest:customization:new_extension#security.");
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class ModuleInstallation extends DBObject
|
||||
{
|
||||
$aParams =
|
||||
[
|
||||
"category" => "core,view_in_gui",
|
||||
"category" => "core,view_in_gui,grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
'name_attcode' => ['name', 'version'],
|
||||
"state_attcode" => "",
|
||||
|
||||
@@ -36,6 +36,8 @@ class SetupPage extends NiceWebPage
|
||||
{
|
||||
public const DEFAULT_PAGE_TEMPLATE_REL_PATH = 'pages/backoffice/setuppage/layout';
|
||||
|
||||
protected const BODY_DATA_GUI_TYPE = 'setup';
|
||||
|
||||
public function __construct($sTitle)
|
||||
{
|
||||
parent::__construct($sTitle);
|
||||
|
||||
@@ -340,8 +340,10 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory
|
||||
$aClassAliases = $oSet->GetFilter()->GetSelectedClasses();
|
||||
$aAuthorizedClasses = [];
|
||||
foreach ($aClassAliases as $sAlias => $sClassName) {
|
||||
if ((UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO) &&
|
||||
((count($aDisplayAliases) == 0) || (in_array($sAlias, $aDisplayAliases)))) {
|
||||
if (
|
||||
((UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) !== UR_ALLOWED_NO) || ($aExtraParams['display_unauthorized_objects'] ?? false) === true)
|
||||
&& ((count($aDisplayAliases) == 0) || (in_array($sAlias, $aDisplayAliases)))
|
||||
) {
|
||||
$aAuthorizedClasses[$sAlias] = $sClassName;
|
||||
}
|
||||
}
|
||||
@@ -520,6 +522,14 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory
|
||||
if ($aData['checked']) {
|
||||
if ($sAttCode == '_key_') {
|
||||
if ($bViewLink) {
|
||||
$sRenderLink = "return row['".$sClassAlias."/hyperlink'];";
|
||||
if (
|
||||
($aExtraParams['display_unauthorized_objects'] ?? false) === true
|
||||
&& UserRights::IsActionAllowed($sClassName, UR_ACTION_READ) !== UR_ALLOWED_YES
|
||||
) {
|
||||
$sRenderLink = "return row['".$sClassAlias."/friendlyname'];";
|
||||
}
|
||||
|
||||
$aColumnDefinition[] = [
|
||||
'description' => $aData['label'],
|
||||
'object_class' => $sClassName,
|
||||
@@ -527,7 +537,7 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory
|
||||
'attribute_code' => $sAttCode,
|
||||
'attribute_type' => '_key_',
|
||||
'attribute_label' => MetaModel::GetName($sClassName),
|
||||
'render' => "return row['".$sClassAlias."/hyperlink'];",
|
||||
'render' => $sRenderLink,
|
||||
];
|
||||
|
||||
}
|
||||
@@ -972,6 +982,8 @@ JS;
|
||||
/** Handler to call when trying to create a new object in modal */
|
||||
'creation_disallowed',
|
||||
/** Don't provide the standard object creation feature */
|
||||
'display_unauthorized_objects',
|
||||
/** bool Display objects for which the user has no read rights */
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,6 +233,7 @@ class AjaxPage extends WebPage implements iTabbedPage
|
||||
'aJsInlineLive' => $this->a_scripts,
|
||||
'aJsInlineOnDomReady' => $this->GetReadyScripts(),
|
||||
'aJsInlineOnInit' => $this->a_init_scripts,
|
||||
'sBodyDataGuiType' => static::BODY_DATA_GUI_TYPE,
|
||||
'bEscapeContent' => ($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'),
|
||||
// TODO 3.0.0: TEMP, used while developping, remove it.
|
||||
'sSanitizedContent' => utils::FilterXSS($this->s_content),
|
||||
|
||||
@@ -172,6 +172,7 @@ class UnauthenticatedWebPage extends NiceWebPage
|
||||
'aJsInlineLive' => $this->a_scripts,
|
||||
'aJsInlineOnDomReady' => $this->GetReadyScripts(),
|
||||
'aJsInlineOnInit' => $this->a_init_scripts,
|
||||
'sBodyDataGuiType' => static::BODY_DATA_GUI_TYPE,
|
||||
|
||||
// TODO 3.0.0: TEMP, used while developing, remove it.
|
||||
'sCapturedOutput' => utils::FilterXSS($s_captured_output),
|
||||
|
||||
@@ -152,6 +152,8 @@ class WebPage implements Page
|
||||
*/
|
||||
public const DEFAULT_PAGE_TEMPLATE_REL_PATH = 'pages/backoffice/webpage/layout';
|
||||
|
||||
protected const BODY_DATA_GUI_TYPE = 'backoffice';
|
||||
|
||||
protected $s_title;
|
||||
protected $s_content;
|
||||
protected $s_deferred_content;
|
||||
@@ -1702,6 +1704,7 @@ JS;
|
||||
'aJsInlineLive' => $this->a_scripts,
|
||||
'aJsInlineOnDomReady' => $this->GetReadyScripts(),
|
||||
'aJsInlineOnInit' => $this->a_init_scripts,
|
||||
'sBodyDataGuiType' => static::BODY_DATA_GUI_TYPE,
|
||||
|
||||
// TODO 3.0.0: TEMP, used while developing, remove it.
|
||||
'sCapturedOutput' => utils::FilterXSS($s_captured_output),
|
||||
|
||||
@@ -1003,6 +1003,7 @@ HTML;
|
||||
'aJsInlineOnInit' => $this->a_init_scripts,
|
||||
'aJsInlineOnDomReady' => $this->GetReadyScripts(),
|
||||
'aJsInlineLive' => $this->a_scripts,
|
||||
'sBodyDataGuiType' => static::BODY_DATA_GUI_TYPE,
|
||||
// TODO 3.0.0: TEMP, used while developping, remove it.
|
||||
'sSanitizedContent' => utils::FilterXSS($this->s_content),
|
||||
'sDeferredContent' => utils::FilterXSS($this->s_deferred_content),
|
||||
|
||||
@@ -42,6 +42,7 @@ use RunTimeEnvironment;
|
||||
use ScalarExpression;
|
||||
use SetupUtils;
|
||||
use UILinksWidget;
|
||||
use UserRights;
|
||||
use utils;
|
||||
use WizardHelper;
|
||||
|
||||
@@ -71,6 +72,12 @@ class AjaxRenderController
|
||||
$bShowObsoleteData = utils::ShowObsoleteData();
|
||||
}
|
||||
$oSet->SetShowObsoleteData($bShowObsoleteData);
|
||||
|
||||
// N°8606 : Check user permissions on the main class
|
||||
if (UserRights::IsActionAllowed($oSet->GetClass(), UR_ACTION_READ, $oSet) !== UR_ALLOWED_YES) {
|
||||
throw new Exception(Dict::Format('UI:Error:ReadNotAllowedOn_Class', $oSet->GetClass()));
|
||||
}
|
||||
|
||||
$aResult["draw"] = $iDrawNumber;
|
||||
$aResult["recordsTotal"] = $oSet->Count();
|
||||
$aResult["recordsFiltered"] = $aResult["recordsTotal"] ;
|
||||
@@ -95,6 +102,11 @@ class AjaxRenderController
|
||||
continue;
|
||||
}
|
||||
|
||||
// N°8606 : Check user permissions on the current class
|
||||
if (UserRights::IsActionAllowed($sClass, UR_ACTION_READ, $oSet) !== UR_ALLOWED_YES) {
|
||||
throw new Exception(Dict::Format('UI:Error:ReadNotAllowedOn_Class', $sClass));
|
||||
}
|
||||
|
||||
foreach ($aColumnsLoad[$sAlias] as $sAttCode) {
|
||||
$aObj[$sAlias."/".$sAttCode] = $aObject[$sAlias]->GetAsHTML($sAttCode);
|
||||
$bExcludeRawValue = false;
|
||||
|
||||
@@ -26,6 +26,7 @@ use CoreException;
|
||||
use DBObjectSearch;
|
||||
use DBObjectSet;
|
||||
use Dict;
|
||||
use EventNotificationNewsroom;
|
||||
use MetaModel;
|
||||
use SecurityException;
|
||||
use UserRights;
|
||||
@@ -358,6 +359,7 @@ JS
|
||||
// Search for all notifications for the current user
|
||||
$oSearch = DBObjectSearch::FromOQL('SELECT EventNotificationNewsroom');
|
||||
$oSearch->AddCondition('contact_id', UserRights::GetContactId(), '=');
|
||||
$oSearch->AllowAllData();
|
||||
$oSet = new DBObjectSet($oSearch, ['read' => true, 'date' => false], []);
|
||||
|
||||
// Add main content block
|
||||
@@ -526,6 +528,7 @@ JS
|
||||
|
||||
if (utils::IsNotNullOrEmptyString($iContactId)) {
|
||||
$oSearch = DBObjectSearch::FromOQL('SELECT EventNotificationNewsroom WHERE contact_id = :contact_id AND read = "no"');
|
||||
$oSearch->AllowAllData();
|
||||
$oSet = new DBObjectSet($oSearch, [], ['contact_id' => $iContactId]);
|
||||
|
||||
while ($oMessage = $oSet->Fetch()) {
|
||||
@@ -539,7 +542,7 @@ $sMessage
|
||||
HTML;
|
||||
|
||||
$sIcon = $oMessage->Get('icon') !== null ?
|
||||
$oMessage->Get('icon')->GetDisplayURL('EventNotificationNewsroom', $oMessage->GetKey(), 'icon') :
|
||||
$oMessage->Get('icon')->GetDisplayURL(EventNotificationNewsroom::class, $oMessage->GetKey(), 'icon') :
|
||||
Branding::GetCompactMainLogoAbsoluteUrl();
|
||||
$aMessages[] = [
|
||||
'id' => $oMessage->GetKey(),
|
||||
@@ -576,6 +579,7 @@ HTML;
|
||||
|
||||
if (utils::IsNotNullOrEmptyString($iContactId)) {
|
||||
$oSearch = DBObjectSearch::FromOQL('SELECT EventNotificationNewsroom WHERE contact_id = :contact_id AND read = "no"');
|
||||
$oSearch->AllowAllData();
|
||||
$oSet = new DBObjectSet($oSearch, [], ['contact_id' => $iContactId]);
|
||||
|
||||
while ($oEvent = $oSet->Fetch()) {
|
||||
@@ -605,7 +609,7 @@ HTML;
|
||||
$sEventId = utils::ReadParam('event_id', 0);
|
||||
if ($sEventId > 0) {
|
||||
try {
|
||||
$oEvent = MetaModel::GetObject('EventNotificationNewsroom', $sEventId);
|
||||
$oEvent = MetaModel::GetObject(EventNotificationNewsroom::class, $sEventId, true, true);
|
||||
if ($oEvent !== null && $oEvent->Get('contact_id') === UserRights::GetContactId()) {
|
||||
$oEvent->Set('read', 'yes');
|
||||
$oEvent->SetCurrentDate('read_date');
|
||||
|
||||
@@ -117,6 +117,7 @@ class NotificationsRepository
|
||||
protected function PrepareSearchForNotificationsByContact(int $iContactId, array $aNotificationIds = []): DBSearch
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL("SELECT EventNotificationNewsroom WHERE contact_id = :contact_id");
|
||||
$oSearch->AllowAllData();
|
||||
$aParams = [
|
||||
"contact_id" => $iContactId,
|
||||
];
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body data-gui-type="backoffice">
|
||||
<body data-gui-type="{{ aPage.sBodyDataGuiType|default('backoffice') }}">
|
||||
{% if aPage.isPrintable %}<div class="printable-content" style="width: 27.7cm;">{% endif %}
|
||||
{% block iboPageBodyHtml %}
|
||||
<div id="ibo-page-container">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[infra]
|
||||
; STS version : testing greatest PHP version possible
|
||||
php_version=8.3-apache
|
||||
php_version=8.4-apache
|
||||
; N°6629 perf bug on some tests on mariadb for now, so specifying MySQL
|
||||
db_version=latest-mariadb
|
||||
|
||||
|
||||
@@ -834,6 +834,11 @@ HTML,
|
||||
'good element_identifier' => [utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER, 'AD05nb', 'AD05nb'],
|
||||
'bad element_identifier' => [utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER, 'AD05nb+', 'AD05nb'],
|
||||
'array' => [utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER, ['AD05nb+','apply_modify'], ['AD05nb','apply_modify']],
|
||||
'good module code' => [utils::ENUM_SANITIZATION_FILTER_MODULE_CODE, 'some-module-code', 'some-module-code'],
|
||||
'good module code with capitalized letters' => [utils::ENUM_SANITIZATION_FILTER_MODULE_CODE, 'SOME-module-code', 'SOME-module-code'],
|
||||
'good module code with dot' => [utils::ENUM_SANITIZATION_FILTER_MODULE_CODE, 'some-module-code-for-3.2-version', 'some-module-code-for-3.2-version'],
|
||||
'bad module code with underscores' => [utils::ENUM_SANITIZATION_FILTER_MODULE_CODE, 'some_module_code', null],
|
||||
'bad module code with slashes' => [utils::ENUM_SANITIZATION_FILTER_MODULE_CODE, 'some-module/code', null],
|
||||
'good url' => [utils::ENUM_SANITIZATION_FILTER_URL, 'https://www.w3schools.com', 'https://www.w3schools.com'],
|
||||
'bad url' => [utils::ENUM_SANITIZATION_FILTER_URL, 'https//www.w3schools.com', null],
|
||||
'url with injection' => [utils::ENUM_SANITIZATION_FILTER_URL, 'https://demo.combodo.com/simple/pages/UI.php?operation=full_text&text=<img zzz src=x onerror=alert(1) //>', 'https://demo.combodo.com/simple/pages/UI.php?operation=full_text&text=<imgzzzsrc=xonerror=alert(1)//>'],
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Core;
|
||||
|
||||
use AttributeDateTime;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use DateTime;
|
||||
use MetaModel;
|
||||
|
||||
class AttributeSubItemTest extends ItopDataTestCase
|
||||
{
|
||||
public const CREATE_TEST_ORG = true;
|
||||
|
||||
/**
|
||||
* @param string $sAttCode
|
||||
* @param string $sVerb
|
||||
* @param string $sExpectedValue
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testGetForTemplate()
|
||||
{
|
||||
$aUserRequestCustomParams = [
|
||||
'title' => "Test DisplayStopwatch",
|
||||
];
|
||||
$oUserRequest = $this->CreateUserRequest(456, $aUserRequestCustomParams);
|
||||
|
||||
$iStartDate = time() - 200;
|
||||
$oStopwatch = $oUserRequest->Get('ttr');
|
||||
$oStopwatch->DefineThreshold(100, $iStartDate);
|
||||
$oUserRequest->Set('ttr', $oStopwatch);
|
||||
|
||||
$sValue = $oUserRequest->Get('ttr_escalation_deadline');
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oUserRequest), 'ttr_escalation_deadline');
|
||||
|
||||
self::assertEquals('Missed by 3 min', $oAttDef->GetForTemplate($sValue, 'html', $oUserRequest));
|
||||
$oDateTime = new DateTime();
|
||||
$oDateTime->setTimestamp($iStartDate);
|
||||
$sDate = $oDateTime->format(AttributeDateTime::GetFormat());
|
||||
self::assertEquals($sDate, $oAttDef->GetForTemplate($sValue, 'label', $oUserRequest), 'label() should render the date in the format specified in the configuration file, in parameter "date_and_time_format"');
|
||||
self::assertEquals('Missed by 3 min', $oAttDef->GetForTemplate($sValue, 'text', $oUserRequest), 'text() should render the deadline as specified in the configuration file, in parameter "deadline_format", and depending on the user language');
|
||||
self::assertEquals($iStartDate, $oAttDef->GetForTemplate($sValue, '', $oUserRequest));
|
||||
}
|
||||
}
|
||||
@@ -7,14 +7,36 @@
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Core;
|
||||
|
||||
use Combodo\iTop\Application\WebPage\CaptureWebPage;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use ormDocument;
|
||||
use UserRights;
|
||||
|
||||
/**
|
||||
* Tests of the ormDocument class
|
||||
*/
|
||||
class ormDocumentTest extends ItopDataTestCase
|
||||
{
|
||||
private const RESTRICTED_PROFILE = 'Configuration Manager';
|
||||
private int $iUserOrg;
|
||||
private int $iOrgDifferentFromUser;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->iUserOrg = $this->GivenObjectInDB('Organization', [
|
||||
'name' => 'UserOrg',
|
||||
]);
|
||||
|
||||
$this->iOrgDifferentFromUser = $this->GivenObjectInDB('Organization', [
|
||||
'name' => 'OrgDifferentFromUser',
|
||||
]);
|
||||
|
||||
$this->LoginRestrictedUser($this->iUserOrg, self::RESTRICTED_PROFILE);
|
||||
$this->ResetMetaModelQueyCacheGetObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@@ -139,4 +161,107 @@ class ormDocumentTest extends ItopDataTestCase
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that DownloadDocument enforces rights for documents
|
||||
*
|
||||
* @dataProvider DownloadDocumentRightsProvider
|
||||
*/
|
||||
public function testDownloadDocumentDifferentOrg(string $sTargetClass, string $sAttCode, string $sData, string $sFileName, ?string $sHostClass)
|
||||
{
|
||||
$iDeniedDocumentId = $this->CreateDownloadTargetInOrg($sTargetClass, $sAttCode, $this->iOrgDifferentFromUser, $sData, $sFileName, $sHostClass);
|
||||
|
||||
$oPageDenied = new CaptureWebPage();
|
||||
ormDocument::DownloadDocument($oPageDenied, $sTargetClass, $iDeniedDocumentId, $sAttCode);
|
||||
$sDeniedHtml = (string) $oPageDenied->GetHtml();
|
||||
$this->assertStringContainsString(
|
||||
'the object does not exist or you are not allowed to view it',
|
||||
$sDeniedHtml,
|
||||
'Expected error message when rights are missing.'
|
||||
);
|
||||
$this->assertStringNotContainsString($sData, $sDeniedHtml, 'Unexpected file data present when rights are missing.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that DownloadDocument allows to retrieve document with the same org (or host object org)
|
||||
*
|
||||
* @dataProvider DownloadDocumentRightsProvider
|
||||
*/
|
||||
public function testDownloadDocumentSameOrg(string $sTargetClass, string $sAttCode, string $sData, string $sFileName, ?string $sHostClass)
|
||||
{
|
||||
$iAllowedDocumentId = $this->CreateDownloadTargetInOrg($sTargetClass, $sAttCode, $this->iUserOrg, $sData, $sFileName, $sHostClass);
|
||||
|
||||
$oPageAllowed = new CaptureWebPage();
|
||||
ormDocument::DownloadDocument($oPageAllowed, $sTargetClass, $iAllowedDocumentId, $sAttCode);
|
||||
$sAllowedHtml = (string) $oPageAllowed->GetHtml();
|
||||
$this->assertStringContainsString($sData, $sAllowedHtml, 'Expected file data present when rights are sufficient.');
|
||||
$this->assertStringNotContainsString('the object does not exist or you are not allowed to view it', $sAllowedHtml, 'Unexpected error message when rights are sufficient.');
|
||||
}
|
||||
|
||||
public function DownloadDocumentRightsProvider(): array
|
||||
{
|
||||
return [
|
||||
'DocumentFile' => [
|
||||
'class' => 'DocumentFile',
|
||||
'data_attribute_id' => 'file',
|
||||
'data' => 'document_data',
|
||||
'file_name' => 'document.txt',
|
||||
'host_class' => null],
|
||||
'Attachment' => [
|
||||
'class' => 'Attachment',
|
||||
'data_attribute_id' => 'contents',
|
||||
'data' => 'attachment_data',
|
||||
'file_name' => 'attachment.txt',
|
||||
'host_class' => 'UserRequest'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to avoid duplicating object creation in tests
|
||||
* Created objects and host objects depending on the Document class
|
||||
* @param string $sTargetClass
|
||||
* @param string $sAttCode
|
||||
* @param int $iOrgId
|
||||
* @param string $sData
|
||||
* @param string $sFileName
|
||||
* @param string|null $sHostClass
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function CreateDownloadTargetInOrg(string $sTargetClass, string $sAttCode, int $iOrgId, string $sData, string $sFileName, ?string $sHostClass): int
|
||||
{
|
||||
|
||||
if ($sTargetClass === 'DocumentFile') {
|
||||
return $this->GivenObjectInDB($sTargetClass, [
|
||||
'name' => 'UnitTestDocFile_'.uniqid(),
|
||||
'org_id' => $iOrgId,
|
||||
$sAttCode => new ormDocument($sData, 'text/plain', $sFileName),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($sTargetClass === 'Attachment') {
|
||||
$iHostId = $this->GivenObjectInDB($sHostClass, [
|
||||
'title' => 'UnitTestUserRequest_'.uniqid(),
|
||||
'org_id' => $iOrgId,
|
||||
'description' => 'A user request for testing attachment download rights',
|
||||
]);
|
||||
|
||||
return $this->GivenObjectInDB('Attachment', [
|
||||
'item_class' => $sHostClass,
|
||||
'item_id' => $iHostId,
|
||||
$sAttCode => new ormDocument($sData, 'text/plain', $sFileName),
|
||||
]);
|
||||
}
|
||||
|
||||
throw new \Exception("Unsupported target class: $sTargetClass");
|
||||
}
|
||||
|
||||
private function LoginRestrictedUser(int $iAllowedOrgId, string $sProfileName): void
|
||||
{
|
||||
if (UserRights::IsLoggedIn()) {
|
||||
UserRights::Logoff();
|
||||
}
|
||||
$sLogin = $this->GivenUserRestrictedToAnOrganizationInDB($iAllowedOrgId, self::$aURP_Profiles[$sProfileName]);
|
||||
UserRights::Login($sLogin);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user