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

This commit is contained in:
Benjamin Dalsass
2025-01-13 15:01:55 +01:00
19 changed files with 136 additions and 25 deletions

View File

@@ -249,9 +249,9 @@ class appUserPreferences extends DBObject
(
"category" => "gui",
"key_type" => "autoincrement",
"name_attcode" => "userid",
"name_attcode" => "login",
"state_attcode" => "",
"reconc_keys" => array("userid"),
"reconc_keys" => array("userid","login"),
"db_table" => "priv_app_preferences",
"db_key_field" => "id",
"db_finalclass_field" => "",
@@ -260,8 +260,10 @@ class appUserPreferences extends DBObject
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributePropertySet("preferences", array("allowed_values"=>null, "sql"=>"preferences", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_SetZListItems('list', array('preferences',));
MetaModel::Init_SetZListItems('default_search', array('userid'));
MetaModel::Init_AddAttribute(new AttributeExternalField("org_id", array("allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "org_id")));
MetaModel::Init_AddAttribute(new AttributeExternalField("login", array("allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "login")));
MetaModel::Init_SetZListItems('list', array('org_id','preferences'));
MetaModel::Init_SetZListItems('default_search', array('userid','login','org_id'));
}
/**

View File

@@ -63,6 +63,8 @@ class UserProfileBrick extends PortalBrick
parent::RegisterTemplates($oTemplatesRegister);
$oTemplatesRegister->RegisterTemplates(self::class,
TemplateDefinitionDto::Create('page', static::TEMPLATES_BASE_PATH . 'user-profile/layout.html.twig'),
TemplateDefinitionDto::Create('user_info', static::TEMPLATES_BASE_PATH.'user-profile/user_info.html.twig'),
TemplateDefinitionDto::Create('user_info_ready_js', static::TEMPLATES_BASE_PATH.'user-profile/user_info.ready.js.twig'),
);
}

View File

@@ -45,6 +45,10 @@ abstract class AbstractController extends SymfonyAbstractController implements T
$oTemplatesRegister->RegisterTemplates(self::class,
TemplateDefinitionDto::Create('page', static::TEMPLATES_BASE_PATH . 'layout.html.twig'),
TemplateDefinitionDto::Create('modal', static::TEMPLATES_BASE_PATH . 'modal/layout.html.twig'),
TemplateDefinitionDto::Create('loader', static::TEMPLATES_BASE_PATH.'helpers/loader.html.twig'),
TemplateDefinitionDto::Create('tagset_clic_handler_js', static::TEMPLATES_BASE_PATH.'helpers/tagset_clic_handler.js.twig'),
TemplateDefinitionDto::Create('session_message', static::TEMPLATES_BASE_PATH.'helpers/session_messages/session_message.html.twig'),
TemplateDefinitionDto::Create('session_messages', static::TEMPLATES_BASE_PATH.'helpers/session_messages/session_messages.html.twig'),
);
}

View File

@@ -84,7 +84,13 @@ class ObjectController extends BrickController
TemplateDefinitionDto::Create('page', static::TEMPLATES_BASE_PATH. 'bricks/object/layout.html.twig'),
TemplateDefinitionDto::Create('modal', static::TEMPLATES_BASE_PATH. 'bricks/object/modal.html.twig'),
TemplateDefinitionDto::Create('mode_create', static::TEMPLATES_BASE_PATH.'bricks/object/mode_create.html.twig', true, 'create'),
TemplateDefinitionDto::Create('mode_loader', static::TEMPLATES_BASE_PATH.'modal/mode_loader.html.twig', true, 'loader'),
TemplateDefinitionDto::Create('mode_edit', static::TEMPLATES_BASE_PATH.'bricks/object/mode_edit.html.twig', true, 'edit'),
TemplateDefinitionDto::Create('mode_search_hierarchy', static::TEMPLATES_BASE_PATH.'bricks/object/mode_search_hierarchy.html.twig', true, 'search_hierarchy'),
TemplateDefinitionDto::Create('mode_search_regular', static::TEMPLATES_BASE_PATH.'bricks/object/mode_search_regular.html.twig', true, 'search_regular'),
TemplateDefinitionDto::Create('mode_view', static::TEMPLATES_BASE_PATH.'bricks/object/mode_view.html.twig', true, 'view'),
TemplateDefinitionDto::Create('mode_apply_stimulus', static::TEMPLATES_BASE_PATH.'bricks/object/mode_apply_stimulus.html.twig', true, 'apply_stimulus'),
TemplateDefinitionDto::Create('mode_loader', static::TEMPLATES_BASE_PATH.'modal/mode_loader.html.twig'),
TemplateDefinitionDto::Create('plugins_buttons', static::TEMPLATES_BASE_PATH.'bricks/object/plugins_buttons.html.twig'),
);
}

View File

@@ -100,9 +100,9 @@ class ApplicationHelper
try
{
// Allowed profiles
if ($oBrick->GetAllowedProfilesOql() !== null && $oBrick->GetAllowedProfilesOql() !== '')
if (utils::IsNotNullOrEmptyString($oBrick->GetAllowedProfilesOql()))
{
$oSearch = DBObjectSearch::FromOQL($oBrick->GetAllowedProfilesOql());
$oSearch = DBObjectSearch::FromOQL_AllData($oBrick->GetAllowedProfilesOql());
$oSet = new DBObjectSet($oSearch);
while ($oProfile = $oSet->Fetch())
{
@@ -111,9 +111,9 @@ class ApplicationHelper
}
// Denied profiles
if ($oBrick->GetDeniedProfilesOql() !== null && $oBrick->GetDeniedProfilesOql() !== '')
if (utils::IsNotNullOrEmptyString($oBrick->GetDeniedProfilesOql()))
{
$oSearch = DBObjectSearch::FromOQL($oBrick->GetDeniedProfilesOql());
$oSearch = DBObjectSearch::FromOQL_AllData($oBrick->GetDeniedProfilesOql());
$oSet = new DBObjectSet($oSearch);
while ($oProfile = $oSet->Fetch())
{

View File

@@ -180,6 +180,7 @@ class TemplatesProviderService
*
* @return string|null
* @throws \ReflectionException
* @throws \Exception
*/
public function GetTemplatePath(string $sProviderClass, string $sTemplateId, bool $bIsInitial = false): ?string
{

View File

@@ -4,7 +4,7 @@
{% block pPageReadyScripts %}
{{ parent() }}
{% include 'itop-portal-base/portal/templates/helpers/tagset_clic_handler.js.twig' %}
{% include template('tagset_clic_handler_js') %}
{% endblock %}

View File

@@ -19,7 +19,7 @@
<div id="brick_mosaic_overlay">
{% block bBrowseMosaicOverlay %}
<div class="overlay_content">
{% include 'itop-portal-base/portal/templates/helpers/loader.html.twig' %}
{% include template('loader') %}
</div>
{% endblock %}
</div>

View File

@@ -34,7 +34,7 @@
</div>
<div id="brick_tree_overlay">
<div class="overlay_content">
{% include 'itop-portal-base/portal/templates/helpers/loader.html.twig' %}
{% include template('loader') %}
</div>
</div>
{% endblock %}

View File

@@ -18,5 +18,5 @@
{% block pPageReadyScripts %}
{{ parent() }}
{% include 'itop-portal-base/portal/templates/helpers/tagset_clic_handler.js.twig' %}
{% include template('tagset_clic_handler_js') %}
{% endblock %}

View File

@@ -30,7 +30,7 @@
{# Misc. buttons #}
{% if form.buttons is defined and (form.buttons.actions is defined or form.buttons.links is defined) %}
<div class="form_btn_misc">
{% include 'itop-portal-base/portal/templates/bricks/object/plugins_buttons.html.twig' with {'aButtons': form.buttons} %}
{% include template('plugins_buttons', 'Combodo\\iTop\\Portal\\Controller\\ObjectController') with {'aButtons': form.buttons} %}
</div>
{% endif %}
{# Transition buttons #}

View File

@@ -6,7 +6,7 @@
{# Misc. buttons #}
{% if form.buttons is defined and (form.buttons.actions is defined or form.buttons.links is defined) %}
<div class="form_btn_misc">
{% include 'itop-portal-base/portal/templates/bricks/object/plugins_buttons.html.twig' with {'aButtons': form.buttons} %}
{% include template('plugins_buttons', 'Combodo\\iTop\\Portal\\Controller\\ObjectController') with {'aButtons': form.buttons} %}
</div>
{% endif %}

View File

@@ -55,7 +55,7 @@
{% set oContactForm = forms.contact %}
{% set oPreferencesForm = forms.preferences %}
{% set oPasswordForm = forms.password %}
{% include 'itop-portal-base/portal/templates/bricks/user-profile/user_info.html.twig' %}
{% include template('user_info', 'Combodo\\iTop\\Portal\\Brick\\UserProfileBrick') %}
{% else %}
<div id="user-profile-wrapper">
<div class="row">
@@ -80,7 +80,7 @@
{% set oContactForm = forms.contact %}
{% set oPreferencesForm = forms.preferences %}
{% set oPasswordForm = forms.password %}
{% include 'itop-portal-base/portal/templates/bricks/user-profile/user_info.ready.js.twig' %}
{% include template('user_info_ready_js', 'Combodo\\iTop\\Portal\\Brick\\UserProfileBrick') %}
{% endif %}
{% endblock %}

View File

@@ -1,5 +1,5 @@
<div id="session-messages">
{% for aSessionMessage in app['session_message_helper'] %}
{% include 'itop-portal-base/portal/templates/helpers/session_messages/session_message.html.twig' with {aSessionMessage: aSessionMessage} %}
{% include template('session_message') with {aSessionMessage: aSessionMessage} %}
{% endfor %}
</div>

View File

@@ -11,7 +11,7 @@
<div class="col-xs-12 col-sm-9 col-md-10 col-sm-offset-3 col-md-offset-2">
<section class="row">
<div class="col-xs-12">
{% include 'itop-portal-base/portal/templates/helpers/session_messages/session_messages.html.twig' %}
{% include template('session_messages') %}
</div>
</section>
<section class="row tiles_wrapper">

View File

@@ -335,7 +335,7 @@
<div class="col-xs-12 col-sm-9 col-md-10 col-sm-offset-3 col-md-offset-2">
<section class="row">
<div class="col-xs-12">
{% include 'itop-portal-base/portal/templates/helpers/session_messages/session_messages.html.twig' %}
{% include template('session_messages') %}
</div>
</section>
<section class="row" id="main-header">
@@ -367,7 +367,7 @@
<div class="modal fade" id="modal-for-all" role="dialog" tabindex="-1">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
{% include 'itop-portal-base/portal/templates/helpers/loader.html.twig' %}
{% include template('loader') %}
</div>
</div>
</div>
@@ -395,7 +395,7 @@
{% block pPageOverlay %}
<div id="page_overlay" class="global_overlay">
<div class="overlay_content">
{% include 'itop-portal-base/portal/templates/helpers/loader.html.twig' %}
{% include template('loader') %}
</div>
</div>
{% endblock %}

View File

@@ -3,7 +3,7 @@
{% extends 'itop-portal-base/portal/templates/modal/layout.html.twig' %}
{% block pModalContent %}
{% include 'itop-portal-base/portal/templates/helpers/loader.html.twig' %}
{% include template('loader') %}
{% if redirection is defined and redirection.url is defined %}
<script type="text/javascript">

View File

@@ -0,0 +1,96 @@
<?php
namespace Combodo\iTop\Test\UnitTest\Module\iTopPortalBase;
/**
* Copyright (C) 2010-2024 Combodo SAS
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http: *www.gnu.org/licenses/>
*
*/
use Combodo\iTop\Portal\Brick\AbstractBrick;
use Combodo\iTop\Portal\Helper\ApplicationHelper;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
/**
* @covers \Combodo\iTop\Portal\Helper\RequestManipulatorHelper
*/
class ApplicationHelperTest extends ItopDataTestCase
{
const PASSWORD = "aBCDEFG@123456789";
protected function LoadRequiredItopFiles(): void
{
parent::LoadRequiredItopFiles();
$this->RequireOnceItopFile('datamodels/2.x/itop-portal-base/portal/src/Helper/ApplicationHelper.php');
}
public static function LoadBrickSecurityProvider()
{
return [
'can access admin profile' => [
'associated_profile' => 'Administrator',
],
'cannot access admin profile' => [
'associated_profile' => 'Portal user',
],
];
}
/**
* @dataProvider LoadBrickSecurityProvider
*/
public function testLoadBrickSecurity_GetAllowedProfilesOql(string $sAssociatedProfileName)
{
$oBrick = $this->createMock(AbstractBrick::class);
$oBrick->expects($this->any())
->method('GetAllowedProfilesOql')
->willReturn("SELECT URP_Profiles WHERE name IN ('Administrator')");
$oBrick->expects($this->exactly(1))
->method('AddAllowedProfile')
->willReturn("Administrator");
$_SESSION = [];
$oUser = $this->CreateContactlessUser("$sAssociatedProfileName-" . uniqid(), self::$aURP_Profiles[$sAssociatedProfileName], self::PASSWORD);
\UserRights::Login($oUser->Get('login'));
$this->InvokeNonPublicStaticMethod(ApplicationHelper::class, 'LoadBrickSecurity', [$oBrick]);
}
/**
* @dataProvider LoadBrickSecurityProvider
*/
public function testLoadBrickSecurity_GetDeniedProfilesOql(string $sAssociatedProfileName)
{
$oBrick = $this->createMock(AbstractBrick::class);
$oBrick->expects($this->any())
->method('GetDeniedProfilesOql')
->willReturn("SELECT URP_Profiles WHERE name IN ('Administrator')");
$oBrick->expects($this->exactly(1))
->method('AddDeniedProfile')
->willReturn("Administrator");
$_SESSION = [];
$oUser = $this->CreateContactlessUser("$sAssociatedProfileName-" . uniqid(), self::$aURP_Profiles[$sAssociatedProfileName], self::PASSWORD);
\UserRights::Login($oUser->Get('login'));
$this->InvokeNonPublicStaticMethod(ApplicationHelper::class, 'LoadBrickSecurity', [$oBrick]);
}
}

View File

@@ -22,6 +22,7 @@
namespace Combodo\iTop\Test\UnitTest\Module\iTopPortalBase;
use Combodo\iTop\Portal\Helper\RequestManipulatorHelper;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
use Combodo\iTop\Test\UnitTest\ItopTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
@@ -29,13 +30,12 @@ use Symfony\Component\HttpFoundation\RequestStack;
/**
* @covers \Combodo\iTop\Portal\Helper\RequestManipulatorHelper
*/
class RequestManipulatorTest extends ItopTestCase
class RequestManipulatorTest extends ItopDataTestCase
{
protected function LoadRequiredItopFiles(): void
{
parent::LoadRequiredItopFiles();
$this->RequireOnceItopFile('datamodels/2.x/itop-portal-base/portal/src/Helper/RequestManipulatorHelper.php');
}
public function testReadParam()