Compare commits

...

25 Commits

Author SHA1 Message Date
Benjamin Dalsass
74e87ed8e3 N°6218 - 1:n & n:n - Read mode: No refresh of tab count on Add/Remove in pop-up 2023-11-14 08:59:46 +01:00
Pierre Goiffon
f5a0b4bf8e Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-11-13 16:27:34 +01:00
Pierre Goiffon
a2b9583379 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-11-13 16:25:53 +01:00
Pierre Goiffon
77409eed99 🎨 DBObject small phpdoc fixes 2023-11-13 16:25:37 +01:00
Molkobain
eb7c971091 N°6948 - Upgrade "symfony/twig-bridge" to v5.4.31 2023-11-13 15:17:52 +01:00
Eric Espie
4d5a704d9a N°6062 - Fix unit tests 2023-11-13 15:14:29 +01:00
Eric Espie
b758113752 N°6436 - Integrate Performance Audit pre requisite (log events for extensions) 2023-11-13 15:02:54 +01:00
Stephen Abello
5465287089 Merge branch 'support/3.0' into support/3.1 2023-11-13 11:21:21 +01:00
Stephen Abello
09be84f69d N°6908 - Security hardening 2023-11-13 11:19:02 +01:00
Romain Quetiez
3b987f97eb Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-11-10 16:15:08 +01:00
Romain Quetiez
d9bdcfeae3 N°6658 - Fix regression: do not reset current user's profile cache 2023-11-10 15:57:08 +01:00
Pierre Goiffon
8df7c22464 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-11-10 15:21:10 +01:00
Pierre Goiffon
d725ba3d84 N°6765 Avoid behat scenario loading issues on portal modal (#569)
- New CombodoJsActivity API
- Replace existing calls in NiceWebPage (ready scripts)
- Add calls in ready block in portal object create template (used in both create and edit)
2023-11-10 15:10:37 +01:00
Molkobain
2d8ecd465b N°6903 - Fix crash when emptying file attribute (eg. picture of a contact) 2023-11-09 18:17:35 +01:00
Molkobain
34ba4fa0ce N°6917 - Security hardening 2023-11-09 16:43:37 +01:00
Molkobain
08d22219f4 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-11-08 14:59:56 +01:00
Molkobain
8a3d81c430 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-11-08 14:59:06 +01:00
Anne-Catherine
83a70daf68 N°6887 - Fix excessive OQL requests to display user's grant matrix (#564)
* N°6887 - Fix excessive OQL requests to display user's grant matrix

* N°6887 - Rename variable and add PHPDoc

---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-11-08 14:57:28 +01:00
Molkobain
d5a8a3bb09 N°4756 - Fix PHPDoc 2023-11-07 12:00:19 +01:00
jf-cbd
eaeb114754 N°6852 - Use "email_default_sender_address" when "forgot_password_from" config param is empty 2023-11-06 10:47:40 +01:00
Molkobain
cfd32581b7 N°6861 - Add PHPDoc and return type hint 2023-11-03 09:20:15 +01:00
vdumas
85a6bd0a05 Typo in EN Dictionary - Allowed orgs class name 2023-11-02 17:43:48 +01:00
Stephen Abello
92cd1e3f19 N°6861 - Display warning message when creating/editing a mandatory blob in modal depending on edition mode and blob value 2023-11-02 14:58:46 +01:00
Stephen Abello
b54022e2ad N°6385 - Move Linkset flags and edit_when logic from UIBlock to DisplayBareRelations 2023-11-02 10:16:35 +01:00
odain-cbd
eaa80c5396 N°6824 - Notification with current_contact placeholder trigger hundred of email sent (#562)
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
Co-authored-by: Thomas Casteleyn <thomas.casteleyn@super-visions.com>
Co-authored-by: Romain Quetiez <romain.quetiez@combodo.com>
2023-10-31 16:17:57 +01:00
75 changed files with 990 additions and 429 deletions

View File

@@ -446,6 +446,12 @@ class UserRightsProfile extends UserRightsAddOnAPI
UR_ACTION_BULK_DELETE => 'bd',
);
/**
* @var array $aUsersProfilesList Cache of users' profiles. Hash array of user ID => [profile ID => profile friendlyname, profile ID => profile friendlyname, ...]
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6887
*/
private $aUsersProfilesList = [];
// Installation: create the very first user
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
{
@@ -758,8 +764,12 @@ class UserRightsProfile extends UserRightsAddOnAPI
$sAction = self::$m_aActionCodes[$iActionCode];
$bStatus = null;
// Cache user's profiles
if(false === array_key_exists($iUser, $this->aUsersProfilesList)){
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
}
// Call the API of UserRights because it caches the list for us
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile)
foreach($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
{
$bGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction);
if (!is_null($bGrant))
@@ -885,11 +895,16 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Note: this code is VERY close to the code of IsActionAllowed()
$iUser = $oUser->GetKey();
// Cache user's profiles
if(false === array_key_exists($iUser, $this->aUsersProfilesList)){
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
}
// Note: The object set is ignored because it was interesting to optimize for huge data sets
// and acceptable to consider only the root class of the object set
$bStatus = null;
// Call the API of UserRights because it caches the list for us
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile)
foreach($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
{
$bGrant = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode);
if (!is_null($bGrant))

View File

@@ -335,7 +335,6 @@ abstract class AbstractPreferencesExtension implements iPreferencesExtension
* A recommended pattern is to cache data by the mean of static members.
*
* @api
* @deprecated 3.1.0 N°4756 use the new event service instead, see {@see DBObject::FireEvent()} method
* @package UIExtensibilityAPI
*/
interface iApplicationUIExtension
@@ -487,7 +486,6 @@ interface iApplicationUIExtension
* @api
* @package UIExtensibilityAPI
* @since 2.7.0
* @deprecated
*/
abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
{
@@ -560,6 +558,7 @@ abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
* or through the GUI.
*
* @api
* @deprecated 3.1.0 N°4756 use the new event service instead, see {@see DBObject::FireEvent()} method. More details on each method PHPDoc.
* @package ORMExtensibilityAPI
*/
interface iApplicationObjectExtension
@@ -574,6 +573,7 @@ interface iApplicationObjectExtension
* Otherwise, the answer is definitively "yes, the object has changed".
*
* @api
* @deprecated 3.1.0 N°4756 No alternative available, this API was unstable and is abandoned
* @param \cmdbAbstractObject $oObject The target object
*
* @return boolean True if something has changed for the target object
@@ -587,6 +587,7 @@ interface iApplicationObjectExtension
* Anyhow, this API can be called in other contexts such as the CSV import tool.
*
* @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_CHECK_TO_WRITE event instead
* @param \cmdbAbstractObject $oObject The target object
*
* @return string[] A list of errors message. An error message is made of one line and it can be displayed to the end-user.
@@ -601,6 +602,7 @@ interface iApplicationObjectExtension
* Please not that it is not possible to cascade deletion by this mean: only stopper issues can be handled.
*
* @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_CHECK_TO_DELETE event instead
* @param \cmdbAbstractObject $oObject The target object
*
* @return string[] A list of errors message. An error message is made of one line and it can be displayed to the end-user.
@@ -617,6 +619,7 @@ interface iApplicationObjectExtension
* * {@see DBObject::Get()} : for a given attribute the new value that was persisted
*
* @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_AFTER_WRITE event instead
* @param \cmdbAbstractObject $oObject The target object
* @param CMDBChange|null $oChange A change context. Since 2.0 it is fine to ignore it, as the framework does maintain this information
* once for all the changes made within the current page
@@ -633,6 +636,7 @@ interface iApplicationObjectExtension
* The method is called right <b>after</b> the object has been written to the database.
*
* @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_AFTER_WRITE event instead
* @param \cmdbAbstractObject $oObject The target object
* @param CMDBChange|null $oChange A change context. Since 2.0 it is fine to ignore it, as the framework does maintain this information
* once for all the changes made within the current page
@@ -647,6 +651,7 @@ interface iApplicationObjectExtension
* The method is called right <b>before</b> the object will be deleted from the database.
*
* @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_AFTER_DELETE event instead
* @param \cmdbAbstractObject $oObject The target object
* @param CMDBChange|null $oChange A change context. Since 2.0 it is fine to ignore it, as the framework does maintain this information
* once for all the changes made within the current page
@@ -660,6 +665,7 @@ interface iApplicationObjectExtension
* Extend this class instead of iApplicationObjectExtension if you don't need to overload all methods
*
* @api
* @deprecated 3.1.0 N°4756 use the new event service instead, see {@see DBObject::FireEvent()} method
* @package ORMExtensibilityAPI
* @since 2.7.0
*/

View File

@@ -746,7 +746,9 @@ HTML
$aArgs = array('this' => $this);
$sEditWhen = $oAttDef->GetEditWhen();
$bIsEditableBasedOnEditWhen = ($sEditWhen === LINKSET_EDITWHEN_ALWAYS || $sEditWhen === LINKSET_EDITWHEN_ON_HOST_EDITION);
// Calculate if edit_when allows to edit based on current $bEditMode
$bIsEditableBasedOnEditWhen = ($sEditWhen === LINKSET_EDITWHEN_ALWAYS) ||
($bEditMode ? $sEditWhen === LINKSET_EDITWHEN_ON_HOST_EDITION : $sEditWhen === LINKSET_EDITWHEN_ON_HOST_DISPLAY);
$bReadOnly = ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE)) || !$bIsEditableBasedOnEditWhen;
if ($bEditMode && (!$bReadOnly)) {
@@ -758,9 +760,9 @@ HTML
$oPage->add($sHTMLValue);
} else {
if ($oAttDef->IsIndirect()) {
$oBlockLinkSetViewTable = new BlockIndirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef);
$oBlockLinkSetViewTable = new BlockIndirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef, $bReadOnly);
} else {
$oBlockLinkSetViewTable = new BlockDirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef);
$oBlockLinkSetViewTable = new BlockDirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef, $bReadOnly);
}
$oPage->AddUiBlock($oBlockLinkSetViewTable);
}

View File

@@ -248,6 +248,7 @@ class LoginWebPage extends NiceWebPage
$oEmail = new Email();
$oEmail->SetRecipientTO($sTo);
$sFrom = MetaModel::GetConfig()->Get('forgot_password_from');
$sFrom = utils::IsNullOrEmptyString($sFrom) ? MetaModel::GetConfig()->Get('email_default_sender_address') : $sFrom;
$oEmail->SetRecipientFrom($sFrom);
$oEmail->SetSubject(Dict::S('UI:ResetPwd-EmailSubject', $oUser->Get('login')));
$sResetUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?loginop=reset_pwd&auth_user='.urlencode($oUser->Get('login')).'&token='.urlencode($sToken);

View File

@@ -976,7 +976,7 @@ HTML
FormHelper::DisableAttributeBlobInputs($this->sTargetClass, $aFormExtraParams);
if(FormHelper::HasMandatoryAttributeBlobInputs($oNewObj)){
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal());
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal(FormHelper::ENUM_MANDATORY_BLOB_MODE_CREATE));
}
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), $aFormExtraParams);

View File

@@ -145,7 +145,7 @@ JS
FormHelper::DisableAttributeBlobInputs($sRealClass, $aFormExtraParams);
if(FormHelper::HasMandatoryAttributeBlobInputs($oObj)){
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal());
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal(FormHelper::ENUM_MANDATORY_BLOB_MODE_CREATE));
}
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, array(), $aFormExtraParams);

22
composer.lock generated
View File

@@ -4476,16 +4476,16 @@
},
{
"name": "symfony/twig-bridge",
"version": "v5.4.11",
"version": "v5.4.31",
"source": {
"type": "git",
"url": "https://github.com/symfony/twig-bridge.git",
"reference": "63b8a50d48c9fe3d04e77307d4f1771dd848baa8"
"reference": "fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/63b8a50d48c9fe3d04e77307d4f1771dd848baa8",
"reference": "63b8a50d48c9fe3d04e77307d4f1771dd848baa8",
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942",
"reference": "fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942",
"shasum": ""
},
"require": {
@@ -4498,22 +4498,22 @@
"phpdocumentor/reflection-docblock": "<3.2.2",
"phpdocumentor/type-resolver": "<1.4.0",
"symfony/console": "<5.3",
"symfony/form": "<5.3",
"symfony/form": "<5.4.21|>=6,<6.2.7",
"symfony/http-foundation": "<5.3",
"symfony/http-kernel": "<4.4",
"symfony/translation": "<5.2",
"symfony/workflow": "<5.2"
},
"require-dev": {
"doctrine/annotations": "^1.12",
"egulias/email-validator": "^2.1.10|^3",
"doctrine/annotations": "^1.12|^2",
"egulias/email-validator": "^2.1.10|^3|^4",
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
"symfony/asset": "^4.4|^5.0|^6.0",
"symfony/console": "^5.3|^6.0",
"symfony/dependency-injection": "^4.4|^5.0|^6.0",
"symfony/expression-language": "^4.4|^5.0|^6.0",
"symfony/finder": "^4.4|^5.0|^6.0",
"symfony/form": "^5.3|^6.0",
"symfony/form": "^5.4.21|^6.2.7",
"symfony/http-foundation": "^5.3|^6.0",
"symfony/http-kernel": "^4.4|^5.0|^6.0",
"symfony/intl": "^4.4|^5.0|^6.0",
@@ -4577,7 +4577,7 @@
"description": "Provides integration for Twig with various Symfony components",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/twig-bridge/tree/v5.4.11"
"source": "https://github.com/symfony/twig-bridge/tree/v5.4.31"
},
"funding": [
{
@@ -4593,7 +4593,7 @@
"type": "tidelift"
}
],
"time": "2022-07-20T13:00:38+00:00"
"time": "2023-11-09T21:19:08+00:00"
},
{
"name": "symfony/twig-bundle",
@@ -5276,5 +5276,5 @@
"platform-overrides": {
"php": "7.4.0"
},
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.6.0"
}

View File

@@ -8599,7 +8599,7 @@ class AttributeBlob extends AttributeDefinition
public function RecordAttChange(DBObject $oObject, $original, $value): void
{
// N°6502 Don't record history if only the download count has changed
if ($original->EqualsExceptDownloadsCount($value)) {
if ((null !== $original) && (null !== $value) && $original->EqualsExceptDownloadsCount($value)) {
return;
}

View File

@@ -9,6 +9,7 @@ use Combodo\iTop\Service\Events\EventData;
use Combodo\iTop\Service\Events\EventException;
use Combodo\iTop\Service\Events\EventService;
use Combodo\iTop\Service\Events\EventServiceLog;
use Combodo\iTop\Service\Module\ModuleService;
use Combodo\iTop\Service\TemporaryObjects\TemporaryObjectManager;
/**
@@ -2410,7 +2411,6 @@ abstract class DBObject implements iDisplay
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \OQLException
*
*/
public function DoCheckToWrite()
{
@@ -2462,7 +2462,6 @@ abstract class DBObject implements iDisplay
}
/**
*
* @api
* @api-advanced
*
@@ -2478,9 +2477,8 @@ abstract class DBObject implements iDisplay
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \OQLException
*
*/
public final function CheckToWrite($bDoComputeValues = true)
final public function CheckToWrite($bDoComputeValues = true)
{
if (MetaModel::SkipCheckToWrite())
{
@@ -6285,7 +6283,10 @@ abstract class DBObject implements iDisplay
}
}
finally {
$oKPI->ComputeStats('FireEvent', $sEvent);
if (!$oKPI->ComputeStatsForExtension($this, $sCallback, "Event: $sEvent")) {
$sSignature = ModuleService::GetInstance()->GetModuleMethodSignature($this, $sCallback);
$oKPI->ComputeStats('FireEvent', "$sEvent callback: $sSignature");
}
}
}
if (!is_null($oFirstException)) {

View File

@@ -3,6 +3,7 @@
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Core\Kpi\KpiLogData;
use Combodo\iTop\Service\Module\ModuleService;
@@ -404,16 +405,31 @@ class ExecutionKPI
$this->ResetCounters();
}
public function ComputeStatsForExtension($object, $sMethod)
/**
* Compute statistics for a call to an extension
* Note: not working in dev mode (with links to env-production)
*
* @param object|string $object object called
* @param string $sMethod method called on the object
* @param string $sMessage additional message
*
* @return bool true if an extension was found for this object::method
* @throws \ReflectionException
*/
public function ComputeStatsForExtension($object, string $sMethod, string $sMessage = ''): bool
{
if (!self::IsEnabled()) {
return;
return true;
}
$sSignature = ModuleService::GetInstance()->GetModuleMethodSignature($object, $sMethod);
if (utils::StartsWith($sSignature, '[')) {
$this->ComputeStats('Extension', $sSignature);
$this->ComputeStats('Extension', "$sSignature $sMessage");
return true;
}
return false;
}
public function ComputeStats($sOperation, $sArguments)

View File

@@ -1241,7 +1241,7 @@ abstract class MetaModel
}
$sTable = self::DBGetTable($sClass);
// Could be completed later with all the classes that are using a given table
// Could be completed later with all the classes that are using a given table
if (!array_key_exists($sTable, $aTables)) {
$aTables[$sTable] = array();
}
@@ -3522,7 +3522,7 @@ abstract class MetaModel
}
// Set the "host class" as soon as possible, since HierarchicalKeys use it for their 'target class' as well
// and this needs to be know early (for Init_IsKnowClass 19 lines below)
// and this needs to be know early (for Init_IsKnowClass 19 lines below)
$oAtt->SetHostClass($sTargetClass);
// Some attributes could refer to a class
@@ -3564,7 +3564,7 @@ abstract class MetaModel
self::$m_aAttribDefs[$sTargetClass][$oAtt->GetCode()] = $oAtt;
self::$m_aAttribOrigins[$sTargetClass][$oAtt->GetCode()] = $sTargetClass;
// Note: it looks redundant to put targetclass there, but a mix occurs when inheritance is used
// Note: it looks redundant to put targetclass there, but a mix occurs when inheritance is used
}
/**
@@ -3764,7 +3764,7 @@ abstract class MetaModel
self::$m_aStimuli[$sTargetClass][$oStimulus->GetCode()] = $oStimulus;
// I wanted to simplify the syntax of the declaration of objects in the biz model
// Therefore, the reference to the host class is set there
// Therefore, the reference to the host class is set there
$oStimulus->SetHostClass($sTargetClass);
}
@@ -4219,40 +4219,77 @@ abstract class MetaModel
}
else
{
$aCurrentUser = array();
$aCurrentContact = array();
$aCurrentUser = [];
$aCurrentContact = [];
foreach ($aExpectedArgs as $expression)
{
$aName = explode('->', $expression->GetName());
if ($aName[0] == 'current_contact_id') {
$aPlaceholders['current_contact_id'] = UserRights::GetContactId();
}
if ($aName[0] == 'current_user') {
} else if ($aName[0] == 'current_user') {
array_push($aCurrentUser, $aName[1]);
}
if ($aName[0] == 'current_contact') {
} else if ($aName[0] == 'current_contact') {
array_push($aCurrentContact, $aName[1]);
}
}
if (count($aCurrentUser) > 0) {
$oUser = UserRights::GetUserObject();
$aPlaceholders['current_user->object()'] = $oUser;
foreach ($aCurrentUser as $sField) {
$aPlaceholders['current_user->'.$sField] = $oUser->Get($sField);
}
static::FillObjectPlaceholders($aPlaceholders, 'current_user', UserRights::GetUserObject(), $aCurrentUser);
}
if (count($aCurrentContact) > 0) {
$oPerson = UserRights::GetContactObject();
$aPlaceholders['current_contact->object()'] = $oPerson;
foreach ($aCurrentContact as $sField) {
$aPlaceholders['current_contact->'.$sField] = $oPerson->Get($sField);
}
static::FillObjectPlaceholders($aPlaceholders, 'current_contact', UserRights::GetContactObject(), $aCurrentContact);
}
}
return $aPlaceholders;
}
/**
* @since 3.1.1 N°6824
* @param array $aPlaceholders
* @param string $sPlaceHolderPrefix
* @param ?\DBObject $oObject
* @param array $aCurrentUser
*
* @return void
*
*/
private static function FillObjectPlaceholders(array &$aPlaceholders, string $sPlaceHolderPrefix, ?\DBObject $oObject, array $aCurrentUser) : void {
$sPlaceHolderKey = $sPlaceHolderPrefix."->object()";
if (is_null($oObject)){
$aContext = [
"current_user_id" => UserRights::GetUserId(),
"null object type" => $sPlaceHolderPrefix,
"fields" => $aCurrentUser,
];
IssueLog::Warning("Unresolved placeholders due to null object in current context", null,
$aContext);
$aPlaceholders[$sPlaceHolderKey] = \Dict::Format("Core:Placeholder:CannotBeResolved", $sPlaceHolderKey);
foreach ($aCurrentUser as $sField) {
$sPlaceHolderKey = $sPlaceHolderPrefix . "->$sField";
$aPlaceholders[$sPlaceHolderKey] = \Dict::Format("Core:Placeholder:CannotBeResolved", $sPlaceHolderKey);
}
} else {
$aPlaceholders[$sPlaceHolderKey] = $oObject;
foreach ($aCurrentUser as $sField) {
$sPlaceHolderKey = $sPlaceHolderPrefix . "->$sField";
if (false === MetaModel::IsValidAttCode(get_class($oObject), $sField)){
$aContext = [
"current_user_id" => UserRights::GetUserId(),
"obj_class" => get_class($oObject),
"placeholder" => $sPlaceHolderKey,
"invalid_field" => $sField,
];
IssueLog::Warning("Unresolved placeholder due to invalid attribute", null,
$aContext);
$aPlaceholders[$sPlaceHolderKey] = \Dict::Format("Core:Placeholder:CannotBeResolved", $sPlaceHolderKey);
continue;
}
$aPlaceholders[$sPlaceHolderKey] = $oObject->Get($sField);
}
}
}
/**
* @param \DBSearch $oFilter
*
@@ -6479,7 +6516,7 @@ abstract class MetaModel
$aCache['m_aExtensionClassNames'] = self::$m_aExtensionClassNames;
$aCache['m_Category2Class'] = self::$m_Category2Class;
$aCache['m_aRootClasses'] = self::$m_aRootClasses; // array of "classname" => "rootclass"
$aCache['m_aParentClasses'] = self::$m_aParentClasses; // array of ("classname" => array of "parentclass")
$aCache['m_aParentClasses'] = self::$m_aParentClasses; // array of ("classname" => array of "parentclass")
$aCache['m_aChildClasses'] = self::$m_aChildClasses; // array of ("classname" => array of "childclass")
$aCache['m_aClassParams'] = self::$m_aClassParams; // array of ("classname" => array of class information)
$aCache['m_aAttribDefs'] = self::$m_aAttribDefs; // array of ("classname" => array of attributes)

View File

@@ -856,6 +856,8 @@ class UserRights
}
/**
* Set the current user (as part of the login process)
*
* @param string $sLogin Login of the concerned user
* @param string $sAuthentication
*
@@ -865,8 +867,6 @@ class UserRights
*/
public static function Login($sLogin, $sAuthentication = 'any')
{
static::Logoff();
$oUser = self::FindUser($sLogin, $sAuthentication);
if (is_null($oUser))
{
@@ -885,6 +885,8 @@ class UserRights
}
/**
* Reset current user and cleanup associated SESSION data
*
* @return void
* @since 3.0.4 3.1.1 3.2.0
*/

View File

@@ -67,6 +67,8 @@
<script type="text/javascript">
$(document).ready(function(){
CombodoJsActivity.AddOngoingScript();
// Form field set declaration
var oFieldSet_{{ sFormIdSanitized }} = $('#{{ sFormId }} > .form_fields').field_set({{ form.fieldset|json_encode()|raw }});
// Form handler declaration
@@ -149,5 +151,7 @@
$('#{{ sFormId }}').closest('.modal').scrollTop(0);
$('#{{ sFormId }}').closest('.modal').find('.modal-footer').hide();
{% endif %}
CombodoJsActivity.RemoveOngoingScript();
});
</script>

View File

@@ -49,6 +49,7 @@ Dict::Add('EN US', 'English', 'English', array(
'Core:AttributeTagSet' => 'List of tags',
'Core:AttributeTagSet+' => '',
'Core:AttributeSet:placeholder' => 'click to add',
'Core:Placeholder:CannotBeResolved' => '(%1$s : cannot be resolved)',
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromClass' => '%1$s (%2$s)',
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromOneChildClass' => '%1$s (%2$s from %3$s)',
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromSeveralChildClasses' => '%1$s (%2$s from child classes)',

View File

@@ -248,7 +248,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:URP_UserOrg' => 'User organizations',
'Class:URP_UserOrg+' => 'Allowed organizations',
'Class:URP_UserOrg/Name' => 'LinkGG between %1$s and %2$s',
'Class:URP_UserOrg/Name' => 'Link between %1$s and %2$s',
'Class:URP_UserOrg/Attribute:userid' => 'User',
'Class:URP_UserOrg/Attribute:userid+' => 'user account',
'Class:URP_UserOrg/Attribute:userlogin' => 'Login',

View File

@@ -39,6 +39,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Core:AttributeTagSet' => 'Liste d\'étiquettes',
'Core:AttributeTagSet+' => '',
'Core:AttributeSet:placeholder' => 'cliquer pour ajouter',
'Core:Placeholder:CannotBeResolved' => '(%1$s : non remplacé)',
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromClass' => '%1$s (%2$s)',
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromOneChildClass' => '%1$s (%2$s de la classe %3$s)',
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromSeveralChildClasses' => '%1$s (%2$s d\'une sous-classe)',

View File

@@ -18,5 +18,7 @@
*/
Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which is not supported in modal mode. The creation/modification of this object may be incomplete and may be completed in a full-page form.~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
));

View File

@@ -18,5 +18,7 @@
*/
Dict::Add('DA DA', 'Danish', 'Dansk', array(
'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which is not supported in modal mode. The creation/modification of this object may be incomplete and may be completed in a full-page form.~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
));

View File

@@ -18,5 +18,7 @@
*/
Dict::Add('DE DE', 'German', 'Deutsch', array(
'UI:Object:Modal:Title' => 'Ein Objekt erstellen',
'UI:Object:Modal:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which is not supported in modal mode. The creation/modification of this object may be incomplete and may be completed in a full-page form.~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
));

View File

@@ -19,5 +19,7 @@
Dict::Add('EN US', 'English', 'English', array(
'UI:Object:Modal:Title' => 'Create an object',
'UI:Object:Modal:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which is not supported in modal mode. The creation/modification of this object may be incomplete and may be completed in a full-page form.',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.',
));

View File

@@ -18,5 +18,7 @@
*/
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which is not supported in modal mode. The creation/modification of this object may be incomplete and may be completed in a full-page form.~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
));

View File

@@ -18,5 +18,7 @@
*/
Dict::Add('FR FR', 'French', 'Français', array(
'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:MandatoryAttributeBlobInputs:Warning:Text' => 'Ce formulaire contient un attribut fichier obligatoire qui n\'est pas supporté en mode pop-up. La création/modification de cet objet risque d\'être incomplète et pourra être complété dans un formulaire en pleine page.',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'Ce formulaire contient un attribut fichier obligatoire qui ne peut pas être renseigné en mode pop-up. La création de cet objet sera incomplète et pourra être complétée dans un formulaire en pleine page.',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'Ce formulaire contient un attribut fichier obligatoire qui ne peut pas être renseigné en mode pop-up. Cet objet est incomplet, il peut être complété dans un formulaire en pleine page.',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'Ce formulaire contient un attribut fichier obligatoire qui ne peut pas être modifié en mode pop-up.',
));

View File

@@ -18,5 +18,7 @@
*/
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which is not supported in modal mode. The creation/modification of this object may be incomplete and may be completed in a full-page form.~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
));

View File

@@ -18,5 +18,7 @@
*/
Dict::Add('IT IT', 'Italian', 'Italiano', array(
'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which is not supported in modal mode. The creation/modification of this object may be incomplete and may be completed in a full-page form.~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
));

View File

@@ -18,5 +18,7 @@
*/
Dict::Add('JA JP', 'Japanese', '日本語', array(
'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which is not supported in modal mode. The creation/modification of this object may be incomplete and may be completed in a full-page form.~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
));

View File

@@ -18,5 +18,7 @@
*/
Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which is not supported in modal mode. The creation/modification of this object may be incomplete and may be completed in a full-page form.~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
));

View File

@@ -18,5 +18,7 @@
*/
Dict::Add('PL PL', 'Polish', 'Polski', array(
'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which is not supported in modal mode. The creation/modification of this object may be incomplete and may be completed in a full-page form.~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
));

View File

@@ -18,5 +18,7 @@
*/
Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which is not supported in modal mode. The creation/modification of this object may be incomplete and may be completed in a full-page form.~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
));

View File

@@ -18,5 +18,7 @@
*/
Dict::Add('RU RU', 'Russian', 'Русский', array(
'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which is not supported in modal mode. The creation/modification of this object may be incomplete and may be completed in a full-page form.~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
));

View File

@@ -18,5 +18,7 @@
*/
Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which is not supported in modal mode. The creation/modification of this object may be incomplete and may be completed in a full-page form.~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
));

View File

@@ -18,5 +18,7 @@
*/
Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which is not supported in modal mode. The creation/modification of this object may be incomplete and may be completed in a full-page form.~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
));

View File

@@ -18,5 +18,7 @@
*/
Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which is not supported in modal mode. The creation/modification of this object may be incomplete and may be completed in a full-page form.~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
));

View File

@@ -73,15 +73,19 @@ $(function () {
this.element.block();
$.post(this.options.sRenderUrl, oParams, function(data) {
let oTableElement = $('#'+me.options.sListId);
let oDataTable = oTableElement.DataTable();
// Nasty workaround to clear the pager's state for paginated lists !!!
// See jquery.tablesorter.pager.js / saveParams / restoreParams
if (window.pager_params) {
window.pager_params['pager'+me.options.sListId] = undefined;
}
var parentElt = $('#'+me.options.sListId).closest('.dataTables_wrapper').parent();
var aOptions = $('#'+me.options.sListId).DataTable().context[0].oInit;
var parentElt = oTableElement.closest('.dataTables_wrapper').parent();
var aOptions = oDataTable.context[0].oInit;
window['bSelectAllowed'+me.options.sListId] = false;
$('#'+me.options.sListId).DataTable().destroy(true);
oDataTable.destroy(true);
var sThead = "";
if (me.options.sSelectMode != "") {
sThead += "<th></th>";
@@ -127,7 +131,20 @@ $(function () {
aOptions["lengthMenu"] = [[oParams.end, oParams.end * 2, oParams.end * 3, oParams.end * 4, -1], [oParams.end, oParams.end * 2, oParams.end * 3, oParams.end * 4, aOptions["lengthMenu"]]];
aOptions["ajax"] = eval(aOptions["ajax"]);
$('#'+me.options.sListId).DataTable(aOptions);
// Rebuild table
oTableElement = $('#'+me.options.sListId);
oDataTable = oTableElement.DataTable(aOptions);
// Update tab information (when data received)
oDataTable.on('xhr', ( e, settings, json, xhr) => {
const sPanelTitle = oParams['extra_params']['panel_title']; // retrieve panel title
const oTabsPanel = oTableElement.closest('.ui-tabs-panel'); // search for tabs panel
if(oTabsPanel !== undefined){
const sLabelledBy = oTabsPanel.attr('aria-labelledby'); // key to search for corresponding tab
const oTab = $(`.ibo-tab-container .ui-tabs-tab[aria-labelledby="${sLabelledBy}"] a`); // tab
oTab.html(json.recordsTotal > 0 ? sPanelTitle + ` (${json.recordsTotal})` : sPanelTitle); // update tab information
}
});
me.element.unblock();

View File

@@ -1137,6 +1137,34 @@ const CombodoJSConsole = {
}
}
/**
* Helper to reflect ongoing JS activity to other processes like BeHat
* @api
* @since 3.0.4 3.1.1 3.2.0 N°6765
*/
const CombodoJsActivity = {
BODY_DATA_ATTR_NAME_READY: "data-ready-scripts",
/**
* Counter so that we set the flag as done only on the last call
* @type number
*/
iOngoingScriptsCount: 0,
AddOngoingScript: function() {
this.iOngoingScriptsCount++;
$("body").attr(this.BODY_DATA_ATTR_NAME_READY, "start");
},
RemoveOngoingScript: function() {
this.iOngoingScriptsCount--;
if (this.iOngoingScriptsCount < 1) {
$("body").attr(this.BODY_DATA_ATTR_NAME_READY, "done");
}
}
}
/**
* Helper to Sanitize string
*

View File

@@ -2,6 +2,24 @@
// autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, $err);
} elseif (!headers_sent()) {
echo $err;
}
}
trigger_error(
$err,
E_USER_ERROR
);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f::getLoader();

View File

@@ -42,35 +42,37 @@ namespace Composer\Autoload;
*/
class ClassLoader
{
/** @var ?string */
/** @var \Closure(string):void */
private static $includeFile;
/** @var string|null */
private $vendorDir;
// PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
* @var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
* @var array<string, list<string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
* @var list<string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
* List of PSR-0 prefixes
*
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
*
* @var array<string, array<string, list<string>>>
*/
private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
* @var list<string>
*/
private $fallbackDirsPsr0 = array();
@@ -78,8 +80,7 @@ class ClassLoader
private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
* @var array<string, string>
*/
private $classMap = array();
@@ -87,29 +88,29 @@ class ClassLoader
private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
* @var array<string, bool>
*/
private $missingClasses = array();
/** @var ?string */
/** @var string|null */
private $apcuPrefix;
/**
* @var self[]
* @var array<string, self>
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
* @param string|null $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
self::initializeIncludeClosure();
}
/**
* @return string[]
* @return array<string, list<string>>
*/
public function getPrefixes()
{
@@ -121,8 +122,7 @@ class ClassLoader
}
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
* @return array<string, list<string>>
*/
public function getPrefixesPsr4()
{
@@ -130,8 +130,7 @@ class ClassLoader
}
/**
* @return array[]
* @psalm-return array<string, string>
* @return list<string>
*/
public function getFallbackDirs()
{
@@ -139,8 +138,7 @@ class ClassLoader
}
/**
* @return array[]
* @psalm-return array<string, string>
* @return list<string>
*/
public function getFallbackDirsPsr4()
{
@@ -148,8 +146,7 @@ class ClassLoader
}
/**
* @return string[] Array of classname => path
* @psalm-var array<string, string>
* @return array<string, string> Array of classname => path
*/
public function getClassMap()
{
@@ -157,8 +154,7 @@ class ClassLoader
}
/**
* @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
* @param array<string, string> $classMap Class to filename map
*
* @return void
*/
@@ -175,24 +171,25 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
$paths
);
}
@@ -201,19 +198,19 @@ class ClassLoader
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
$this->prefixesPsr0[$first][$prefix] = $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
$paths
);
}
}
@@ -222,9 +219,9 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
@@ -232,17 +229,18 @@ class ClassLoader
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
$paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
@@ -252,18 +250,18 @@ class ClassLoader
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
$this->prefixDirsPsr4[$prefix] = $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
$paths
);
}
}
@@ -272,8 +270,8 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 base directories
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 base directories
*
* @return void
*/
@@ -290,8 +288,8 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
@@ -425,7 +423,8 @@ class ClassLoader
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
$includeFile = self::$includeFile;
$includeFile($file);
return true;
}
@@ -476,9 +475,9 @@ class ClassLoader
}
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
* Returns the currently registered loaders keyed by their corresponding vendor directories.
*
* @return self[]
* @return array<string, self>
*/
public static function getRegisteredLoaders()
{
@@ -555,18 +554,26 @@ class ClassLoader
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{
include $file;
/**
* @return void
*/
private static function initializeIncludeClosure()
{
if (self::$includeFile !== null) {
return;
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
*/
self::$includeFile = \Closure::bind(static function($file) {
include $file;
}, null, null);
}
}

View File

@@ -21,12 +21,14 @@ use Composer\Semver\VersionParser;
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*
* @final
*/
class InstalledVersions
{
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
*/
private static $installed;
@@ -37,7 +39,7 @@ class InstalledVersions
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static $installedByVendor = array();
@@ -96,7 +98,7 @@ class InstalledVersions
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
}
}
@@ -117,7 +119,7 @@ class InstalledVersions
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$constraint = $parser->parseConstraints((string) $constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
@@ -241,7 +243,7 @@ class InstalledVersions
/**
* @return array
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
*/
public static function getRootPackage()
{
@@ -255,7 +257,7 @@ class InstalledVersions
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
*/
public static function getRawData()
{
@@ -278,7 +280,7 @@ class InstalledVersions
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
public static function getAllRawData()
{
@@ -301,7 +303,7 @@ class InstalledVersions
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
*/
public static function reload($data)
{
@@ -311,7 +313,7 @@ class InstalledVersions
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static function getInstalled()
{
@@ -326,7 +328,9 @@ class InstalledVersions
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require $vendorDir.'/composer/installed.php';
$installed[] = self::$installedByVendor[$vendorDir] = $required;
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
@@ -338,12 +342,17 @@ class InstalledVersions
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = require __DIR__ . '/installed.php';
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require __DIR__ . '/installed.php';
self::$installed = $required;
} else {
self::$installed = array();
}
}
$installed[] = self::$installed;
if (self::$installed !== array()) {
$installed[] = self::$installed;
}
return $installed;
}

View File

@@ -2,7 +2,7 @@
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(

View File

@@ -2,23 +2,23 @@
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php',
'23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php',
'0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'c9d07b32a2e02bc0fc582d4f0c1b56cc' => $vendorDir . '/laminas/laminas-servicemanager/src/autoload.php',
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
'c9d07b32a2e02bc0fc582d4f0c1b56cc' => $vendorDir . '/laminas/laminas-servicemanager/src/autoload.php',
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
);

View File

@@ -2,7 +2,7 @@
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(

View File

@@ -2,7 +2,7 @@
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(

View File

@@ -25,46 +25,31 @@ class ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f', 'loadClassLoader'));
$includePaths = require __DIR__ . '/include_paths.php';
$includePaths[] = get_include_path();
set_include_path(implode(PATH_SEPARATOR, $includePaths));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::getInitializer($loader));
} else {
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::getInitializer($loader));
$loader->setClassMapAuthoritative(true);
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire7f81b4a2a468a061c306af5e447a9a9f($fileIdentifier, $file);
$filesToLoad = \Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}, null, null);
foreach ($filesToLoad as $fileIdentifier => $file) {
$requireFile($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequire7f81b4a2a468a061c306af5e447a9a9f($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}

View File

@@ -7,21 +7,21 @@ namespace Composer\Autoload;
class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
{
public static $files = array (
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
'0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php',
'23c18046f52bef3eea034657bafda50f' => __DIR__ . '/..' . '/symfony/polyfill-php81/bootstrap.php',
'0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php',
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'c9d07b32a2e02bc0fc582d4f0c1b56cc' => __DIR__ . '/..' . '/laminas/laminas-servicemanager/src/autoload.php',
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
'8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php',
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
'c9d07b32a2e02bc0fc582d4f0c1b56cc' => __DIR__ . '/..' . '/laminas/laminas-servicemanager/src/autoload.php',
'8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php',
);
public static $prefixLengthsPsr4 = array (

View File

@@ -2,7 +2,7 @@
// include_paths.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(

View File

@@ -4712,17 +4712,17 @@
},
{
"name": "symfony/twig-bridge",
"version": "v5.4.11",
"version_normalized": "5.4.11.0",
"version": "v5.4.31",
"version_normalized": "5.4.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/twig-bridge.git",
"reference": "63b8a50d48c9fe3d04e77307d4f1771dd848baa8"
"reference": "fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/63b8a50d48c9fe3d04e77307d4f1771dd848baa8",
"reference": "63b8a50d48c9fe3d04e77307d4f1771dd848baa8",
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942",
"reference": "fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942",
"shasum": ""
},
"require": {
@@ -4735,22 +4735,22 @@
"phpdocumentor/reflection-docblock": "<3.2.2",
"phpdocumentor/type-resolver": "<1.4.0",
"symfony/console": "<5.3",
"symfony/form": "<5.3",
"symfony/form": "<5.4.21|>=6,<6.2.7",
"symfony/http-foundation": "<5.3",
"symfony/http-kernel": "<4.4",
"symfony/translation": "<5.2",
"symfony/workflow": "<5.2"
},
"require-dev": {
"doctrine/annotations": "^1.12",
"egulias/email-validator": "^2.1.10|^3",
"doctrine/annotations": "^1.12|^2",
"egulias/email-validator": "^2.1.10|^3|^4",
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
"symfony/asset": "^4.4|^5.0|^6.0",
"symfony/console": "^5.3|^6.0",
"symfony/dependency-injection": "^4.4|^5.0|^6.0",
"symfony/expression-language": "^4.4|^5.0|^6.0",
"symfony/finder": "^4.4|^5.0|^6.0",
"symfony/form": "^5.3|^6.0",
"symfony/form": "^5.4.21|^6.2.7",
"symfony/http-foundation": "^5.3|^6.0",
"symfony/http-kernel": "^4.4|^5.0|^6.0",
"symfony/intl": "^4.4|^5.0|^6.0",
@@ -4788,7 +4788,7 @@
"symfony/web-link": "For using the WebLinkExtension",
"symfony/yaml": "For using the YamlExtension"
},
"time": "2022-07-20T13:00:38+00:00",
"time": "2023-11-09T21:19:08+00:00",
"type": "symfony-bridge",
"installation-source": "dist",
"autoload": {
@@ -4816,7 +4816,7 @@
"description": "Provides integration for Twig with various Symfony components",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/twig-bridge/tree/v5.4.11"
"source": "https://github.com/symfony/twig-bridge/tree/v5.4.31"
},
"funding": [
{

View File

@@ -1,40 +1,40 @@
<?php return array(
'root' => array(
'name' => 'combodo/itop',
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'reference' => '5465287089bacacae3304af6688ce5991893835a',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'dbf3393c9729a20f0bf389d343507238d61fef56',
'name' => 'combodo/itop',
'dev' => true,
),
'versions' => array(
'apereo/phpcas' => array(
'pretty_version' => '1.6.0',
'version' => '1.6.0.0',
'reference' => 'f817c72a961484afef95ac64a9257c8e31f063b9',
'type' => 'library',
'install_path' => __DIR__ . '/../apereo/phpcas',
'aliases' => array(),
'reference' => 'f817c72a961484afef95ac64a9257c8e31f063b9',
'dev_requirement' => false,
),
'combodo/itop' => array(
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'reference' => '5465287089bacacae3304af6688ce5991893835a',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'dbf3393c9729a20f0bf389d343507238d61fef56',
'dev_requirement' => false,
),
'combodo/tcpdf' => array(
'pretty_version' => '6.4.4',
'version' => '6.4.4.0',
'reference' => '0e31c013ccd000aa6762e9186778aa6e259ac8e8',
'type' => 'library',
'install_path' => __DIR__ . '/../combodo/tcpdf',
'aliases' => array(),
'reference' => '0e31c013ccd000aa6762e9186778aa6e259ac8e8',
'dev_requirement' => false,
),
'container-interop/container-interop' => array(
@@ -46,181 +46,181 @@
'firebase/php-jwt' => array(
'pretty_version' => 'v6.4.0',
'version' => '6.4.0.0',
'reference' => '4dd1e007f22a927ac77da5a3fbb067b42d3bc224',
'type' => 'library',
'install_path' => __DIR__ . '/../firebase/php-jwt',
'aliases' => array(),
'reference' => '4dd1e007f22a927ac77da5a3fbb067b42d3bc224',
'dev_requirement' => false,
),
'guzzlehttp/guzzle' => array(
'pretty_version' => '7.7.0',
'version' => '7.7.0.0',
'reference' => 'fb7566caccf22d74d1ab270de3551f72a58399f5',
'type' => 'library',
'install_path' => __DIR__ . '/../guzzlehttp/guzzle',
'aliases' => array(),
'reference' => 'fb7566caccf22d74d1ab270de3551f72a58399f5',
'dev_requirement' => false,
),
'guzzlehttp/promises' => array(
'pretty_version' => '2.0.0',
'version' => '2.0.0.0',
'reference' => '3a494dc7dc1d7d12e511890177ae2d0e6c107da6',
'type' => 'library',
'install_path' => __DIR__ . '/../guzzlehttp/promises',
'aliases' => array(),
'reference' => '3a494dc7dc1d7d12e511890177ae2d0e6c107da6',
'dev_requirement' => false,
),
'guzzlehttp/psr7' => array(
'pretty_version' => '2.5.0',
'version' => '2.5.0.0',
'reference' => 'b635f279edd83fc275f822a1188157ffea568ff6',
'type' => 'library',
'install_path' => __DIR__ . '/../guzzlehttp/psr7',
'aliases' => array(),
'reference' => 'b635f279edd83fc275f822a1188157ffea568ff6',
'dev_requirement' => false,
),
'laminas/laminas-loader' => array(
'pretty_version' => '2.8.0',
'version' => '2.8.0.0',
'reference' => 'd0589ec9dd48365fd95ad10d1c906efd7711c16b',
'type' => 'library',
'install_path' => __DIR__ . '/../laminas/laminas-loader',
'aliases' => array(),
'reference' => 'd0589ec9dd48365fd95ad10d1c906efd7711c16b',
'dev_requirement' => false,
),
'laminas/laminas-mail' => array(
'pretty_version' => '2.16.0',
'version' => '2.16.0.0',
'reference' => '1ee1a384b96c8af29ecad9b3a7adc27a150ebc49',
'type' => 'library',
'install_path' => __DIR__ . '/../laminas/laminas-mail',
'aliases' => array(),
'reference' => '1ee1a384b96c8af29ecad9b3a7adc27a150ebc49',
'dev_requirement' => false,
),
'laminas/laminas-mime' => array(
'pretty_version' => '2.9.1',
'version' => '2.9.1.0',
'reference' => '72d21a1b4bb7086d4a4d7058c0abca180b209184',
'type' => 'library',
'install_path' => __DIR__ . '/../laminas/laminas-mime',
'aliases' => array(),
'reference' => '72d21a1b4bb7086d4a4d7058c0abca180b209184',
'dev_requirement' => false,
),
'laminas/laminas-servicemanager' => array(
'pretty_version' => '3.16.0',
'version' => '3.16.0.0',
'reference' => '863c66733740cd36ebf5e700f4258ef2c68a2a24',
'type' => 'library',
'install_path' => __DIR__ . '/../laminas/laminas-servicemanager',
'aliases' => array(),
'reference' => '863c66733740cd36ebf5e700f4258ef2c68a2a24',
'dev_requirement' => false,
),
'laminas/laminas-stdlib' => array(
'pretty_version' => '3.12.0',
'version' => '3.12.0.0',
'reference' => 'c5aed3c798018e31fbb7b1e421b8d96bf2cda453',
'type' => 'library',
'install_path' => __DIR__ . '/../laminas/laminas-stdlib',
'aliases' => array(),
'reference' => 'c5aed3c798018e31fbb7b1e421b8d96bf2cda453',
'dev_requirement' => false,
),
'laminas/laminas-validator' => array(
'pretty_version' => '2.23.0',
'version' => '2.23.0.0',
'reference' => '6d61b6cc3b222f13807a18d9247cdfb084958b03',
'type' => 'library',
'install_path' => __DIR__ . '/../laminas/laminas-validator',
'aliases' => array(),
'reference' => '6d61b6cc3b222f13807a18d9247cdfb084958b03',
'dev_requirement' => false,
),
'league/oauth2-client' => array(
'pretty_version' => '2.6.1',
'version' => '2.6.1.0',
'reference' => '2334c249907190c132364f5dae0287ab8666aa19',
'type' => 'library',
'install_path' => __DIR__ . '/../league/oauth2-client',
'aliases' => array(),
'reference' => '2334c249907190c132364f5dae0287ab8666aa19',
'dev_requirement' => false,
),
'league/oauth2-google' => array(
'pretty_version' => '3.0.4',
'version' => '3.0.4.0',
'reference' => '6b79441f244040760bed5fdcd092a2bda7cf34c6',
'type' => 'library',
'install_path' => __DIR__ . '/../league/oauth2-google',
'aliases' => array(),
'reference' => '6b79441f244040760bed5fdcd092a2bda7cf34c6',
'dev_requirement' => false,
),
'nikic/php-parser' => array(
'pretty_version' => 'v4.14.0',
'version' => '4.14.0.0',
'reference' => '34bea19b6e03d8153165d8f30bba4c3be86184c1',
'type' => 'library',
'install_path' => __DIR__ . '/../nikic/php-parser',
'aliases' => array(),
'reference' => '34bea19b6e03d8153165d8f30bba4c3be86184c1',
'dev_requirement' => false,
),
'paragonie/random_compat' => array(
'pretty_version' => 'v9.99.100',
'version' => '9.99.100.0',
'reference' => '996434e5492cb4c3edcb9168db6fbb1359ef965a',
'type' => 'library',
'install_path' => __DIR__ . '/../paragonie/random_compat',
'aliases' => array(),
'reference' => '996434e5492cb4c3edcb9168db6fbb1359ef965a',
'dev_requirement' => false,
),
'pear/archive_tar' => array(
'pretty_version' => '1.4.14',
'version' => '1.4.14.0',
'reference' => '4d761c5334c790e45ef3245f0864b8955c562caa',
'type' => 'library',
'install_path' => __DIR__ . '/../pear/archive_tar',
'aliases' => array(),
'reference' => '4d761c5334c790e45ef3245f0864b8955c562caa',
'dev_requirement' => false,
),
'pear/console_getopt' => array(
'pretty_version' => 'v1.4.3',
'version' => '1.4.3.0',
'reference' => 'a41f8d3e668987609178c7c4a9fe48fecac53fa0',
'type' => 'library',
'install_path' => __DIR__ . '/../pear/console_getopt',
'aliases' => array(),
'reference' => 'a41f8d3e668987609178c7c4a9fe48fecac53fa0',
'dev_requirement' => false,
),
'pear/pear-core-minimal' => array(
'pretty_version' => 'v1.10.11',
'version' => '1.10.11.0',
'reference' => '68d0d32ada737153b7e93b8d3c710ebe70ac867d',
'type' => 'library',
'install_path' => __DIR__ . '/../pear/pear-core-minimal',
'aliases' => array(),
'reference' => '68d0d32ada737153b7e93b8d3c710ebe70ac867d',
'dev_requirement' => false,
),
'pear/pear_exception' => array(
'pretty_version' => 'v1.0.2',
'version' => '1.0.2.0',
'reference' => 'b14fbe2ddb0b9f94f5b24cf08783d599f776fff0',
'type' => 'class',
'install_path' => __DIR__ . '/../pear/pear_exception',
'aliases' => array(),
'reference' => 'b14fbe2ddb0b9f94f5b24cf08783d599f776fff0',
'dev_requirement' => false,
),
'pelago/emogrifier' => array(
'pretty_version' => 'v6.0.0',
'version' => '6.0.0.0',
'reference' => 'aa72d5407efac118f3896bcb995a2cba793df0ae',
'type' => 'library',
'install_path' => __DIR__ . '/../pelago/emogrifier',
'aliases' => array(),
'reference' => 'aa72d5407efac118f3896bcb995a2cba793df0ae',
'dev_requirement' => false,
),
'psr/cache' => array(
'pretty_version' => '1.0.1',
'version' => '1.0.1.0',
'reference' => 'd11b50ad223250cf17b86e38383413f5a6764bf8',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/cache',
'aliases' => array(),
'reference' => 'd11b50ad223250cf17b86e38383413f5a6764bf8',
'dev_requirement' => false,
),
'psr/cache-implementation' => array(
@@ -232,10 +232,10 @@
'psr/container' => array(
'pretty_version' => '1.1.2',
'version' => '1.1.2.0',
'reference' => '513e0666f7216c7459170d56df27dfcefe1689ea',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/container',
'aliases' => array(),
'reference' => '513e0666f7216c7459170d56df27dfcefe1689ea',
'dev_requirement' => false,
),
'psr/container-implementation' => array(
@@ -248,10 +248,10 @@
'psr/event-dispatcher' => array(
'pretty_version' => '1.0.0',
'version' => '1.0.0.0',
'reference' => 'dbefd12671e8a14ec7f180cab83036ed26714bb0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/event-dispatcher',
'aliases' => array(),
'reference' => 'dbefd12671e8a14ec7f180cab83036ed26714bb0',
'dev_requirement' => false,
),
'psr/event-dispatcher-implementation' => array(
@@ -263,10 +263,10 @@
'psr/http-client' => array(
'pretty_version' => '1.0.2',
'version' => '1.0.2.0',
'reference' => '0955afe48220520692d2d09f7ab7e0f93ffd6a31',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-client',
'aliases' => array(),
'reference' => '0955afe48220520692d2d09f7ab7e0f93ffd6a31',
'dev_requirement' => false,
),
'psr/http-client-implementation' => array(
@@ -278,10 +278,10 @@
'psr/http-factory' => array(
'pretty_version' => '1.0.2',
'version' => '1.0.2.0',
'reference' => 'e616d01114759c4c489f93b099585439f795fe35',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-factory',
'aliases' => array(),
'reference' => 'e616d01114759c4c489f93b099585439f795fe35',
'dev_requirement' => false,
),
'psr/http-factory-implementation' => array(
@@ -293,10 +293,10 @@
'psr/http-message' => array(
'pretty_version' => '2.0',
'version' => '2.0.0.0',
'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-message',
'aliases' => array(),
'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71',
'dev_requirement' => false,
),
'psr/http-message-implementation' => array(
@@ -308,10 +308,10 @@
'psr/log' => array(
'pretty_version' => '1.1.4',
'version' => '1.1.4.0',
'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/log',
'aliases' => array(),
'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11',
'dev_requirement' => false,
),
'psr/log-implementation' => array(
@@ -329,10 +329,10 @@
'ralouphie/getallheaders' => array(
'pretty_version' => '3.0.3',
'version' => '3.0.3.0',
'reference' => '120b605dfeb996808c31b6477290a714d356e822',
'type' => 'library',
'install_path' => __DIR__ . '/../ralouphie/getallheaders',
'aliases' => array(),
'reference' => '120b605dfeb996808c31b6477290a714d356e822',
'dev_requirement' => false,
),
'rsky/pear-core-min' => array(
@@ -344,37 +344,37 @@
'sabberworm/php-css-parser' => array(
'pretty_version' => '8.4.0',
'version' => '8.4.0.0',
'reference' => 'e41d2140031d533348b2192a83f02d8dd8a71d30',
'type' => 'library',
'install_path' => __DIR__ . '/../sabberworm/php-css-parser',
'aliases' => array(),
'reference' => 'e41d2140031d533348b2192a83f02d8dd8a71d30',
'dev_requirement' => false,
),
'scssphp/scssphp' => array(
'pretty_version' => 'v1.10.5',
'version' => '1.10.5.0',
'reference' => '6d44282ccf283e133ab70b6282f8e068ff2f9bf9',
'type' => 'library',
'install_path' => __DIR__ . '/../scssphp/scssphp',
'aliases' => array(),
'reference' => '6d44282ccf283e133ab70b6282f8e068ff2f9bf9',
'dev_requirement' => false,
),
'symfony/cache' => array(
'pretty_version' => 'v5.4.11',
'version' => '5.4.11.0',
'reference' => '5a0fff46df349f0db3fe242263451fddf5277362',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/cache',
'aliases' => array(),
'reference' => '5a0fff46df349f0db3fe242263451fddf5277362',
'dev_requirement' => false,
),
'symfony/cache-contracts' => array(
'pretty_version' => 'v2.5.2',
'version' => '2.5.2.0',
'reference' => '64be4a7acb83b6f2bf6de9a02cee6dad41277ebc',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/cache-contracts',
'aliases' => array(),
'reference' => '64be4a7acb83b6f2bf6de9a02cee6dad41277ebc',
'dev_requirement' => false,
),
'symfony/cache-implementation' => array(
@@ -386,82 +386,82 @@
'symfony/config' => array(
'pretty_version' => 'v5.4.11',
'version' => '5.4.11.0',
'reference' => 'ec79e03125c1d2477e43dde8528535d90cc78379',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/config',
'aliases' => array(),
'reference' => 'ec79e03125c1d2477e43dde8528535d90cc78379',
'dev_requirement' => false,
),
'symfony/console' => array(
'pretty_version' => 'v5.4.19',
'version' => '5.4.19.0',
'reference' => 'dccb8d251a9017d5994c988b034d3e18aaabf740',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/console',
'aliases' => array(),
'reference' => 'dccb8d251a9017d5994c988b034d3e18aaabf740',
'dev_requirement' => false,
),
'symfony/css-selector' => array(
'pretty_version' => 'v5.4.11',
'version' => '5.4.11.0',
'reference' => 'c1681789f059ab756001052164726ae88512ae3d',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/css-selector',
'aliases' => array(),
'reference' => 'c1681789f059ab756001052164726ae88512ae3d',
'dev_requirement' => false,
),
'symfony/dependency-injection' => array(
'pretty_version' => 'v5.4.11',
'version' => '5.4.11.0',
'reference' => 'a8b9251016e9476db73e25fa836904bc0bf74c62',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/dependency-injection',
'aliases' => array(),
'reference' => 'a8b9251016e9476db73e25fa836904bc0bf74c62',
'dev_requirement' => false,
),
'symfony/deprecation-contracts' => array(
'pretty_version' => 'v2.5.2',
'version' => '2.5.2.0',
'reference' => 'e8b495ea28c1d97b5e0c121748d6f9b53d075c66',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
'aliases' => array(),
'reference' => 'e8b495ea28c1d97b5e0c121748d6f9b53d075c66',
'dev_requirement' => false,
),
'symfony/dotenv' => array(
'pretty_version' => 'v5.4.19',
'version' => '5.4.19.0',
'reference' => '38190ba62566afa26ca723a795d0a004e061bd2a',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/dotenv',
'aliases' => array(),
'reference' => '38190ba62566afa26ca723a795d0a004e061bd2a',
'dev_requirement' => false,
),
'symfony/error-handler' => array(
'pretty_version' => 'v5.4.11',
'version' => '5.4.11.0',
'reference' => 'f75d17cb4769eb38cd5fccbda95cd80a054d35c8',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/error-handler',
'aliases' => array(),
'reference' => 'f75d17cb4769eb38cd5fccbda95cd80a054d35c8',
'dev_requirement' => false,
),
'symfony/event-dispatcher' => array(
'pretty_version' => 'v5.4.9',
'version' => '5.4.9.0',
'reference' => '8e6ce1cc0279e3ff3c8ff0f43813bc88d21ca1bc',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/event-dispatcher',
'aliases' => array(),
'reference' => '8e6ce1cc0279e3ff3c8ff0f43813bc88d21ca1bc',
'dev_requirement' => false,
),
'symfony/event-dispatcher-contracts' => array(
'pretty_version' => 'v2.5.2',
'version' => '2.5.2.0',
'reference' => 'f98b54df6ad059855739db6fcbc2d36995283fe1',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/event-dispatcher-contracts',
'aliases' => array(),
'reference' => 'f98b54df6ad059855739db6fcbc2d36995283fe1',
'dev_requirement' => false,
),
'symfony/event-dispatcher-implementation' => array(
@@ -473,145 +473,145 @@
'symfony/filesystem' => array(
'pretty_version' => 'v5.4.11',
'version' => '5.4.11.0',
'reference' => '6699fb0228d1bc35b12aed6dd5e7455457609ddd',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/filesystem',
'aliases' => array(),
'reference' => '6699fb0228d1bc35b12aed6dd5e7455457609ddd',
'dev_requirement' => false,
),
'symfony/finder' => array(
'pretty_version' => 'v5.4.11',
'version' => '5.4.11.0',
'reference' => '7872a66f57caffa2916a584db1aa7f12adc76f8c',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/finder',
'aliases' => array(),
'reference' => '7872a66f57caffa2916a584db1aa7f12adc76f8c',
'dev_requirement' => false,
),
'symfony/framework-bundle' => array(
'pretty_version' => 'v5.4.19',
'version' => '5.4.19.0',
'reference' => 'a208ee578000f9dedcb50a9784ec7ff8706a7bf1',
'type' => 'symfony-bundle',
'install_path' => __DIR__ . '/../symfony/framework-bundle',
'aliases' => array(),
'reference' => 'a208ee578000f9dedcb50a9784ec7ff8706a7bf1',
'dev_requirement' => false,
),
'symfony/http-foundation' => array(
'pretty_version' => 'v5.4.20',
'version' => '5.4.20.0',
'reference' => 'd0435363362a47c14e9cf50663cb8ffbf491875a',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/http-foundation',
'aliases' => array(),
'reference' => 'd0435363362a47c14e9cf50663cb8ffbf491875a',
'dev_requirement' => false,
),
'symfony/http-kernel' => array(
'pretty_version' => 'v5.4.20',
'version' => '5.4.20.0',
'reference' => 'aaeec341582d3c160cc9ecfa8b2419ba6c69954e',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/http-kernel',
'aliases' => array(),
'reference' => 'aaeec341582d3c160cc9ecfa8b2419ba6c69954e',
'dev_requirement' => false,
),
'symfony/polyfill-ctype' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'reference' => '6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
'aliases' => array(),
'reference' => '6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4',
'dev_requirement' => false,
),
'symfony/polyfill-intl-grapheme' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'reference' => '433d05519ce6990bf3530fba6957499d327395c2',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-grapheme',
'aliases' => array(),
'reference' => '433d05519ce6990bf3530fba6957499d327395c2',
'dev_requirement' => false,
),
'symfony/polyfill-intl-idn' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'reference' => '59a8d271f00dd0e4c2e518104cc7963f655a1aa8',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-idn',
'aliases' => array(),
'reference' => '59a8d271f00dd0e4c2e518104cc7963f655a1aa8',
'dev_requirement' => false,
),
'symfony/polyfill-intl-normalizer' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'reference' => '219aa369ceff116e673852dce47c3a41794c14bd',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
'aliases' => array(),
'reference' => '219aa369ceff116e673852dce47c3a41794c14bd',
'dev_requirement' => false,
),
'symfony/polyfill-mbstring' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'reference' => '9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
'aliases' => array(),
'reference' => '9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e',
'dev_requirement' => false,
),
'symfony/polyfill-php72' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'reference' => 'bf44a9fd41feaac72b074de600314a93e2ae78e2',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php72',
'aliases' => array(),
'reference' => 'bf44a9fd41feaac72b074de600314a93e2ae78e2',
'dev_requirement' => false,
),
'symfony/polyfill-php73' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'reference' => 'e440d35fa0286f77fb45b79a03fedbeda9307e85',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php73',
'aliases' => array(),
'reference' => 'e440d35fa0286f77fb45b79a03fedbeda9307e85',
'dev_requirement' => false,
),
'symfony/polyfill-php80' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'reference' => 'cfa0ae98841b9e461207c13ab093d76b0fa7bace',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
'aliases' => array(),
'reference' => 'cfa0ae98841b9e461207c13ab093d76b0fa7bace',
'dev_requirement' => false,
),
'symfony/polyfill-php81' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'reference' => '13f6d1271c663dc5ae9fb843a8f16521db7687a1',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php81',
'aliases' => array(),
'reference' => '13f6d1271c663dc5ae9fb843a8f16521db7687a1',
'dev_requirement' => false,
),
'symfony/routing' => array(
'pretty_version' => 'v5.4.11',
'version' => '5.4.11.0',
'reference' => '3e01ccd9b2a3a4167ba2b3c53612762300300226',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/routing',
'aliases' => array(),
'reference' => '3e01ccd9b2a3a4167ba2b3c53612762300300226',
'dev_requirement' => false,
),
'symfony/service-contracts' => array(
'pretty_version' => 'v2.5.2',
'version' => '2.5.2.0',
'reference' => '4b426aac47d6427cc1a1d0f7e2ac724627f5966c',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/service-contracts',
'aliases' => array(),
'reference' => '4b426aac47d6427cc1a1d0f7e2ac724627f5966c',
'dev_requirement' => false,
),
'symfony/service-implementation' => array(
@@ -623,82 +623,82 @@
'symfony/stopwatch' => array(
'pretty_version' => 'v5.4.19',
'version' => '5.4.19.0',
'reference' => 'bd2b066090fd6a67039371098fa25a84cb2679ec',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/stopwatch',
'aliases' => array(),
'reference' => 'bd2b066090fd6a67039371098fa25a84cb2679ec',
'dev_requirement' => true,
),
'symfony/string' => array(
'pretty_version' => 'v5.4.11',
'version' => '5.4.11.0',
'reference' => '5eb661e49ad389e4ae2b6e4df8d783a8a6548322',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/string',
'aliases' => array(),
'reference' => '5eb661e49ad389e4ae2b6e4df8d783a8a6548322',
'dev_requirement' => false,
),
'symfony/translation-contracts' => array(
'pretty_version' => 'v2.5.2',
'version' => '2.5.2.0',
'reference' => '136b19dd05cdf0709db6537d058bcab6dd6e2dbe',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/translation-contracts',
'aliases' => array(),
'reference' => '136b19dd05cdf0709db6537d058bcab6dd6e2dbe',
'dev_requirement' => false,
),
'symfony/twig-bridge' => array(
'pretty_version' => 'v5.4.11',
'version' => '5.4.11.0',
'pretty_version' => 'v5.4.31',
'version' => '5.4.31.0',
'reference' => 'fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942',
'type' => 'symfony-bridge',
'install_path' => __DIR__ . '/../symfony/twig-bridge',
'aliases' => array(),
'reference' => '63b8a50d48c9fe3d04e77307d4f1771dd848baa8',
'dev_requirement' => false,
),
'symfony/twig-bundle' => array(
'pretty_version' => 'v5.4.19',
'version' => '5.4.19.0',
'reference' => '286bd9e38b9bcb142f1eda0a75b0bbeb49ff34bd',
'type' => 'symfony-bundle',
'install_path' => __DIR__ . '/../symfony/twig-bundle',
'aliases' => array(),
'reference' => '286bd9e38b9bcb142f1eda0a75b0bbeb49ff34bd',
'dev_requirement' => false,
),
'symfony/var-dumper' => array(
'pretty_version' => 'v5.4.11',
'version' => '5.4.11.0',
'reference' => 'b8f306d7b8ef34fb3db3305be97ba8e088fb4861',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/var-dumper',
'aliases' => array(),
'reference' => 'b8f306d7b8ef34fb3db3305be97ba8e088fb4861',
'dev_requirement' => false,
),
'symfony/var-exporter' => array(
'pretty_version' => 'v5.4.10',
'version' => '5.4.10.0',
'reference' => '8fc03ee75eeece3d9be1ef47d26d79bea1afb340',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/var-exporter',
'aliases' => array(),
'reference' => '8fc03ee75eeece3d9be1ef47d26d79bea1afb340',
'dev_requirement' => false,
),
'symfony/web-profiler-bundle' => array(
'pretty_version' => 'v5.4.19',
'version' => '5.4.19.0',
'reference' => 'cd83822071f2bc05583af1e53c1bc46be625a56d',
'type' => 'symfony-bundle',
'install_path' => __DIR__ . '/../symfony/web-profiler-bundle',
'aliases' => array(),
'reference' => 'cd83822071f2bc05583af1e53c1bc46be625a56d',
'dev_requirement' => true,
),
'symfony/yaml' => array(
'pretty_version' => 'v5.4.19',
'version' => '5.4.19.0',
'reference' => '71c05db20cb9b54d381a28255f17580e2b7e36a5',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/yaml',
'aliases' => array(),
'reference' => '71c05db20cb9b54d381a28255f17580e2b7e36a5',
'dev_requirement' => false,
),
'tecnickcom/tcpdf' => array(
@@ -710,28 +710,28 @@
'thenetworg/oauth2-azure' => array(
'pretty_version' => 'v2.1.1',
'version' => '2.1.1.0',
'reference' => '06fb2d620fb6e6c934f632c7ec7c5ea2e978a844',
'type' => 'library',
'install_path' => __DIR__ . '/../thenetworg/oauth2-azure',
'aliases' => array(),
'reference' => '06fb2d620fb6e6c934f632c7ec7c5ea2e978a844',
'dev_requirement' => false,
),
'twig/twig' => array(
'pretty_version' => 'v3.4.3',
'version' => '3.4.3.0',
'reference' => 'c38fd6b0b7f370c198db91ffd02e23b517426b58',
'type' => 'library',
'install_path' => __DIR__ . '/../twig/twig',
'aliases' => array(),
'reference' => 'c38fd6b0b7f370c198db91ffd02e23b517426b58',
'dev_requirement' => false,
),
'webmozart/assert' => array(
'pretty_version' => '1.11.0',
'version' => '1.11.0.0',
'reference' => '11cb2199493b2f8a3b53e7f19068fc6aac760991',
'type' => 'library',
'install_path' => __DIR__ . '/../webmozart/assert',
'aliases' => array(),
'reference' => '11cb2199493b2f8a3b53e7f19068fc6aac760991',
'dev_requirement' => false,
),
),

View File

@@ -42,8 +42,8 @@ final class CodeExtension extends AbstractExtension
public function getFilters(): array
{
return [
new TwigFilter('abbr_class', [$this, 'abbrClass'], ['is_safe' => ['html']]),
new TwigFilter('abbr_method', [$this, 'abbrMethod'], ['is_safe' => ['html']]),
new TwigFilter('abbr_class', [$this, 'abbrClass'], ['is_safe' => ['html'], 'pre_escape' => 'html']),
new TwigFilter('abbr_method', [$this, 'abbrMethod'], ['is_safe' => ['html'], 'pre_escape' => 'html']),
new TwigFilter('format_args', [$this, 'formatArgs'], ['is_safe' => ['html']]),
new TwigFilter('format_args_as_text', [$this, 'formatArgsAsText']),
new TwigFilter('file_excerpt', [$this, 'fileExcerpt'], ['is_safe' => ['html']]),
@@ -85,22 +85,23 @@ final class CodeExtension extends AbstractExtension
$result = [];
foreach ($args as $key => $item) {
if ('object' === $item[0]) {
$item[1] = htmlspecialchars($item[1], \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset);
$parts = explode('\\', $item[1]);
$short = array_pop($parts);
$formattedValue = sprintf('<em>object</em>(<abbr title="%s">%s</abbr>)', $item[1], $short);
} elseif ('array' === $item[0]) {
$formattedValue = sprintf('<em>array</em>(%s)', \is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]);
$formattedValue = sprintf('<em>array</em>(%s)', \is_array($item[1]) ? $this->formatArgs($item[1]) : htmlspecialchars(var_export($item[1], true), \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset));
} elseif ('null' === $item[0]) {
$formattedValue = '<em>null</em>';
} elseif ('boolean' === $item[0]) {
$formattedValue = '<em>'.strtolower(var_export($item[1], true)).'</em>';
$formattedValue = '<em>'.strtolower(htmlspecialchars(var_export($item[1], true), \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset)).'</em>';
} elseif ('resource' === $item[0]) {
$formattedValue = '<em>resource</em>';
} else {
$formattedValue = str_replace("\n", '', htmlspecialchars(var_export($item[1], true), \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset));
}
$result[] = \is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue);
$result[] = \is_int($key) ? $formattedValue : sprintf("'%s' => %s", htmlspecialchars($key, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset), $formattedValue);
}
return implode(', ', $result);
@@ -123,13 +124,25 @@ final class CodeExtension extends AbstractExtension
// highlight_file could throw warnings
// see https://bugs.php.net/25725
$code = @highlight_file($file, true);
// remove main code/span tags
$code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code);
// split multiline spans
$code = preg_replace_callback('#<span ([^>]++)>((?:[^<]*+<br \/>)++[^<]*+)</span>#', function ($m) {
return "<span $m[1]>".str_replace('<br />', "</span><br /><span $m[1]>", $m[2]).'</span>';
}, $code);
$content = explode('<br />', $code);
if (\PHP_VERSION_ID >= 80300) {
// remove main pre/code tags
$code = preg_replace('#^<pre.*?>\s*<code.*?>(.*)</code>\s*</pre>#s', '\\1', $code);
// split multiline code tags
$code = preg_replace_callback('#<code ([^>]++)>((?:[^<]*+\\n)++[^<]*+)</code>#', function ($m) {
return "<code $m[1]>".str_replace("\n", "</code>\n<code $m[1]>", $m[2]).'</code>';
}, $code);
// Convert spaces to html entities to preserve indentation when rendered
$code = str_replace(' ', '&nbsp;', $code);
$content = explode("\n", $code);
} else {
// remove main code/span tags
$code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code);
// split multiline spans
$code = preg_replace_callback('#<span ([^>]++)>((?:[^<]*+<br \/>)++[^<]*+)</span>#', function ($m) {
return "<span $m[1]>".str_replace('<br />', "</span><br /><span $m[1]>", $m[2]).'</span>';
}, $code);
$content = explode('<br />', $code);
}
$lines = [];
if (0 > $srcContext) {
@@ -154,11 +167,14 @@ final class CodeExtension extends AbstractExtension
$file = trim($file);
if (null === $text) {
$text = $file;
if (null !== $rel = $this->getFileRelative($text)) {
$rel = explode('/', $rel, 2);
$text = sprintf('<abbr title="%s%2$s">%s</abbr>%s', $this->projectDir, $rel[0], '/'.($rel[1] ?? ''));
if (null !== $rel = $this->getFileRelative($file)) {
$rel = explode('/', htmlspecialchars($rel, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset), 2);
$text = sprintf('<abbr title="%s%2$s">%s</abbr>%s', htmlspecialchars($this->projectDir, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset), $rel[0], '/'.($rel[1] ?? ''));
} else {
$text = htmlspecialchars($file, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset);
}
} else {
$text = htmlspecialchars($text, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset);
}
if (0 < $line) {

View File

@@ -116,6 +116,10 @@ final class TranslationExtension extends AbstractExtension
throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be a locale passed as a string when the message is a "%s", "%s" given.', __METHOD__, TranslatableInterface::class, get_debug_type($arguments)));
}
if ($message instanceof TranslatableMessage && '' === $message->getMessage()) {
return '';
}
return $message->trans($this->getTranslator(), $locale ?? (\is_string($arguments) ? $arguments : null));
}

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2022 Fabien Potencier
Copyright (c) 2004-present Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -235,7 +235,7 @@ class NotificationEmail extends TemplatedEmail
*/
public function __serialize(): array
{
return [$this->context, parent::__serialize()];
return [$this->context, $this->theme, parent::__serialize()];
}
/**
@@ -243,7 +243,12 @@ class NotificationEmail extends TemplatedEmail
*/
public function __unserialize(array $data): void
{
[$this->context, $parentData] = $data;
if (3 === \count($data)) {
[$this->context, $this->theme, $parentData] = $data;
} else {
// Backwards compatibility for deserializing data structures that were serialized without the theme
[$this->context, $parentData] = $data;
}
parent::__unserialize($parentData);
}

View File

@@ -35,6 +35,12 @@ final class WrappedTemplatedEmail
return $this->message->getTo()[0]->getName();
}
/**
* @param string $image A Twig path to the image file. It's recommended to define
* some Twig namespace for email images (e.g. '@email/images/logo.png').
* @param string|null $contentType The media type (i.e. MIME type) of the image file (e.g. 'image/png').
* Some email clients require this to display embedded images.
*/
public function image(string $image, string $contentType = null): string
{
$file = $this->twig->getLoader()->getSourceContext($image);
@@ -47,6 +53,13 @@ final class WrappedTemplatedEmail
return 'cid:'.$image;
}
/**
* @param string $file A Twig path to the file. It's recommended to define
* some Twig namespace for email files (e.g. '@email/files/contract.pdf').
* @param string|null $name A custom file name that overrides the original name of the attached file
* @param string|null $contentType The media type (i.e. MIME type) of the file (e.g. 'application/pdf').
* Some email clients require this to display attached files.
*/
public function attach(string $file, string $name = null, string $contentType = null): void
{
$file = $this->twig->getLoader()->getSourceContext($file);

View File

@@ -1,7 +1,7 @@
/*
* Copyright (c) 2017 ZURB, inc. -- MIT License
*
* https://raw.githubusercontent.com/foundation/foundation-emails/v2.2.1/dist/foundation-emails.css
* https://github.com/foundation/foundation-emails/blob/v2.2.1/dist/foundation-emails.css
*/
.wrapper {

View File

@@ -26,7 +26,7 @@
{% if markdown %}
{{ include('@email/zurb_2/notification/content_markdown.html.twig') }}
{% else %}
{{ (raw ? content|raw : content)|nl2br }}
{{ raw ? content|raw : content|nl2br }}
{% endif %}
{% endblock %}

View File

@@ -90,6 +90,10 @@
{%- if required -%}
{%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' required')|trim}) -%}
{%- endif -%}
{%- if parent_label_class is defined -%}
{% set embed_label_classes = parent_label_class|split(' ')|filter(class => class in ['checkbox-inline', 'radio-inline']) %}
{%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ embed_label_classes|join(' '))|trim}) -%}
{% endif %}
{%- if label is not same as(false) and label is empty -%}
{%- if label_format is not empty -%}
{%- set label = label_format|replace({

View File

@@ -6,7 +6,7 @@
{%- set prepend = not (money_pattern starts with '{{') -%}
{%- set append = not (money_pattern ends with '}}') -%}
{%- if prepend or append -%}
<div class="input-group{{ group_class|default('') }}">
<div class="input-group {{ group_class|default('') }}">
{%- if prepend -%}
<div class="input-group-prepend">
<span class="input-group-text">{{ money_pattern|form_encode_currency }}</span>
@@ -283,6 +283,10 @@
{%- if required -%}
{%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' required')|trim}) -%}
{%- endif -%}
{%- if parent_label_class is defined -%}
{% set embed_label_classes = parent_label_class|split(' ')|filter(class => class in ['checkbox-inline', 'radio-inline']) %}
{%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ embed_label_classes|join(' '))|trim}) -%}
{% endif %}
{%- if label is not same as(false) and label is empty -%}
{%- if label_format is not empty -%}
{%- set label = label_format|replace({

View File

@@ -6,7 +6,7 @@
{%- set prepend = not (money_pattern starts with '{{') -%}
{%- set append = not (money_pattern ends with '}}') -%}
{%- if prepend or append -%}
<div class="input-group{{ group_class|default('') }}">
<div class="input-group {{ group_class|default('') }}">
{%- if prepend -%}
<span class="input-group-text">{{ money_pattern|form_encode_currency }}</span>
{%- endif -%}
@@ -213,10 +213,10 @@
{%- set parent_label_class = parent_label_class|default(label_attr.class|default('')) -%}
{%- set row_class = 'form-check' -%}
{%- if 'checkbox-inline' in parent_label_class %}
{% set row_class = row_class ~ ' form-check-inline' %}
{%- set row_class = row_class ~ ' form-check-inline' -%}
{% endif -%}
{%- if 'checkbox-switch' in parent_label_class %}
{% set row_class = row_class ~ ' form-switch' %}
{%- set row_class = row_class ~ ' form-switch' -%}
{% endif -%}
<div class="{{ row_class }}">
{{- form_label(form, null, { widget: parent() }) -}}

View File

@@ -11,7 +11,7 @@
{% set prepend = not (money_pattern starts with '{{') %}
{% set append = not (money_pattern ends with '}}') %}
{% if prepend or append %}
<div class="input-group{{ group_class|default('') }}">
<div class="input-group {{ group_class|default('') }}">
{% if prepend %}
<span class="input-group-addon">{{ money_pattern|form_encode_currency }}</span>
{% endif %}

View File

@@ -253,6 +253,10 @@
{% if errors|length > 0 -%}
{% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' error')|trim}) %}
{% endif %}
{%- if parent_label_class is defined -%}
{% set embed_label_classes = parent_label_class|split(' ')|filter(class => class in ['checkbox-inline', 'radio-inline']) %}
{%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ embed_label_classes|join(' '))|trim}) -%}
{% endif %}
{% if label is empty %}
{%- if label_format is not empty -%}
{% set label = label_format|replace({

View File

@@ -11,6 +11,7 @@
namespace Symfony\Bridge\Twig;
use Composer\InstalledVersions;
use Symfony\Bundle\FullStack;
use Twig\Error\SyntaxError;
use Twig\TwigFilter;
@@ -102,6 +103,12 @@ class UndefinedCallableHandler
return sprintf('Did you forget to %s? Unknown %s "%s".', self::FULL_STACK_ENABLE[$component], $type, $name);
}
return sprintf('Did you forget to run "composer require symfony/%s"? Unknown %s "%s".', $component, $type, $name);
$missingPackage = 'symfony/'.$component;
if (class_exists(InstalledVersions::class) && InstalledVersions::isInstalled($missingPackage)) {
$missingPackage = 'symfony/twig-bundle';
}
return sprintf('Did you forget to run "composer require %s"? Unknown %s "%s".', $missingPackage, $type, $name);
}
}

View File

@@ -22,13 +22,13 @@
"twig/twig": "^2.13|^3.0.4"
},
"require-dev": {
"doctrine/annotations": "^1.12",
"egulias/email-validator": "^2.1.10|^3",
"doctrine/annotations": "^1.12|^2",
"egulias/email-validator": "^2.1.10|^3|^4",
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
"symfony/asset": "^4.4|^5.0|^6.0",
"symfony/dependency-injection": "^4.4|^5.0|^6.0",
"symfony/finder": "^4.4|^5.0|^6.0",
"symfony/form": "^5.3|^6.0",
"symfony/form": "^5.4.21|^6.2.7",
"symfony/http-foundation": "^5.3|^6.0",
"symfony/http-kernel": "^4.4|^5.0|^6.0",
"symfony/intl": "^4.4|^5.0|^6.0",
@@ -56,7 +56,7 @@
"phpdocumentor/reflection-docblock": "<3.2.2",
"phpdocumentor/type-resolver": "<1.4.0",
"symfony/console": "<5.3",
"symfony/form": "<5.3",
"symfony/form": "<5.4.21|>=6,<6.2.7",
"symfony/http-foundation": "<5.3",
"symfony/http-kernel": "<4.4",
"symfony/translation": "<5.2",

View File

@@ -25,6 +25,23 @@ use utils;
*/
class FormHelper
{
/**
* @var string
* @since 3.1.1 N°6861
*/
public const ENUM_MANDATORY_BLOB_MODE_CREATE = 'Create';
/**
* @var string
* @since 3.1.1 N°6861
*/
public const ENUM_MANDATORY_BLOB_MODE_MODIFY_EMPTY = 'Modify';
/**
* @var string
* @since 3.1.1 N°6861
*/
public const ENUM_MANDATORY_BLOB_MODE_MODIFY_FILLED = 'Modify:Filled';
/**
* DisableAttributeBlobInputs.
*
@@ -58,6 +75,26 @@ class FormHelper
}
}
/**
* Returns an attribute code if the object has a mandatory attribute blob, null otherwise
*
* @see N°6861 - Display warning when creating/editing a mandatory blob in modal
*
* @param \DBObject $oObject
*
* @return string|null
* @throws \CoreException
*/
public static function GetMandatoryAttributeBlobInputs(DBObject $oObject): ?string
{
foreach (MetaModel::ListAttributeDefs(get_class($oObject)) as $sAttCode => $oAttDef) {
if ($oAttDef instanceof AttributeBlob && (!$oAttDef->IsNullAllowed() || ($oObject->GetFormAttributeFlags($sAttCode) & OPT_ATT_MANDATORY))) {
return $sAttCode;
}
}
return null;
}
/**
* Returns true if the object has a mandatory attribute blob
*
@@ -70,25 +107,29 @@ class FormHelper
*/
public static function HasMandatoryAttributeBlobInputs(DBObject $oObject): bool
{
foreach (MetaModel::ListAttributeDefs(get_class($oObject)) as $sAttCode => $oAttDef) {
if ($oAttDef instanceof AttributeBlob && (!$oAttDef->IsNullAllowed() || ($oObject->GetFormAttributeFlags($sAttCode) & OPT_ATT_MANDATORY))) {
return true;
}
}
return false;
return self::GetMandatoryAttributeBlobInputs($oObject) !== null;
}
/**
* Returns an Alert explaining what will happen when a mandatory attribute blob is displayed in a form
*
* @see N°6861 - Display warning when creating/editing a mandatory blob in modal
* @see self::ENUM_MANDATORY_BLOB_MODE_XXX
*
* @param string $sMode
*
* @return \Combodo\iTop\Application\UI\Base\Component\Alert\Alert
*/
public static function GetAlertForMandatoryAttributeBlobInputsInModal(): Alert
public static function GetAlertForMandatoryAttributeBlobInputsInModal(string $sMode = self::ENUM_MANDATORY_BLOB_MODE_MODIFY_EMPTY): Alert
{
$oAlert = AlertUIBlockFactory::MakeForWarning('',Dict::S('UI:Object:Modal:MandatoryAttributeBlobInputs:Warning:Text'));
return $oAlert;
$sMessage = Dict::S('UI:Object:Modal:'.$sMode.':MandatoryAttributeBlobInputs:Warning:Text');
// If the mandatory attribute is already filled, there's no risk to make an object incomplete so we display an information level alert
if($sMode === self::ENUM_MANDATORY_BLOB_MODE_MODIFY_FILLED){
return AlertUIBlockFactory::MakeForInformation('', $sMessage);
}
return AlertUIBlockFactory::MakeForWarning('', $sMessage);
}
/**

View File

@@ -91,11 +91,11 @@ abstract class AbstractBlockLinkSetViewTable extends UIContentBlock
* @param string $sObjectClass
* @param string $sAttCode
* @param AttributeLinkedSet $oAttDef
* @param bool $bIsReadOnly
*
* @throws CoreException
* @throws Exception
* @throws \CoreException
*/
public function __construct(WebPage $oPage, DBObject $oDbObject, string $sObjectClass, string $sAttCode, AttributeLinkedSet $oAttDef)
public function __construct(WebPage $oPage, DBObject $oDbObject, string $sObjectClass, string $sAttCode, AttributeLinkedSet $oAttDef, bool $bIsReadOnly = false)
{
parent::__construct("links_view_table_$sAttCode", ["ibo-block-links-table"]);
@@ -105,6 +105,7 @@ abstract class AbstractBlockLinkSetViewTable extends UIContentBlock
$this->sObjectClass = $sObjectClass;
$this->oDbObject = $oDbObject;
$this->sTableId = 'rel_'.$this->sAttCode;
$this->bIsAttEditable = !$bIsReadOnly;
$this->SetDataAttributes(['role' => 'ibo-block-links-table', 'link-attcode' => $sAttCode, 'link-class' => $this->oAttDef->GetLinkedClass()]);
// Initialization
$this->Init();
@@ -122,7 +123,6 @@ abstract class AbstractBlockLinkSetViewTable extends UIContentBlock
private function Init()
{
$this->sTargetClass = $this->GetTargetClass();
$this->InitIsAttEditable();
// User rights
$this->bIsAllowCreate = $this->bIsAttEditable && UserRights::IsActionAllowed($this->oAttDef->GetLinkedClass(), UR_ACTION_CREATE) == UR_ALLOWED_YES;
@@ -199,38 +199,8 @@ abstract class AbstractBlockLinkSetViewTable extends UIContentBlock
$oBlock = new DisplayBlock($oLinkSet->GetFilter(), DisplayBlock::ENUM_STYLE_LIST_IN_OBJECT, false);
$this->AddSubBlock($oBlock->GetRenderContent($oPage, $this->GetExtraParam(), $this->sTableId));
}
/**
* @return void
* @throws \CoreException
*/
private function InitIsAttEditable(): void
{
$iFlags = OPT_ATT_NORMAL;
if ($this->oDbObject->IsNew())
{
$iFlags = $this->oDbObject->GetInitialStateAttributeFlags($this->sAttCode);
}
else
{
$iFlags = $this->oDbObject->GetAttributeFlags($this->sAttCode);
}
$bEditWhen = $this->IsEditableBasedOnEditWhen();
$this->bIsAttEditable = !($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE | OPT_ATT_HIDDEN)) && $bEditWhen;
}
/**
* Compares Linkset attribute edit_when values with its usage requirements
*
* @return bool
* @since 3.1.1 3.2.0 N°6385
*/
protected function IsEditableBasedOnEditWhen(): bool{
return true;
}
/**
* GetTableId.

View File

@@ -179,13 +179,4 @@ class BlockDirectLinkSetViewTable extends AbstractBlockLinkSetViewTable
return $aDefaults;
}
/**
* @inheritDoc
*/
protected function IsEditableBasedOnEditWhen(): bool
{
$sEditWhen = $this->oAttDef->GetEditWhen();
return $sEditWhen === LINKSET_EDITWHEN_ALWAYS || $sEditWhen === LINKSET_EDITWHEN_ON_HOST_DISPLAY;
}
}

View File

@@ -126,13 +126,4 @@ class BlockIndirectLinkSetViewTable extends AbstractBlockLinkSetViewTable
return $sAttCodesToDisplay;
}
/**
* @inheritDoc
*/
protected function IsEditableBasedOnEditWhen(): bool
{
$sEditWhen = $this->oAttDef->GetEditWhen();
return $sEditWhen === LINKSET_EDITWHEN_ALWAYS || $sEditWhen === LINKSET_EDITWHEN_ON_HOST_DISPLAY;
}
}

View File

@@ -254,14 +254,14 @@ JS
protected function GetReadyScriptsStartedTrigger(): ?string
{
return <<<JS
$("body").attr("data-ready-scripts", "start");
CombodoJsActivity.AddOngoingScript();
JS;
}
protected function GetReadyScriptsFinishedTrigger(): ?string
{
return <<<JS
$("body").attr("data-ready-scripts", "done");
CombodoJsActivity.RemoveOngoingScript();
JS;
}
}

View File

@@ -171,7 +171,7 @@ JS;
FormHelper::DisableAttributeBlobInputs($sRealClass, $aFormExtraParams);
if(FormHelper::HasMandatoryAttributeBlobInputs($oObjToClone)){
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal());
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal(FormHelper::ENUM_MANDATORY_BLOB_MODE_CREATE));
}
$aFormExtraParams['js_handlers']['cancel_button_on_click'] =
@@ -210,13 +210,14 @@ JS
}
return $oPage;
}
/**
* @return \iTopWebPage|\AjaxPage Object edit form in its webpage
* @throws \ApplicationException
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \SecurityException
* @throws \Exception
*/
public function OperationModify()
{
@@ -298,7 +299,14 @@ JS;
FormHelper::DisableAttributeBlobInputs($sClass, $aFormExtraParams);
if(FormHelper::HasMandatoryAttributeBlobInputs($oObj)){
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal());
$sMandatoryBlobAttCode = FormHelper::GetMandatoryAttributeBlobInputs($oObj);
$sAlertFormMandatoryAttMessageMode = FormHelper::ENUM_MANDATORY_BLOB_MODE_MODIFY_EMPTY;
$oMandatoryBlobAttCodeValue = $oObj->Get($sMandatoryBlobAttCode);
// If the current value of the mandatory attribute is not empty, display a different message
if($oMandatoryBlobAttCodeValue instanceof \ormDocument && !$oMandatoryBlobAttCodeValue->IsEmpty()){
$sAlertFormMandatoryAttMessageMode = FormHelper::ENUM_MANDATORY_BLOB_MODE_MODIFY_FILLED;
}
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal($sAlertFormMandatoryAttMessageMode));
}
} else {
$oPage = new iTopWebPage('', $bPrintable);

View File

@@ -230,7 +230,7 @@ JS
FormHelper::DisableAttributeBlobInputs($sRealClass, $aExtraParams);
if(FormHelper::HasMandatoryAttributeBlobInputs($oObj)){
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal());
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal(FormHelper::ENUM_MANDATORY_BLOB_MODE_CREATE));
}
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, array(), $aExtraParams);

View File

@@ -38,7 +38,7 @@ class ObjectRepository
*
* @return array|null
*/
static public function Search(string $sObjectClass, array $aFieldsToLoad, string $sSearch): ?array
public static function Search(string $sObjectClass, array $aFieldsToLoad, string $sSearch): ?array
{
try {
@@ -82,7 +82,7 @@ class ObjectRepository
*
* @return array|null
*/
static public function SearchFromOql(string $sObjectClass, array $aFieldsToLoad, string $sOql, string $sSearch, DBObject $oThisObject = null): ?array
public static function SearchFromOql(string $sObjectClass, array $aFieldsToLoad, string $sOql, string $sSearch, DBObject $oThisObject = null): ?array
{
try {
@@ -117,7 +117,7 @@ class ObjectRepository
* @throws \CoreException
* @throws \DictExceptionMissingString
*/
static private function DBSetToObjectArray(iDBObjectSetIterator $oDbObjectSet, string $sObjectClass, array $aFieldsToLoad): array
private static function DBSetToObjectArray(iDBObjectSetIterator $oDbObjectSet, string $sObjectClass, array $aFieldsToLoad): array
{
// Retrieve friendly name complementary specification
$aComplementAttributeSpec = MetaModel::GetNameSpec($sObjectClass, FriendlyNameType::COMPLEMENTARY);
@@ -158,7 +158,7 @@ class ObjectRepository
*
* @return mixed
*/
static public function GetDefaultFieldsToLoad(array $aComplementAttributeSpec, string $sObjectImageAttCode)
public static function GetDefaultFieldsToLoad(array $aComplementAttributeSpec, string $sObjectImageAttCode)
{
// Friendly name complementary fields
$aFieldsToLoad = $aComplementAttributeSpec[1];
@@ -185,7 +185,7 @@ class ObjectRepository
*
* @return array
*/
static public function ComputeOthersData(DBObject $oDbObject, string $sClass, array $aData, array $aComplementAttributeSpec, string $sObjectImageAttCode): array
public static function ComputeOthersData(DBObject $oDbObject, string $sClass, array $aData, array $aComplementAttributeSpec, string $sObjectImageAttCode): array
{
try {
@@ -196,6 +196,7 @@ class ObjectRepository
$aData['obsolescence_flag'] = $oDbObject->IsObsolete();
// Additional fields
$sFriendlynameForHtml = utils::EscapeHtml($aData['friendlyname']);
if (count($aComplementAttributeSpec[1]) > 0) {
$aData['has_additional_field'] = true;
$aArguments = [];
@@ -203,9 +204,10 @@ class ObjectRepository
$aArguments[] = $oDbObject->Get($sAdditionalField);
}
$aData['additional_field'] = vsprintf($aComplementAttributeSpec[0], $aArguments);
$aData['full_description'] = "{$aData['friendlyname']}<br><i><small>{$aData['additional_field']}</small></i>";
$sAdditionalFieldForHtml = utils::EscapeHtml($aData['additional_field']);
$aData['full_description'] = "{$sFriendlynameForHtml}<br><i><small>{$sAdditionalFieldForHtml}</small></i>";
} else {
$aData['full_description'] = $aData['friendlyname'];
$aData['full_description'] = $sFriendlynameForHtml;
}
// Image
@@ -308,7 +310,7 @@ class ObjectRepository
*
* @return bool
*/
static public function DeleteFromOql(string $sOql): bool
public static function DeleteFromOql(string $sOql): bool
{
try {

View File

@@ -8,6 +8,7 @@ namespace Combodo\iTop\Service\Events;
use Closure;
use Combodo\iTop\Service\Events\Description\EventDescription;
use Combodo\iTop\Service\Module\ModuleService;
use ContextTag;
use CoreException;
use DBObject;
@@ -136,12 +137,9 @@ final class EventService
throw new CoreException($sError);
}
$eventSource = $oEventData->GetEventSource();
$oKPI = new ExecutionKPI();
$sLogEventName = "$sEvent - ".self::GetSourcesAsString($eventSource).' '.json_encode($oEventData->GetEventData());
EventServiceLog::Trace("Fire event '$sLogEventName'");
if (!isset(self::$aEventListeners[$sEvent])) {
$oKPI->ComputeStats('FireEvent', $sEvent);
return;
}
@@ -157,7 +155,14 @@ final class EventService
$bEventFired = true;
try {
$oEventData->SetCallbackData($aEventCallback['data']);
$oKPI = new ExecutionKPI();
call_user_func($aEventCallback['callback'], $oEventData);
if (is_array($aEventCallback['callback']) && !$oKPI->ComputeStatsForExtension($aEventCallback['callback'][0], $aEventCallback['callback'][1], "Event: $sEvent")) {
$sSignature = ModuleService::GetInstance()->GetModuleMethodSignature($aEventCallback['callback'][0], $aEventCallback['callback'][1]);
$oKPI->ComputeStats('FireEvent', "$sEvent callback: $sSignature");
}
}
catch (EventException $e) {
EventServiceLog::Error("Event '$sLogEventName' for '$sName' id {$aEventCallback['id']} failed with blocking error: ".$e->getMessage());
@@ -172,7 +177,6 @@ final class EventService
if ($bEventFired) {
EventServiceLog::Debug("End of event '$sLogEventName'");
}
$oKPI->ComputeStats('FireEvent', $sEvent);
if (!is_null($oLastException)) {
EventServiceLog::Error("Throwing the last exception caught: $sLastExceptionMessage");

View File

@@ -61,7 +61,7 @@ if (typeof(charts) === "undefined")
}
var idxChart=charts.length;
charts.push(chart);
var refreshChart{{ oUIBlock.sId|sanitize(constant('utils::ENUM_SANITIZATION_FILTER_VARIABLE_NAME')) }} = '$.post("{{ oUIBlock.sURLForRefresh|raw }}&refresh='+idxChart+'","", function (data) {'+
var refreshChart{{ oUIBlock.sId|sanitize(constant('utils::ENUM_SANITIZATION_FILTER_VARIABLE_NAME')) }} = '$.post("{{ oUIBlock.sURLForRefresh|escape('js') }}&refresh='+idxChart+'","", function (data) {'+
'charts['+idxChart+'].unload();'+
'setTimeout(function () {eval(data.js);},50);'+
'})';

View File

@@ -39,7 +39,7 @@ if (typeof (charts) === "undefined")
}
var idxChart = charts.length;
charts.push(chart);
var refreshChart{{ oUIBlock.sId|sanitize(constant('utils::ENUM_SANITIZATION_FILTER_VARIABLE_NAME')) }}=' $.post("{{ oUIBlock.sURLForRefresh|raw }}&refresh='+idxChart+'","", function (data) {'+
var refreshChart{{ oUIBlock.sId|sanitize(constant('utils::ENUM_SANITIZATION_FILTER_VARIABLE_NAME')) }}=' $.post("{{ oUIBlock.sURLForRefresh|escape('js')}}&refresh='+idxChart+'","", function (data) {'+
'charts['+idxChart+'].unload();'+
'setTimeout(function () {eval(data.js);},50);'+
'});';

View File

@@ -2,7 +2,7 @@
{# @license http://opensource.org/licenses/AGPL-3.0 #}
{% apply spaceless %}
$.post(
'{{ oUIBlock.sAjaxLink|raw }}',
'{{ oUIBlock.sAjaxLink|escape('js') }}',
{{ oUIBlock.sJsonParams|raw }},
function(data) {
$('#csv_content').html(data);

View File

@@ -0,0 +1,206 @@
<?php
namespace Combodo\iTop\Test\UnitTest\Core;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
use MetaModel;
use PHPUnit\Framework\ExpectationFailedException;
use UserRights;
use VariableExpression;
/**
* Class MetaModelMagicPlaceholderTest
* @since 3.1.1 N°6824
* @covers MetaModel::AddMagicPlaceholders()
* @package Combodo\iTop\Test\UnitTest\Core
*/
class MetaModelMagicPlaceholderTest extends ItopDataTestCase
{
/**
* Asserts that two array with DBObjects are equal (the important is to check the {class,id} couple
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws ExpectationFailedException
*/
public static function assertEqualsShallow($expected, $actual, string $message = ''): void
{
if (is_array($expected)) {
foreach ($expected as $key => $value) {
if ($value instanceof \DBObject) {
$expected[$key] = get_class($value).'::'.$value->GetKey();
}
}
foreach ($actual as $key => $value) {
if ($value instanceof \DBObject) {
$actual[$key] = get_class($value).'::'.$value->GetKey();
}
}
}
parent::assertEquals($expected, $actual, $message);
}
public function testAddMagicPlaceholdersWhenLoggedInUserHasAContact()
{
// Create data fixture => User + Person
$iNum = uniqid();
$sLogin = "AddMagicPlaceholders".$iNum;
$this->CreateTestOrganization();
$oPerson = $this->CreatePerson($iNum);
$sContactId = $oPerson->GetKey();
$oUser = $this->CreateUser($sLogin, 1, "Abcdef@12345678", $oPerson->GetKey());
UserRights::Login($sLogin);
// Test legacy behavior (no expected args)
$aPlaceholders = MetaModel::AddMagicPlaceholders(['gabu' => "zomeu"]);
$this->assertEqualsShallow(
[
'gabu' => 'zomeu',
'current_contact_id' => $sContactId,
'current_user->object()' => $oUser,
'current_contact->object()' => $oPerson,
],
$aPlaceholders,
'AddMagicPlaceholders without second parameter (legacy) should add "curent_contact_id/current_user->object()/current_contact->object()"'
);
// With expected arguments explicitly given as "none"
$aExpectedArgs = [];
$aPlaceholders = MetaModel::AddMagicPlaceholders(['gabu' => "zomeu"], $aExpectedArgs);
$this->assertEqualsShallow(
[
'gabu' => 'zomeu',
],
$aPlaceholders,
'AddMagicPlaceholders should add only expected arguments'
);
// Test new behavior (with expected args)
$aExpectedArgs = [
new VariableExpression('current_user->login'),
new VariableExpression('current_user->not_existing_attribute'),
new VariableExpression('current_contact_id'),
new VariableExpression('current_contact->org_id'),
new VariableExpression('current_contact->not_existing_attribute'),
];
$aPlaceholders = MetaModel::AddMagicPlaceholders(['gabu' => "zomeu"], $aExpectedArgs);
$this->assertEqualsShallow(
[
'gabu' => 'zomeu',
'current_contact_id' => $sContactId,
'current_user->object()' => $oUser,
'current_user->not_existing_attribute' => '(current_user->not_existing_attribute : cannot be resolved)',
'current_user->login' => $sLogin,
'current_contact->object()' => $oPerson,
'current_contact->org_id' => $oPerson->Get('org_id'),
'current_contact->not_existing_attribute' => '(current_contact->not_existing_attribute : cannot be resolved)',
],
$aPlaceholders,
'AddMagicPlaceholders should add expected arguments and render them with an explicit error when the information could not be known'
);
}
public function testAddMagicPlaceholdersWhenLoggedInUserHasNoContact()
{
// Create data fixture => User without contact
$iNum = uniqid();
$sLogin = "AddMagicPlaceholders".$iNum;
$oUser = $this->CreateContactlessUser($sLogin, 1, "Abcdef@12345678");
UserRights::Login($sLogin);
// Test legacy behavior (no expected args)
$aPlaceholders = MetaModel::AddMagicPlaceholders(['gabu' => "zomeu"]);
$this->assertEqualsShallow(
[
'gabu' => 'zomeu',
'current_contact_id' => 0,
'current_user->object()' => $oUser,
],
$aPlaceholders,
'AddMagicPlaceholders without second parameter (legacy) should add "current_contact_id=0/current_user->object()"'
);
// Test with expected arguments explicitly given as "none"
$aExpectedArgs = [];
$aPlaceholders = MetaModel::AddMagicPlaceholders(['gabu' => "zomeu"], $aExpectedArgs);
$this->assertEqualsShallow(
[
'gabu' => 'zomeu',
],
$aPlaceholders,
'AddMagicPlaceholders should add only expected arguments'
);
// Test with a few expected arguments, some of which being invalid attributes
$aExpectedArgs = [
new VariableExpression('current_user->login'),
new VariableExpression('current_user->not_existing_attribute'),
new VariableExpression('current_contact_id'),
new VariableExpression('current_contact->org_id'),
new VariableExpression('current_contact->not_existing_attribute'),
];
$aPlaceholders = MetaModel::AddMagicPlaceholders(['gabu' => "zomeu"], $aExpectedArgs);
$this->assertEqualsShallow(
[
'gabu' => 'zomeu',
'current_contact_id' => 0,
'current_user->object()' => $oUser,
'current_user->not_existing_attribute' => '(current_user->not_existing_attribute : cannot be resolved)',
'current_user->login' => $sLogin,
'current_contact->object()' => '(current_contact->object() : cannot be resolved)',
'current_contact->org_id' => '(current_contact->org_id : cannot be resolved)',
'current_contact->not_existing_attribute' => '(current_contact->not_existing_attribute : cannot be resolved)',
],
$aPlaceholders,
'AddMagicPlaceholders should add expected arguments and render them with an explicit error when the information could not be known'
);
}
public function testAddMagicPlaceholdersWhenThereIsNoLoggedInUser()
{
// Test legacy behavior (no expected args)
$aPlaceholders = MetaModel::AddMagicPlaceholders(['gabu' => "zomeu"]);
$this->assertEqualsShallow(
[
'gabu' => 'zomeu',
'current_contact_id' => '',
],
$aPlaceholders,
'AddMagicPlaceholders without second parameter (legacy) should add "curent_contact_id"'
);
// Test with expected arguments explicitly given as "none"
$aExpectedArgs = [];
$aPlaceholders = MetaModel::AddMagicPlaceholders(['gabu' => "zomeu"], $aExpectedArgs);
$this->assertEqualsShallow(
[
'gabu' => 'zomeu',
],
$aPlaceholders,
'AddMagicPlaceholders should add only expected arguments'
);
// Test with a few expected arguments, some of which being invalid attributes
$aExpectedArgs = [
new VariableExpression('current_user->login'),
new VariableExpression('current_user->not_existing_attribute'),
new VariableExpression('current_contact_id'),
new VariableExpression('current_contact->org_id'),
new VariableExpression('current_contact->not_existing_attribute'),
];
$aPlaceholders = MetaModel::AddMagicPlaceholders(['gabu' => "zomeu"], $aExpectedArgs);
$this->assertEqualsShallow(
[
'gabu' => 'zomeu',
'current_contact_id' => '',
'current_user->object()' => '(current_user->object() : cannot be resolved)',
'current_user->not_existing_attribute' => '(current_user->not_existing_attribute : cannot be resolved)',
'current_user->login' => '(current_user->login : cannot be resolved)',
'current_contact->object()' => '(current_contact->object() : cannot be resolved)',
'current_contact->org_id' => '(current_contact->org_id : cannot be resolved)',
'current_contact->not_existing_attribute' => '(current_contact->not_existing_attribute : cannot be resolved)',
],
$aPlaceholders,
'AddMagicPlaceholders should add expected arguments and render them with an explicit error when the information could not be known'
);
}
}

View File

@@ -109,7 +109,6 @@ class UserRightsTest extends ItopDataTestCase
}
$this->assertEquals($bResult, UserRights::Login($sLogin));
$this->assertEquals($bResult, UserRights::IsLoggedIn());
UserRights::Logoff();
}
public function LoginProvider(): array
@@ -164,7 +163,6 @@ class UserRightsTest extends ItopDataTestCase
$this->CreateUniqueUserAndLogin('test1', $iProfileId);
$bRes = UserRights::IsActionAllowed($aClassActionResult['class'], $aClassActionResult['action']) == UR_ALLOWED_YES;
$this->assertEquals($aClassActionResult['res'], $bRes);
UserRights::Logoff();
}
/*
@@ -245,7 +243,6 @@ class UserRightsTest extends ItopDataTestCase
$sClass = $aClassActionResult['class'];
$bRes = UserRights::IsActionAllowedOnAttribute($sClass, self::$aClasses[$sClass]['attcode'], $aClassActionResult['action']) == UR_ALLOWED_YES;
$this->assertEquals($aClassActionResult['res'], $bRes);
UserRights::Logoff();
}
/*
@@ -299,10 +296,6 @@ class UserRightsTest extends ItopDataTestCase
$this->fail('Profile should not be added');
} catch (CoreCannotSaveObjectException $e) {
}
// logout
$_SESSION = [];
UserRights::Logoff();
}
public function ProfileDenyingConsoleProvider(): array
@@ -329,10 +322,6 @@ class UserRightsTest extends ItopDataTestCase
$this->fail('User should not modify self');
} catch (CoreException $e) {
}
// logout
$_SESSION = [];
UserRights::Logoff();
}
public function ProfileCannotModifySelfProvider(): array
@@ -359,10 +348,6 @@ class UserRightsTest extends ItopDataTestCase
$this->fail('Current User cannot be deleted');
} catch (DeleteException $e) {
}
// logout
$_SESSION = [];
UserRights::Logoff();
}
public function DeletingSelfUserProvider(): array
@@ -394,8 +379,6 @@ class UserRightsTest extends ItopDataTestCase
$this->fail('Current User cannot remove his own contact');
} catch (CoreCannotSaveObjectException $e) {
}
UserRights::Logoff();
}
public function RemovingOwnContactProvider(): array
@@ -423,10 +406,6 @@ class UserRightsTest extends ItopDataTestCase
} catch (CoreCannotSaveObjectException $e) {
} catch (CoreException $e) {
}
// logout
$_SESSION = [];
UserRights::Logoff();
}
/**
@@ -453,10 +432,6 @@ class UserRightsTest extends ItopDataTestCase
$this->fail('Should not be able to deny User modifications');
} catch (CoreCannotSaveObjectException $e) {
}
// logout
$_SESSION = [];
UserRights::Logoff();
}
/**
@@ -477,10 +452,6 @@ class UserRightsTest extends ItopDataTestCase
$oSearch = DBObjectSearch::FromOQL('SELECT URP_Profiles JOIN URP_UserProfile ON URP_UserProfile.profileid = URP_Profiles.id WHERE URP_UserProfile.userid='.$oUser->GetKey());
$oSet = new DBObjectSet($oSearch);
$this->assertEquals(1, $oSet->Count());
// logout
$_SESSION = [];
UserRights::Logoff();
}
public function NonAdminCanListOwnProfilesProvider(): array
@@ -508,10 +479,6 @@ class UserRightsTest extends ItopDataTestCase
$oSearch = DBObjectSearch::FromOQL('SELECT URP_Profiles JOIN URP_UserProfile ON URP_UserProfile.profileid = URP_Profiles.id WHERE URP_UserProfile.userid='.$oUserAdmin->GetKey());
$oSet = new DBObjectSet($oSearch);
$this->assertEquals($iExpectedCount, $oSet->Count());
// logout
$_SESSION = [];
UserRights::Logoff();
}
public function NonAdminCannotListAdminProfilesProvider(): array

View File

@@ -0,0 +1,142 @@
<?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\ItopDataTestCase;
use ormDocument;
/**
* Tests of the ormDocument class
*/
class ormDocumentTest extends ItopDataTestCase
{
/**
* @inheritDoc
*/
protected function LoadRequiredItopFiles(): void
{
parent::LoadRequiredItopFiles();
$this->RequireOnceItopFile('core/ormdocument.class.inc.php');
}
/**
* @param array $aDocAData
* @param array $aDocBData
* @param bool $bExpectedResult
*
* @dataProvider EqualsExceptDownloadsCountProvider
*/
public function testEqualsExceptDownloadsCount(array $aDocAData, array $aDocBData, bool $bExpectedResult)
{
$oDocA = new ormDocument(base64_decode($aDocAData[0]), $aDocAData[1], $aDocAData[2], $aDocAData[3]);
$oDocB = new ormDocument(base64_decode($aDocBData[0]), $aDocBData[1], $aDocBData[2], $aDocBData[3]);
$bTestedResult = $oDocA->EqualsExceptDownloadsCount($oDocB);
$this->assertSame($bExpectedResult, $bTestedResult);
}
public function EqualsExceptDownloadsCountProvider(): array
{
$sFirstDummyTextFileContentBase64 = "Rmlyc3Q=";
$sSecondDummyTextFileContentBase64 = "U2Vjb25k";
return [
'Total different files' => [
[
$sFirstDummyTextFileContentBase64,
"text/plain",
"a.txt",
0
],
[
$sSecondDummyTextFileContentBase64,
"image/png",
"b.png",
1
],
false,
],
'Different data only' => [
[
$sFirstDummyTextFileContentBase64,
"text/plain",
"a.txt",
0
],
[
$sSecondDummyTextFileContentBase64,
"text/plain",
"a.txt",
0
],
false,
],
'Different mime types only' => [
[
$sFirstDummyTextFileContentBase64,
"text/plain",
"a.txt",
0
],
[
$sFirstDummyTextFileContentBase64,
"image/png",
"a.txt",
0
],
false,
],
'Different file names only' => [
[
$sFirstDummyTextFileContentBase64,
"text/plain",
"a.txt",
0
],
[
$sFirstDummyTextFileContentBase64,
"text/plain",
"b.txt",
0
],
false,
],
'Different download counts only' => [
[
$sFirstDummyTextFileContentBase64,
"text/plain",
"a.txt",
0
],
[
$sFirstDummyTextFileContentBase64,
"text/plain",
"a.txt",
1
],
true,
],
'Identical files, different object instances' => [
[
$sFirstDummyTextFileContentBase64,
"text/plain",
"a.txt",
0
],
[
$sFirstDummyTextFileContentBase64,
"text/plain",
"a.txt",
0
],
false,
],
];
}
}