Merge remote-tracking branch 'origin/support/3.2' into develop

This commit is contained in:
Pierre Goiffon
2024-03-07 11:12:39 +01:00
11 changed files with 148 additions and 17 deletions

View File

@@ -549,10 +549,9 @@ class CMDBSource
/**
* @param string $sSQLQuery
*
* @return \mysqli_result|null
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \CoreException
* @return mysqli_result|null
* @throws MySQLException
* @throws MySQLHasGoneAwayException
*
* @since 2.7.0 N°679 handles nested transactions
*/

View File

@@ -53,7 +53,7 @@ abstract class Trigger extends cmdbAbstractObject
MetaModel::Init_AddAttribute(new AttributeEnumSet("context", array("allowed_values" => null, "possible_values" => new ValueSetEnumPadded($aTags, true), "sql" => "context", "depends_on" => array(), "is_null_allowed" => true, "max_items" => 12)));
// "complement" is a computed field, fed by Trigger sub-classes, in general in ComputeValues method, for eg. the TriggerOnObject fed it with target_class info
MetaModel::Init_AddAttribute(new AttributeString("complement", array("allowed_values" => null, "sql" => "complement", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("subscription_policy", array("allowed_values" => new ValueSetEnum('allow_no_channel,force_at_least_one_channel,force_all_channels'), "sql" => "subscription_policy", "default_value" => 'allow_no_channel', "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("subscription_policy", array("allowed_values" => new ValueSetEnum(Combodo\iTop\Core\Trigger\Enum\SubscriptionPolicy::cases()), "sql" => "subscription_policy", "default_value" => \Combodo\iTop\Core\Trigger\Enum\SubscriptionPolicy::AllowNoChannel->value, "is_null_allowed" => false, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('finalclass', 'description', 'context', 'subscription_policy', 'action_list', 'complement')); // Attributes to be displayed for the complete details

View File

@@ -476,6 +476,7 @@ class ValueSetEnum extends ValueSetDefinition
* @param bool $bLocalizedSort
*
* @since 3.1.0 N°1646 Add $bLocalizedSort parameter
* @since 3.2.0 N°7157 $Values can be an array of backed-enum cases
*/
public function __construct($Values, bool $bSortByValues = false)
{
@@ -523,13 +524,21 @@ class ValueSetEnum extends ValueSetDefinition
*/
protected function LoadValues($aArgs)
{
$aValues = [];
if (is_array($this->m_values))
{
$aValues = $this->m_values;
foreach ($this->m_values as $value) {
// Handle backed-enum case
if (is_object($value) && enum_exists(get_class($value))) {
$aValues[] = $value->value;
continue;
}
$aValues[] = $value;
}
}
elseif (is_string($this->m_values) && strlen($this->m_values) > 0)
{
$aValues = array();
foreach (explode(",", $this->m_values) as $sVal)
{
$sVal = trim($sVal);
@@ -539,7 +548,7 @@ class ValueSetEnum extends ValueSetDefinition
}
else
{
$aValues = array();
$aValues = [];
}
$this->m_aValues = $aValues;
return true;

View File

@@ -418,6 +418,7 @@ return array(
'Combodo\\iTop\\Core\\Kpi\\KpiLogData' => $baseDir . '/sources/Core/Kpi/KpiLogData.php',
'Combodo\\iTop\\Core\\MetaModel\\FriendlyNameType' => $baseDir . '/sources/Core/MetaModel/FriendlyNameType.php',
'Combodo\\iTop\\Core\\MetaModel\\HierarchicalKey' => $baseDir . '/sources/Core/MetaModel/HierarchicalKey.php',
'Combodo\\iTop\\Core\\Trigger\\Enum\\SubscriptionPolicy' => $baseDir . '/sources/Core/Trigger/Enum/SubscriptionPolicy.php',
'Combodo\\iTop\\DesignDocument' => $baseDir . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\DesignElement' => $baseDir . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\Form\\Field\\AbstractSimpleField' => $baseDir . '/sources/Form/Field/AbstractSimpleField.php',

View File

@@ -793,6 +793,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Core\\Kpi\\KpiLogData' => __DIR__ . '/../..' . '/sources/Core/Kpi/KpiLogData.php',
'Combodo\\iTop\\Core\\MetaModel\\FriendlyNameType' => __DIR__ . '/../..' . '/sources/Core/MetaModel/FriendlyNameType.php',
'Combodo\\iTop\\Core\\MetaModel\\HierarchicalKey' => __DIR__ . '/../..' . '/sources/Core/MetaModel/HierarchicalKey.php',
'Combodo\\iTop\\Core\\Trigger\\Enum\\SubscriptionPolicy' => __DIR__ . '/../..' . '/sources/Core/Trigger/Enum/SubscriptionPolicy.php',
'Combodo\\iTop\\DesignDocument' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\DesignElement' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\Form\\Field\\AbstractSimpleField' => __DIR__ . '/../..' . '/sources/Form/Field/AbstractSimpleField.php',

View File

@@ -624,7 +624,7 @@ class WebPage implements Page
break;
case static::ENUM_RESOURCE_TYPE_CSS:
$this->a_linked_stylesheets[$sFileAbsURI] = $sFileAbsURI;
$this->a_linked_stylesheets[$sFileAbsURI] = ['link' => $sFileAbsURI, 'condition' => ''];
break;
}
}

View File

@@ -11,6 +11,7 @@ use Combodo\iTop\Application\UI\Base\Component\Input\Set\SetUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Panel\Panel;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
use Combodo\iTop\Application\WebPage\iTopWebPage;
use Combodo\iTop\Core\Trigger\Enum\SubscriptionPolicy;
use Combodo\iTop\Renderer\BlockRenderer;
use Combodo\iTop\Service\Notification\NotificationsRepository;
use Combodo\iTop\Service\Router\Router;
@@ -82,7 +83,7 @@ class NotificationsCenterController extends Controller
// Add the action notification to the list of actions notifications for the trigger
$oActionsNotificationsByTrigger[$iTriggerId][] = $oSubscribedActionNotification;
// Add the subscribed status to the list of subscribed actions notifications for the trigger
$aSubscribedActionsNotificationsByTrigger[$iTriggerId][$oSubscribedActionNotification->GetKey()] = $oLnkActionsNotifications->Get('subscribed') || $oTrigger->Get('subscription_policy') === 'force_all_channels';
$aSubscribedActionsNotificationsByTrigger[$iTriggerId][$oSubscribedActionNotification->GetKey()] = $oLnkActionsNotifications->Get('subscribed') || $oTrigger->Get('subscription_policy') === SubscriptionPolicy::ForceAllChannels->value;
}
// Build table rows
@@ -155,7 +156,7 @@ class NotificationsCenterController extends Controller
$oChannelSet->SetOptionsTemplate('application/object/set/option_renderer.html.twig');
$oChannelSet->SetItemsTemplate('application/preferences/notification-center/item_renderer.html.twig');
// Disable the input set if the subscription policy is 'force_all_channels'
if($sTriggerSubscriptionPolicy === 'force_all_channels'){
if ($sTriggerSubscriptionPolicy === SubscriptionPolicy::ForceAllChannels->value) {
$oChannelSet->SetIsDisabled(true);
}
// Add a CSRF Token
@@ -190,8 +191,8 @@ $.ajax({
});
JS
);
// Set the minimum number of channels to 1 if the subscription policy is 'force_at_least_one_channel'
if($sTriggerSubscriptionPolicy === 'force_at_least_one_channel')
// Set the minimum number of channels to 1 if the subscription policy is {@see SubscriptionPolicy::ForceAtLeastOneChannel}
if($sTriggerSubscriptionPolicy === SubscriptionPolicy::ForceAtLeastOneChannel->value)
{
$oChannelSet->SetMinItems(1);
}
@@ -289,7 +290,7 @@ JS
// Add the action notification to the list of actions notifications for the trigger
$oActionsNotificationsByTrigger[$iTriggerId][] = $oSubscribedActionNotification;
// Add the subscribed status to the list of subscribed actions notifications for the trigger
$aSubscribedActionsNotificationsByTrigger[$iTriggerId][$oSubscribedActionNotification->GetKey()] = $oLnkActionsNotifications->Get('subscribed') || $oTrigger->Get('subscription_policy') === 'force_all_channels';
$aSubscribedActionsNotificationsByTrigger[$iTriggerId][$oSubscribedActionNotification->GetKey()] = $oLnkActionsNotifications->Get('subscribed') || $oTrigger->Get('subscription_policy') === SubscriptionPolicy::ForceAllChannels->value;
}
$oPage->AddTabContainer('NotificationsCenter', '', $oNotificationsPanel);
@@ -491,7 +492,7 @@ JS
throw new \Exception('Invalid trigger');
}
// Check the trigger subscription policy
if($oTrigger->Get('subscription_policy') === 'force_all_channels'){
if ($oTrigger->Get('subscription_policy') === SubscriptionPolicy::ForceAllChannels->value) {
throw new \Exception('You are not allowed to unsubscribe from this channel');
}
@@ -501,7 +502,7 @@ JS
throw new \Exception('You are not subscribed to any channel');
}
// Check the trigger subscription policy and if we are subscribed to at least 1 channel if necessary
if($oTrigger->Get('subscription_policy') === 'force_at_least_one_channel') {
if($oTrigger->Get('subscription_policy') === SubscriptionPolicy::ForceAtLeastOneChannel->value) {
$oTotalSubscribedActionsNotificationsSet = NotificationsRepository::GetInstance()->SearchSubscriptionByTriggerContactAndSubscription($iTriggerId, \UserRights::GetContactId(), '1');
if (($oTotalSubscribedActionsNotificationsSet->Count() - $oSubscribedActionsNotificationsSet->Count()) === 0) {
throw new \Exception('You can\'t unsubscribe from this channel, you must be subscribed to at least one channel');

View File

@@ -0,0 +1,16 @@
<?php
namespace Combodo\iTop\Core\Trigger\Enum;
/**
* Class SubscriptionPolicy
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Core\Trigger\Enum
* @since 3.2.0
*/
enum SubscriptionPolicy: string {
case AllowNoChannel = "allow_no_channel";
case ForceAtLeastOneChannel = "force_at_least_one_channel";
case ForceAllChannels = "force_all_channels";
}

View File

@@ -3,6 +3,7 @@ namespace Combodo\iTop\Service\Notification;
use ActionNotification;
use Combodo\iTop\Core\Trigger\Enum\SubscriptionPolicy;
use Contact;
use lnkActionNotificationToContact;
use Trigger;
@@ -99,7 +100,7 @@ class NotificationsService {
public function IsSubscribed(Trigger $oTrigger, ActionNotification $oActionNotification, Contact $oRecipient): bool
{
// Check if the trigger subscription policy is 'force_all_channels'
if ($oTrigger->Get('subscription_policy') === 'force_all_channels') {
if ($oTrigger->Get('subscription_policy') === SubscriptionPolicy::ForceAllChannels->value) {
return true;
}
// Check if the user is already subscribed to the action notification

View File

@@ -0,0 +1,13 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Test\UnitTest\Core\ValueSetEnum;
enum ABCEnum: string {
case A = "a";
case B = "b";
case C = "c";
}

View File

@@ -0,0 +1,90 @@
<?php
/*
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Test\UnitTest\Core;
use Combodo\iTop\Test\UnitTest\Core\ValueSetEnum\ABCEnum;
use Combodo\iTop\Test\UnitTest\ItopTestCase;
use ValueSetEnum;
/**
* Class ValueSetEnumTest
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Test\UnitTest\Core
* @coves \ValueSetEnum
*/
class ValueSetEnumTest extends ItopTestCase
{
public static function setupBeforeClass(): void
{
require_once __DIR__ . "/ValueSetEnum/ABCEnum.php";
}
/**
* @dataProvider LoadValuesProvider
*
* @param mixed $input
* @param array $aExpectedValues
* @param bool $bIsInputBackedEnum
*
* @return void
*/
public function testLoadValues(mixed $input, array $aExpectedValues, bool $bIsInputBackedEnum = false): void
{
if ($bIsInputBackedEnum) {
$input = $input::cases();
}
$oValueSetEnum = new ValueSetEnum($input);
$aTestedValues = $oValueSetEnum->GetValues([]);
$this->assertEquals($aExpectedValues, $aTestedValues, "Values should be the same and ordered the same way");
}
public function LoadValuesProvider(): array
{
return [
"CSV list, trimmed values, already ordered" => [
"a,b,c",
[
"a" => "a",
"b" => "b",
"c" => "c",
],
],
"CSV list, values to trim, already ordered" => [
"a, b ,c ",
[
"a" => "a",
"b" => "b",
"c" => "c",
],
],
"Array, already ordered" => [
["a", "b", "c"],
[
0 => "a",
1 => "b",
2 => "c",
],
],
"Backed-Enum" => [
ABCEnum::class,
[
0 => "a",
1 => "b",
2 => "c",
],
true, // Is the input value a backed enum?
],
"Invalid int value" => [
123,
[],
]
];
}
}