mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-12 23:14:18 +01:00
Merge remote-tracking branch 'origin/support/3.2' into develop
This commit is contained in:
@@ -82,7 +82,7 @@ class LoginWebPage extends NiceWebPage
|
||||
}
|
||||
|
||||
protected static $m_sLoginFailedMessage = '';
|
||||
|
||||
|
||||
public function __construct($sTitle = null)
|
||||
{
|
||||
if ($sTitle === null) {
|
||||
@@ -101,6 +101,15 @@ class LoginWebPage extends NiceWebPage
|
||||
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-awesome/css/all.min.css');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected function GetFaviconAbsoluteUrl()
|
||||
{
|
||||
return Branding::GetLoginFavIconAbsoluteUrl();
|
||||
}
|
||||
|
||||
public static function SetLoginFailedMessage($sMessage)
|
||||
{
|
||||
self::$m_sLoginFailedMessage = $sMessage;
|
||||
|
||||
@@ -41,12 +41,6 @@ require_once(APPROOT.'/core/email.class.inc.php');
|
||||
*/
|
||||
abstract class Action extends cmdbAbstractObject
|
||||
{
|
||||
/**
|
||||
* @var $oCallingTrigger Trigger|null The trigger that called this action {@see DoExecute}
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected ?Trigger $oCallingTrigger = null;
|
||||
|
||||
/**
|
||||
* @throws \CoreException
|
||||
* @throws \Exception
|
||||
@@ -276,14 +270,6 @@ abstract class ActionNotification extends Action
|
||||
// MetaModel::Init_SetZListItems('default_search', array('name'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function DoExecute($oTrigger, $aContextArgs)
|
||||
{
|
||||
$this->oCallingTrigger = $oTrigger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sLanguage
|
||||
* @param $sLanguageCode
|
||||
@@ -447,7 +433,7 @@ class ActionEmail extends ActionNotification
|
||||
*/
|
||||
protected function FindRecipients($sRecipAttCode, $aArgs)
|
||||
{
|
||||
$oTrigger = $this->oCallingTrigger;
|
||||
$oTrigger = $aArgs['trigger->object()'] ?? null;
|
||||
$sOQL = $this->Get($sRecipAttCode);
|
||||
if (utils::IsNullOrEmptyString($sOQL)) return '';
|
||||
|
||||
@@ -523,7 +509,6 @@ class ActionEmail extends ActionNotification
|
||||
*/
|
||||
public function DoExecute($oTrigger, $aContextArgs)
|
||||
{
|
||||
parent::DoExecute($oTrigger, $aContextArgs);
|
||||
if (MetaModel::IsLogEnabledNotification())
|
||||
{
|
||||
$oLog = new EventNotificationEmail();
|
||||
|
||||
@@ -447,14 +447,14 @@ class Config
|
||||
'show_in_conf_sample' => true,
|
||||
],
|
||||
'export_pdf_font' => [ // @since 2.7.0 PR #49 / N°1947
|
||||
'type' => 'string',
|
||||
'description' => 'Font used when generating a PDF file',
|
||||
'default' => 'DejaVuSans', // DejaVuSans is a UTF-8 Unicode font, embedded in the TCPPDF lib we're using
|
||||
// Standard PDF fonts like helvetica or times newroman are NOT Unicode
|
||||
// A new DroidSansFallback can be used to improve CJK support (se PR #49)
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
'type' => 'string',
|
||||
'description' => 'Font used when generating a PDF file',
|
||||
'default' => 'DejaVuSans', // DejaVuSans is a UTF-8 Unicode font, embedded in the TCPPDF lib we're using
|
||||
// Standard PDF fonts like helvetica or times newroman are NOT Unicode
|
||||
// A new DroidSansFallback can be used to improve CJK support (se PR #49)
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'access_mode' => [
|
||||
'type' => 'integer',
|
||||
@@ -1119,6 +1119,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'purge_data.max_chunk_size' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Maximum number of items deleted per loop. Used in function MetaModel::PurgeData',
|
||||
'default' => 1000,
|
||||
'value' => 1000,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'max_history_length' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Maximum length of the history table (in the "History" tab on each object) before it gets truncated. Latest modifications are displayed first.',
|
||||
@@ -1324,9 +1332,9 @@ class Config
|
||||
'draft_attachments_lifetime' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Lifetime (in seconds) of drafts\' attachments and inline images: after this duration, the garbage collector will delete them.',
|
||||
'default' => 86400,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'default' => 86400,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'date_and_time_format' => [
|
||||
@@ -1882,6 +1890,7 @@ class Config
|
||||
* @var integer Number of seconds between two reloads of the display (standard)
|
||||
*/
|
||||
protected $m_iStandardReloadInterval;
|
||||
|
||||
/**
|
||||
* @var integer Number of seconds between two reloads of the display (fast)
|
||||
*/
|
||||
@@ -2553,9 +2562,9 @@ class Config
|
||||
|
||||
// Old fashioned integer settings
|
||||
$aIntValues = array(
|
||||
'fast_reload_interval' => $this->m_iFastReloadInterval,
|
||||
'max_display_limit' => $this->m_iMaxDisplayLimit,
|
||||
'min_display_limit' => $this->m_iMinDisplayLimit,
|
||||
'fast_reload_interval' => $this->m_iFastReloadInterval,
|
||||
'max_display_limit' => $this->m_iMaxDisplayLimit,
|
||||
'min_display_limit' => $this->m_iMinDisplayLimit,
|
||||
'standard_reload_interval' => $this->m_iStandardReloadInterval,
|
||||
);
|
||||
foreach ($aIntValues as $sKey => $iValue)
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<attributes>
|
||||
<attribute id="title"/>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
</naming>
|
||||
<style>
|
||||
@@ -390,7 +390,18 @@
|
||||
<is_null_allowed>false</is_null_allowed>
|
||||
</field>
|
||||
</fields>
|
||||
<presentation/>
|
||||
<presentation>
|
||||
<summary>
|
||||
<items>
|
||||
<item id="date">
|
||||
<rank>10</rank>
|
||||
</item>
|
||||
<item id="message">
|
||||
<rank>20</rank>
|
||||
</item>
|
||||
</items>
|
||||
</summary>
|
||||
</presentation>
|
||||
<methods/>
|
||||
</class>
|
||||
</classes>
|
||||
|
||||
@@ -2311,7 +2311,6 @@ abstract class DBObject implements iDisplay
|
||||
protected function HasObjectsInDbForUniquenessRule($sUniquenessRuleId, $aUniquenessRuleProperties)
|
||||
{
|
||||
$oUniquenessQuery = $this->GetSearchForUniquenessRule($sUniquenessRuleId, $aUniquenessRuleProperties);
|
||||
$oUniquenessQuery->AllowAllData();
|
||||
$oUniquenessDuplicates = new DBObjectSet($oUniquenessQuery);
|
||||
$bHasDuplicates = $oUniquenessDuplicates->CountExceeds(0);
|
||||
|
||||
@@ -2357,6 +2356,8 @@ abstract class DBObject implements iDisplay
|
||||
$oUniquenessQuery->AddConditionForInOperatorUsingParam('finalclass', $aChildClassesWithRuleDisabled, false);
|
||||
}
|
||||
|
||||
$oUniquenessQuery->AllowAllData();
|
||||
|
||||
return $oUniquenessQuery;
|
||||
}
|
||||
|
||||
|
||||
@@ -7168,32 +7168,45 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function PurgeData($oFilter)
|
||||
{
|
||||
$iMaxChunkSize = MetaModel::GetConfig()->Get('purge_data.max_chunk_size');
|
||||
$sTargetClass = $oFilter->GetClass();
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
$oSet->OptimizeColumnLoad(array($sTargetClass => array('finalclass')));
|
||||
$aIdToClass = $oSet->GetColumnAsArray('finalclass', true);
|
||||
$iNbIdsDeleted = 0;
|
||||
$bExecuteQuery = true;
|
||||
|
||||
$aIds = array_keys($aIdToClass);
|
||||
if (count($aIds) > 0)
|
||||
{
|
||||
$aQuotedIds = CMDBSource::Quote($aIds);
|
||||
$sIdList = implode(',', $aQuotedIds);
|
||||
$aTargetClasses = array_merge(
|
||||
self::EnumChildClasses($sTargetClass, ENUM_CHILD_CLASSES_ALL),
|
||||
self::EnumParentClasses($sTargetClass, ENUM_PARENT_CLASSES_EXCLUDELEAF)
|
||||
);
|
||||
foreach($aTargetClasses as $sSomeClass)
|
||||
{
|
||||
$sTable = MetaModel::DBGetTable($sSomeClass);
|
||||
$sPKField = MetaModel::DBGetKey($sSomeClass);
|
||||
// This loop allows you to delete objects in batches of $iMaxChunkSize elements
|
||||
while ($bExecuteQuery) {
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
$oSet->SetLimit($iMaxChunkSize);
|
||||
$oSet->OptimizeColumnLoad(array($sTargetClass => array('finalclass')));
|
||||
$aIdToClass = $oSet->GetColumnAsArray('finalclass', true);
|
||||
|
||||
$sDeleteSQL = "DELETE FROM `$sTable` WHERE `$sPKField` IN ($sIdList)";
|
||||
CMDBSource::DeleteFrom($sDeleteSQL);
|
||||
$aIds = array_keys($aIdToClass);
|
||||
$iNbIds = count($aIds);
|
||||
if ($iNbIds > 0) {
|
||||
$aQuotedIds = CMDBSource::Quote($aIds);
|
||||
$sIdList = implode(',', $aQuotedIds);
|
||||
$aTargetClasses = array_merge(
|
||||
self::EnumChildClasses($sTargetClass, ENUM_CHILD_CLASSES_ALL),
|
||||
self::EnumParentClasses($sTargetClass, ENUM_PARENT_CLASSES_EXCLUDELEAF)
|
||||
);
|
||||
foreach ($aTargetClasses as $sSomeClass) {
|
||||
$sTable = MetaModel::DBGetTable($sSomeClass);
|
||||
$sPKField = MetaModel::DBGetKey($sSomeClass);
|
||||
|
||||
$sDeleteSQL = "DELETE FROM `$sTable` WHERE `$sPKField` IN ($sIdList)";
|
||||
CMDBSource::DeleteFrom($sDeleteSQL);
|
||||
}
|
||||
$iNbIdsDeleted += $iNbIds;
|
||||
}
|
||||
|
||||
// stop loop if query returned fewer objects than $iMaxChunkSize. In this case, all objects have been deleted.
|
||||
if ($iNbIds < $iMaxChunkSize) {
|
||||
$bExecuteQuery = false;
|
||||
}
|
||||
}
|
||||
return count($aIds);
|
||||
}
|
||||
|
||||
return $iNbIdsDeleted;
|
||||
}
|
||||
// Links
|
||||
//
|
||||
//
|
||||
|
||||
@@ -112,6 +112,8 @@ abstract class Trigger extends cmdbAbstractObject
|
||||
return;
|
||||
}
|
||||
|
||||
$aContextArgs['trigger->object()'] = $this;
|
||||
|
||||
// Find the related actions
|
||||
$oLinkedActions = $this->Get('action_list');
|
||||
|
||||
@@ -121,7 +123,7 @@ abstract class Trigger extends cmdbAbstractObject
|
||||
$aActionListOrdered[(int) $oLink->Get('order')][] = $oLink;
|
||||
}
|
||||
ksort($aActionListOrdered);
|
||||
|
||||
|
||||
// Execute actions
|
||||
foreach ($aActionListOrdered as $aActionSubList) {
|
||||
foreach ($aActionSubList as $oLink) /** @var \DBObject $oLink */ {
|
||||
@@ -131,6 +133,7 @@ abstract class Trigger extends cmdbAbstractObject
|
||||
$oAction = MetaModel::GetObject('Action', $iActionId);
|
||||
if ($oAction->IsActive()) {
|
||||
$oKPI = new ExecutionKPI();
|
||||
$aContextArgs['action->object()'] = $oAction;
|
||||
$oAction->DoExecute($this, $aContextArgs);
|
||||
$oKPI->ComputeStatsForExtension($oAction, 'DoExecute');
|
||||
}
|
||||
|
||||
@@ -49,3 +49,63 @@
|
||||
.ibo-navigation-menu.ibo-is-active .ibo-navigation-menu--drawer{
|
||||
transform: translate3d(0,0,0);
|
||||
}
|
||||
|
||||
// Toggler legacy CSS that has somehow been added to iTop 3.0 and that is now used by some extensions
|
||||
// Round Toggle
|
||||
/* The switch - the box around the slider */
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 36px;
|
||||
height: 20px;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/* Hide default HTML checkbox */
|
||||
.switch input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* The slider */
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: $ibo-color-secondary-600;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
left: 3px;
|
||||
bottom: 3px;
|
||||
background-color: $ibo-color-secondary-300;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: $ibo-color-primary-600;
|
||||
}
|
||||
|
||||
input:focus + .slider {
|
||||
box-shadow: 0 0 1px $ibo-color-primary-600;
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
transform: translateX(14.5px);
|
||||
}
|
||||
|
||||
/* Rounded sliders */
|
||||
.slider.round {
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.slider.round:before {
|
||||
border-radius: 7px;
|
||||
}
|
||||
|
||||
@@ -85,6 +85,9 @@ $ibo-panel--collapsible-toggler--margin-right: $ibo-spacing-300 !default;
|
||||
$ibo-panel--collapsible-toggler--font-size: $ibo-font-size-250 !default;
|
||||
$ibo-panel--collapsible-toggler--color: $ibo-color-grey-700 !default;
|
||||
|
||||
$ibo-panel--is-selectable--body--after--z-index: $ibo-panel--header--z-index + 1 !default;
|
||||
$ibo-panel--is-selectable--body--after--font-size: $ibo-font-size-700 !default;
|
||||
|
||||
/* Rules */
|
||||
.ibo-panel {
|
||||
--ibo-main-color: #{map-get($ibo-panel-colors, 'neutral')}; /* --ibo-main-color is to allow overload from custom dynamic value from the DM. The overload will be done through an additional CSS class of a particular DM class or DM attribute */
|
||||
@@ -128,6 +131,30 @@ $ibo-panel--collapsible-toggler--color: $ibo-color-grey-700 !default;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.ibo-is-selectable .ibo-panel--body::after {
|
||||
@include ibo-selectable;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: $ibo-panel--is-selectable--body--after--z-index;
|
||||
font-size: $ibo-panel--is-selectable--body--after--font-size;
|
||||
}
|
||||
&.ibo-is-selectable:hover .ibo-panel--body::after {
|
||||
@include ibo-selectable-hover;
|
||||
display: flex;
|
||||
}
|
||||
&.ibo-is-selected .ibo-panel--body::after {
|
||||
@include ibo-selected;
|
||||
display: flex;
|
||||
}
|
||||
&.ibo-is-selected:hover .ibo-panel--body::after {
|
||||
@include ibo-selected-hover;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-panel--header {
|
||||
|
||||
@@ -16,3 +16,4 @@
|
||||
@import "input-one-way-password";
|
||||
@import "input-set";
|
||||
@import "input-text";
|
||||
@import "input-toggler";
|
||||
|
||||
72
css/backoffice/components/input/_input-toggler.scss
Normal file
72
css/backoffice/components/input/_input-toggler.scss
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-toggler--wrapper--width: 36px !default;
|
||||
$ibo-toggler--wrapper--height: 20px !default;
|
||||
|
||||
$ibo-toggler--slider--border-radius: $ibo-border-radius-900 !default;
|
||||
$ibo-toggler--slider--background-color: $ibo-color-secondary-600 !default;
|
||||
|
||||
$ibo-toggler--slider--before--height: 15px !default;
|
||||
$ibo-toggler--slider--before--width: 15px !default;
|
||||
$ibo-toggler--slider--before--border-radius: $ibo-border-radius-full !default;
|
||||
$ibo-toggler--slider--before--background-color: $ibo-color-grey-100 !default;
|
||||
|
||||
$ibo-toggler--slider--checked--background-color: $ibo-color-primary-600 !default;
|
||||
$ibo-toggler--slider--focus--box-shadow: 0 0 1px $ibo-color-primary-600 !default;
|
||||
|
||||
$ibo-toggler--label--margin-left: 4px !default;
|
||||
|
||||
|
||||
.ibo-toggler--wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: $ibo-toggler--wrapper--width;
|
||||
height: $ibo-toggler--wrapper--height;
|
||||
vertical-align: baseline;
|
||||
.ibo-toggler {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-toggler--slider{
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border-radius: $ibo-toggler--slider--border-radius;
|
||||
background-color: $ibo-toggler--slider--background-color;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
.ibo-toggler--slider:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 3px;
|
||||
bottom: 3px;
|
||||
height: $ibo-toggler--slider--before--height;
|
||||
width: $ibo-toggler--slider--before--width;
|
||||
border-radius: $ibo-toggler--slider--before--border-radius;
|
||||
background-color: $ibo-toggler--slider--before--background-color;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
.ibo-toggler--wrapper input:checked + .ibo-toggler--slider {
|
||||
background-color: $ibo-toggler--slider--checked--background-color;
|
||||
}
|
||||
|
||||
input:focus + .ibo-toggler--slider {
|
||||
box-shadow: $ibo-toggler--slider--focus--box-shadow;
|
||||
}
|
||||
|
||||
input:checked + .ibo-toggler--slider:before {
|
||||
transform: translateX(14.5px);
|
||||
}
|
||||
|
||||
label ~ .ibo-toggler--wrapper {
|
||||
margin-left: $ibo-toggler--label--margin-left;
|
||||
}
|
||||
@@ -62,63 +62,4 @@ $ibo-top-bar--toolbar-dashboard-title--max-width: 350px !default;
|
||||
@extend %ibo-full-height-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
// Round Toggle
|
||||
/* The switch - the box around the slider */
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 36px;
|
||||
height: 20px;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/* Hide default HTML checkbox */
|
||||
.switch input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* The slider */
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: $ibo-color-secondary-600;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
left: 3px;
|
||||
bottom: 3px;
|
||||
background-color: $ibo-color-secondary-300;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: $ibo-color-primary-600;
|
||||
}
|
||||
|
||||
input:focus + .slider {
|
||||
box-shadow: 0 0 1px $ibo-color-primary-600;
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
transform: translateX(14.5px);
|
||||
}
|
||||
|
||||
/* Rounded sliders */
|
||||
.slider.round {
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.slider.round:before {
|
||||
border-radius: 7px;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,8 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-object-summary--header--margin-y: $ibo-panel--highlight--height!default;
|
||||
$ibo-object-summary--header--margin-top: $ibo-panel--highlight--height!default;
|
||||
$ibo-object-summary--header--margin-bottom: $ibo-spacing-0!default;
|
||||
$ibo-object-summary--header--margin-x: $ibo-spacing-0 !default;
|
||||
|
||||
$ibo-object-summary--header--padding-y: $ibo-spacing-300 !default;
|
||||
@@ -51,7 +52,7 @@ $ibo-object-summary--content--attributes--code--padding-right: $ibo-spacing-500
|
||||
}
|
||||
|
||||
.ibo-object-summary--header{
|
||||
margin: $ibo-object-summary--header--margin-y $ibo-object-summary--header--margin-x;
|
||||
margin: $ibo-object-summary--header--margin-top $ibo-object-summary--header--margin-x $ibo-object-summary--header--margin-bottom $ibo-object-summary--header--margin-x;
|
||||
padding: $ibo-object-summary--header--padding-y $ibo-object-summary--header--padding-x;
|
||||
background-color: $ibo-object-summary--header--background-color;
|
||||
border-bottom: $ibo-object-summary--header--border;
|
||||
|
||||
@@ -16,4 +16,5 @@
|
||||
@import "run-query";
|
||||
@import "welcome-popup";
|
||||
@import "oauth.wizard";
|
||||
@import "notifications";
|
||||
@import "notifications-center";
|
||||
66
css/backoffice/pages/_notifications.scss
Normal file
66
css/backoffice/pages/_notifications.scss
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-notifications--view-all--container--grid-gap: $ibo-spacing-600 !default;
|
||||
$ibo-notifications--view-all--container--object-summary--panel--body--max-height: unset !default;
|
||||
|
||||
$ibo-notifications--view-all--item--unread--highlight--background-color: $ibo-color-red-600 !default;
|
||||
$ibo-notifications--view-all--item--read--highlight--background-color: $ibo-color-grey-200 !default;
|
||||
|
||||
$ibo-notifications--view-all--container--large--grid-template-columns: repeat(3, 1fr) !default;
|
||||
$ibo-notifications--view-all--container--medium--grid-template-columns: repeat(2, 1fr) !default;
|
||||
$ibo-notifications--view-all--container--small--grid-template-columns: repeat(1, 1fr) !default;
|
||||
|
||||
$ibo-notifications--view-all--empty--margin-top: $ibo-spacing-950 !default;
|
||||
$ibo-notifications--view-all--empty--svg--max-width: 30% !default;
|
||||
|
||||
.ibo-notifications--view-all--container{
|
||||
display: grid;
|
||||
grid-gap: $ibo-notifications--view-all--container--grid-gap;
|
||||
.ibo-object-summary .ibo-panel--title{
|
||||
font-size: $ibo-font-size-250;
|
||||
}
|
||||
.ibo-object-summary > .ibo-panel--body{
|
||||
box-shadow: none;
|
||||
max-height: $ibo-notifications--view-all--container--object-summary--panel--body--max-height;
|
||||
}
|
||||
.ibo-object-summary + .ibo-object-summary{
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
@include mobile {
|
||||
grid-template-columns: $ibo-notifications--view-all--container--small--grid-template-columns;
|
||||
}
|
||||
@include desktop {
|
||||
grid-template-columns: $ibo-notifications--view-all--container--medium--grid-template-columns;
|
||||
}
|
||||
@include fullhd {
|
||||
grid-template-columns: $ibo-notifications--view-all--container--large--grid-template-columns; }
|
||||
}
|
||||
.ibo-notifications--view-all--toolbar {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.ibo-notifications--view-all--toggler {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.ibo-notifications--view-all--item--read .ibo-panel--body::before{
|
||||
background-color: $ibo-notifications--view-all--item--read--highlight--background-color;
|
||||
}
|
||||
.ibo-notifications--view-all--item--unread .ibo-panel--body::before{
|
||||
background-color: $ibo-notifications--view-all--item--unread--highlight--background-color;
|
||||
}
|
||||
|
||||
.ibo-notifications--view-all--empty {
|
||||
@extend %ibo-fully-centered-content;
|
||||
flex-direction: column;
|
||||
margin-top: $ibo-notifications--view-all--empty--margin-top;
|
||||
|
||||
svg {
|
||||
max-width: $ibo-notifications--view-all--empty--svg--max-width;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,5 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "highlight";
|
||||
@import "highlight";
|
||||
@import "selectable";
|
||||
40
css/backoffice/utils/mixins/_selectable.scss
Normal file
40
css/backoffice/utils/mixins/_selectable.scss
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-selectable--background-color: transparent !default;
|
||||
|
||||
$ibo-selectable--hover--color: $ibo-color-grey-100 !default;
|
||||
$ibo-selectable--hover--background-color: $ibo-color-grey-600 !default;
|
||||
$ibo-selectable--hover--background-opacity: 0.6 !default;
|
||||
|
||||
$ibo-selected--color: $ibo-color-grey-100 !default;
|
||||
$ibo-selected--background-color: $ibo-color-grey-900 !default;
|
||||
$ibo-selected--background-opacity: 0.5 !default;
|
||||
|
||||
$ibo-selected--hover--background-color: $ibo-color-grey-700 !default;
|
||||
$ibo-selected--hover--background-opacity: 0.5 !default;
|
||||
@mixin ibo-selectable {
|
||||
content: ' ';
|
||||
@extend %fa-solid-base;
|
||||
background-color: $ibo-selectable--background-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
@mixin ibo-selectable-hover {
|
||||
@extend %fa-regular-base;
|
||||
content: '\f058';
|
||||
color: $ibo-selectable--hover--color;
|
||||
background-color: transparentize($ibo-selectable--hover--background-color, $ibo-selectable--hover--background-opacity);
|
||||
}
|
||||
|
||||
@mixin ibo-selected {
|
||||
@extend %fa-solid-base;
|
||||
content: '\f058';
|
||||
color: $ibo-selected--color;
|
||||
background-color: transparentize($ibo-selected--background-color, $ibo-selected--background-opacity);
|
||||
}
|
||||
|
||||
@mixin ibo-selected-hover {
|
||||
background-color: transparentize($ibo-selected--hover--background-color, $ibo-selected--hover--background-opacity);
|
||||
}
|
||||
@@ -13,6 +13,7 @@ $ibo-spacing-600: $ibo-size-300 !default;
|
||||
$ibo-spacing-700: $ibo-size-350 !default;
|
||||
$ibo-spacing-800: $ibo-size-400 !default;
|
||||
$ibo-spacing-900: $ibo-size-450 !default;
|
||||
$ibo-spacing-950: $ibo-size-500 !default;
|
||||
|
||||
:root{
|
||||
--ibo-spacing-0: #{$ibo-size-0};
|
||||
|
||||
@@ -15,6 +15,9 @@ $ibo-font-size-400: 2rem !default; /* 24px */
|
||||
$ibo-font-size-450: 2.5rem !default; /* 30px */
|
||||
$ibo-font-size-500: 3rem !default; /* 36px */
|
||||
$ibo-font-size-550: 4rem !default; /* 48px */
|
||||
$ibo-font-size-600: 5rem !default; /* 60px */
|
||||
$ibo-font-size-650: 6rem !default; /* 72px */
|
||||
$ibo-font-size-700: 7rem !default; /* 84px */
|
||||
|
||||
/* Value Common weight name (https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight) */
|
||||
$ibo-font-weight-100: 100 !default; /* 100 Thin (Harline) */
|
||||
|
||||
@@ -52,6 +52,8 @@ class Basic extends AbstractConfiguration
|
||||
$aPortalConf = $this->ParseGlobalProperties($aPortalConf);
|
||||
// - Rectifying portal logo url
|
||||
$aPortalConf = $this->AppendLogoUri($aPortalConf);
|
||||
// - Rectifying portal favicon url
|
||||
$aPortalConf = $this->AppendFavIconUri($aPortalConf);
|
||||
}
|
||||
catch (Exception $oException)
|
||||
{
|
||||
@@ -71,10 +73,11 @@ class Basic extends AbstractConfiguration
|
||||
{
|
||||
$aPortalConf = array(
|
||||
'properties' => array(
|
||||
'id' => $_ENV['PORTAL_ID'],
|
||||
'name' => 'Page:DefaultTitle',
|
||||
'logo' => Branding::GetPortalLogoAbsoluteUrl(),
|
||||
'themes' => array(
|
||||
'id' => $_ENV['PORTAL_ID'],
|
||||
'name' => 'Page:DefaultTitle',
|
||||
'logo' => Branding::GetPortalLogoAbsoluteUrl(),
|
||||
'favicon' => Branding::GetPortalFavIconAbsoluteUrl(),
|
||||
'themes' => array(
|
||||
'bootstrap' => 'itop-portal-base/portal/public/css/bootstrap-theme-combodo.scss',
|
||||
'portal' => 'itop-portal-base/portal/public/css/portal.scss',
|
||||
'others' => array(),
|
||||
@@ -116,11 +119,8 @@ class Basic extends AbstractConfiguration
|
||||
case 'name':
|
||||
case 'urlmaker_class':
|
||||
case 'triggers_query':
|
||||
$aPortalConf['properties'][$oPropertyNode->nodeName] = $oPropertyNode->GetText(
|
||||
$aPortalConf['properties'][$oPropertyNode->nodeName]
|
||||
);
|
||||
break;
|
||||
case 'logo':
|
||||
case 'favicon':
|
||||
$aPortalConf['properties'][$oPropertyNode->nodeName] = $oPropertyNode->GetText(
|
||||
$aPortalConf['properties'][$oPropertyNode->nodeName]
|
||||
);
|
||||
@@ -263,8 +263,7 @@ class Basic extends AbstractConfiguration
|
||||
private function AppendLogoUri(array $aPortalConf)
|
||||
{
|
||||
$sLogoUri = $aPortalConf['properties']['logo'];
|
||||
if (!preg_match('/^http/', $sLogoUri))
|
||||
{
|
||||
if (!preg_match('/^http/', $sLogoUri)) {
|
||||
// We prefix it with the server base url
|
||||
$sLogoUri = utils::GetAbsoluteUrlAppRoot().'env-'.utils::GetCurrentEnvironment().'/'.$sLogoUri;
|
||||
}
|
||||
@@ -272,4 +271,23 @@ class Basic extends AbstractConfiguration
|
||||
|
||||
return $aPortalConf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aPortalConf
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
* @since 3.2.0 N°3363
|
||||
*/
|
||||
private function AppendFaviconUri(array $aPortalConf)
|
||||
{
|
||||
$sFaviconUri = $aPortalConf['properties']['favicon'];
|
||||
if (!preg_match('/^http/', $sFaviconUri)) {
|
||||
// We prefix it with the server base url
|
||||
$sFaviconUri = utils::GetAbsoluteUrlAppRoot().'env-'.utils::GetCurrentEnvironment().'/'.$sFaviconUri;
|
||||
}
|
||||
$aPortalConf['properties']['favicon'] = $sFaviconUri;
|
||||
|
||||
return $aPortalConf;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@
|
||||
{% block pPageExtraMetas %}
|
||||
{% endblock %}
|
||||
<title>{% block pPageTitle %}{% if sPageTitle is defined and sPageTitle is not null %}{{ sPageTitle }} - {{ constant('ITOP_APPLICATION_SHORT') }}{% else %}{{ 'Page:DefaultTitle'|dict_format(constant('ITOP_APPLICATION_SHORT')) }}{% endif %}{% endblock %}</title>
|
||||
<link rel="shortcut icon" href="{{ app['combodo.absolute_url'] ~ 'images/favicon.ico'|add_itop_version }}" />
|
||||
<link rel="shortcut icon" href="{{ app['combodo.portal.instance.conf'].properties.favicon|add_itop_version }}"/>
|
||||
|
||||
{% block pPageStylesheets %}
|
||||
{# First bootstrap core, lib themes, then bootstrap theme, portal adjustements #}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2013-2023 Combodo SARL
|
||||
* Copyright (C) 2013-2024 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
@@ -19,5 +19,44 @@
|
||||
|
||||
Dict::Add('EN US', 'English', 'English', array(
|
||||
'UI:Newsroom:iTopNotification:Label' => ITOP_APPLICATION_SHORT,
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Title' => ITOP_APPLICATION_SHORT.' notifications',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Title' => 'Your ' . ITOP_APPLICATION_SHORT.' notifications',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Read:Label' => 'Read',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Unread:Label' => 'Unread',
|
||||
'UI:Newsroom:iTopNotification:SelectMode:Label' => 'Select mode',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAllAsRead:Label' => 'Mark all as read',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAllAsUnread:Label' => 'Mark all as unread',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Label' => 'Delete all',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Success:Message' => 'All %1$s notifications have been deleted',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Confirmation:Title' => 'Delete all notifications',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Confirmation:Message' => 'Are you sure you want to delete all notifications?',
|
||||
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Empty:Title' => 'No notification, you are up to date!',
|
||||
|
||||
// Actions
|
||||
// - Unitary buttons
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:Delete:Label' => 'Delete this notification',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:ViewObject:Label' => 'Go to the notification url',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsRead:Label' => 'Mark as read',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsUnread:Label' => 'Mark as unread',
|
||||
// - Bulk buttons
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkSelectedAsRead:Label' => 'Mark selected as read',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkSelectedAsUnread:Label' => 'Mark selected as unread',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Label' => 'Delete selected',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Confirmation:Title' => 'Delete selected notifications',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Confirmation:Message' => 'Are you sure you want to delete selected notifications?',
|
||||
|
||||
// Feedback messages
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:InvalidAction:Message' => 'Invalid action: "%1$s"',
|
||||
// - Mark as read
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsRead:NoEvent:Message' => 'No notification to mark as read',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsRead:Success:Message' => 'The notification has been marked as read',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsReadMultiple:Success:Message' => '%1$s notifications have been marked as read',
|
||||
// - Mark as unread
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsUnread:NoEvent:Message' => 'No notification to mark as read',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsUnread:Success:Message' => 'The notification has been marked as unread',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsUnreadMultiple:Success:Message' => '%1$s notifications have been marked as unread',
|
||||
// Delete
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:Delete:NoEvent:Message' => 'No notification to delete',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:Delete:Success:Message' => 'The notification has been deleted',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteMultiple:Success:Message' => '%1$s notifications have been deleted',
|
||||
));
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2013-2024 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'UI:Newsroom:iTopNotification:Label' => ITOP_APPLICATION_SHORT,
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Title' => 'Vos notifications ' . ITOP_APPLICATION_SHORT,
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Read:Label' => 'Lue',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Unread:Label' => 'Non lue',
|
||||
'UI:Newsroom:iTopNotification:SelectMode:Label' => 'Sélection multiple',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAllAsRead:Label' => 'Marquer tout comme lu',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAllAsUnread:Label' => 'Marquer tout comme non lu',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Label' => 'Supprimer tout',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Success:Message' => '%1$s notifications ont été supprimées',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Confirmation:Title' => 'Supprimer toutes les notifications',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Confirmation:Message' => 'Êtes-vous sûr de vouloir supprimer toutes les notifications ?',
|
||||
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Empty:Title' => 'Aucune notification, vous êtes à jour !',
|
||||
|
||||
// Actions
|
||||
// - Unitary buttons
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:Delete:Label' => 'Supprimer cette notification',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:ViewObject:Label' => 'Aller à l\'url de la notification',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsRead:Label' => 'Marquer comme lu',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsUnread:Label' => 'Marquer comme non lu',
|
||||
// - Bulk buttons
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkSelectedAsRead:Label' => 'Marquer sélectionnée(s) comme lu',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkSelectedAsUnread:Label' => 'Marquer sélectionnée(s) comme non lu',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Label' => 'Supprimer sélectionnée(s)',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Confirmation:Title' => 'Supprimer les notifications sélectionnées',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Confirmation:Message' => 'Êtes-vous sûr de vouloir supprimer les notifications sélectionnées ?',
|
||||
|
||||
// Feedback messages
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:InvalidAction:Message' => 'Action invalide : "%1$s"',
|
||||
// - Mark as read
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsRead:NoEvent:Message' => 'Aucune notification à marquer comme lue',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsRead:Success:Message' => 'La notification a été marquée comme lue',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsReadMultiple:Success:Message' => '%1$s notifications ont été marquées comme lues',
|
||||
// - Mark as unread
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsUnread:NoEvent:Message' => 'Aucune notification à marquer comme non lue',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsUnread:Success:Message' => 'La notification a été marquée comme non lue',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsUnreadMultiple:Success:Message' => '%1$s notifications ont été marquées comme non lues',
|
||||
// Delete
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:Delete:NoEvent:Message' => 'Aucune notification à supprimer',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:Delete:Success:Message' => 'La notification a été supprimée',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteMultiple:Success:Message' => '%1$s notifications ont été supprimées',
|
||||
));
|
||||
49
images/illustrations/undraw_social_serenity.svg
Normal file
49
images/illustrations/undraw_social_serenity.svg
Normal file
@@ -0,0 +1,49 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="870.4" height="598.682" viewBox="0 0 870.4 598.682" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="Group_14" data-name="Group 14" transform="translate(-182.787 -183.377)">
|
||||
<g id="Group_9" data-name="Group 9" transform="translate(-237 346.007)">
|
||||
<path id="Path_141-368" data-name="Path 141" d="M827.279,436.053H539.012a5.345,5.345,0,0,1-5.338-5.338V184.207a5.345,5.345,0,0,1,5.338-5.338H827.279a5.345,5.345,0,0,1,5.338,5.338V430.714a5.344,5.344,0,0,1-5.338,5.338ZM539.012,181a3.207,3.207,0,0,0-3.2,3.2V430.714a3.207,3.207,0,0,0,3.2,3.2H827.279a3.206,3.206,0,0,0,3.2-3.2V184.207a3.207,3.207,0,0,0-3.2-3.2Z" transform="translate(-44.905)" fill="#e6e6e6"/>
|
||||
<circle id="Ellipse_19" data-name="Ellipse 19" cx="21.745" cy="21.745" r="21.745" transform="translate(517.152 370.472)" fill="#e6e6e6"/>
|
||||
<path id="Path_142-369" data-name="Path 142" d="M634.024,377.72a3.624,3.624,0,0,0,0,7.248H804.88a3.624,3.624,0,0,0,0-7.248Z" transform="translate(-44.905)" fill="#e6e6e6"/>
|
||||
<path id="Path_143-370" data-name="Path 143" d="M634.024,399.466a3.624,3.624,0,0,0,0,7.248h73.52a3.624,3.624,0,0,0,0-7.248Z" transform="translate(-44.905)" fill="#e6e6e6"/>
|
||||
<path id="Path_154-371" data-name="Path 154" d="M895.768,350.074H607.5a5.344,5.344,0,0,1-5.338-5.338V218.229a5.344,5.344,0,0,1,5.338-5.338H895.768a5.344,5.344,0,0,1,5.338,5.338V344.736A5.344,5.344,0,0,1,895.768,350.074Z" transform="translate(-44.905)" fill="#6c63ff"/>
|
||||
<path id="Path_155-372" data-name="Path 155" d="M666.206,256.985a3.624,3.624,0,0,0,0,7.248H837.063a3.624,3.624,0,0,0,0-7.248Z" transform="translate(-44.905)" fill="#fff"/>
|
||||
<path id="Path_156-373" data-name="Path 156" d="M666.206,277.985a3.624,3.624,0,0,0,0,7.248H837.063a3.624,3.624,0,0,0,0-7.248Z" transform="translate(-44.905)" fill="#fff"/>
|
||||
<path id="Path_157-374" data-name="Path 157" d="M666.206,298.731a3.624,3.624,0,0,0,0,7.248h73.52a3.624,3.624,0,0,0,0-7.248Z" transform="translate(-44.905)" fill="#fff"/>
|
||||
</g>
|
||||
<rect id="Rectangle_1" data-name="Rectangle 1" width="870.4" height="2" transform="translate(182.787 779.636)" fill="#3f3d56"/>
|
||||
<g id="Group_10" data-name="Group 10" transform="translate(100 164.584)">
|
||||
<path id="Path_138-375" data-name="Path 138" d="M883.279,615.053H595.012a5.345,5.345,0,0,1-5.338-5.338V538.207a5.345,5.345,0,0,1,5.338-5.338H883.279a5.345,5.345,0,0,1,5.338,5.338v71.507A5.345,5.345,0,0,1,883.279,615.053ZM595.012,535a3.207,3.207,0,0,0-3.2,3.2v71.507a3.207,3.207,0,0,0,3.2,3.2H883.279a3.206,3.206,0,0,0,3.2-3.2V538.207a3.206,3.206,0,0,0-3.2-3.2Z" transform="translate(-44.906)" fill="#e6e6e6"/>
|
||||
<circle id="Ellipse_18" data-name="Ellipse 18" cx="21.745" cy="21.745" r="21.745" transform="translate(573.152 549.472)" fill="#e6e6e6"/>
|
||||
<path id="Path_139-376" data-name="Path 139" d="M690.024,556.72a3.624,3.624,0,0,0,0,7.248H860.88a3.624,3.624,0,0,0,0-7.248Z" transform="translate(-44.906)" fill="#e6e6e6"/>
|
||||
<path id="Path_140-377" data-name="Path 140" d="M690.024,578.466a3.624,3.624,0,0,0,0,7.248h73.52a3.624,3.624,0,0,0,0-7.248Z" transform="translate(-44.906)" fill="#e6e6e6"/>
|
||||
</g>
|
||||
<g id="Group_11" data-name="Group 11" transform="matrix(0.914, 0.407, -0.407, 0.914, 266.183, -179.59)">
|
||||
<path id="Path_135-378" data-name="Path 135" d="M811.279,721.053H523.012a5.345,5.345,0,0,1-5.338-5.338V644.207a5.344,5.344,0,0,1,5.338-5.338H811.279a5.345,5.345,0,0,1,5.338,5.338v71.507a5.345,5.345,0,0,1-5.338,5.338ZM523.012,641a3.207,3.207,0,0,0-3.2,3.2v71.507a3.207,3.207,0,0,0,3.2,3.2H811.279a3.206,3.206,0,0,0,3.2-3.2V644.207a3.207,3.207,0,0,0-3.2-3.2Z" transform="translate(112.094 -174.499)" fill="#e6e6e6"/>
|
||||
<circle id="Ellipse_17" data-name="Ellipse 17" cx="21.745" cy="21.745" r="21.745" transform="translate(658.152 480.973)" fill="#e6e6e6"/>
|
||||
<path id="Path_136-379" data-name="Path 136" d="M618.024,662.72a3.624,3.624,0,0,0,0,7.248H788.88a3.624,3.624,0,1,0,0-7.248Z" transform="translate(112.094 -174.499)" fill="#e6e6e6"/>
|
||||
<path id="Path_137-380" data-name="Path 137" d="M618.024,684.466a3.624,3.624,0,1,0,0,7.248h73.52a3.624,3.624,0,0,0,0-7.248Z" transform="translate(112.094 -174.499)" fill="#e6e6e6"/>
|
||||
</g>
|
||||
<g id="Group_12" data-name="Group 12" transform="matrix(0.996, -0.087, 0.087, 0.996, -217.382, -42.579)">
|
||||
<path id="Path_138-2-381" data-name="Path 138" d="M883.279,615.053H595.012a5.345,5.345,0,0,1-5.338-5.338V538.207a5.345,5.345,0,0,1,5.338-5.338H883.279a5.345,5.345,0,0,1,5.338,5.338v71.507A5.345,5.345,0,0,1,883.279,615.053ZM595.012,535a3.207,3.207,0,0,0-3.2,3.2v71.507a3.207,3.207,0,0,0,3.2,3.2H883.279a3.206,3.206,0,0,0,3.2-3.2V538.207a3.206,3.206,0,0,0-3.2-3.2Z" transform="translate(-44.906)" fill="#e6e6e6"/>
|
||||
<circle id="Ellipse_18-2" data-name="Ellipse 18" cx="21.745" cy="21.745" r="21.745" transform="translate(573.152 549.472)" fill="#e6e6e6"/>
|
||||
<path id="Path_139-2-382" data-name="Path 139" d="M690.024,556.72a3.624,3.624,0,0,0,0,7.248H860.88a3.624,3.624,0,0,0,0-7.248Z" transform="translate(-44.906)" fill="#e6e6e6"/>
|
||||
<path id="Path_140-2-383" data-name="Path 140" d="M690.024,578.466a3.624,3.624,0,0,0,0,7.248h73.52a3.624,3.624,0,0,0,0-7.248Z" transform="translate(-44.906)" fill="#e6e6e6"/>
|
||||
</g>
|
||||
<g id="Group_13" data-name="Group 13" transform="translate(-1676 -1453.057)">
|
||||
<circle id="Ellipse_21" data-name="Ellipse 21" cx="33" cy="33" r="33" transform="translate(2259.759 1653.646)" fill="#2f2e41"/>
|
||||
<circle id="Ellipse_22" data-name="Ellipse 22" cx="24.561" cy="24.561" r="24.561" transform="translate(2268.868 1673.439)" fill="#feb8b8"/>
|
||||
<path id="Path_159-384" data-name="Path 159" d="M902.978,629.758s-45-8-74,2l8.5,67.5-2,11,60-2-4-11Z" transform="translate(1427.781 1103.888)" fill="#feb8b8"/>
|
||||
<path id="Path_160-385" data-name="Path 160" d="M871.707,702.6a302.545,302.545,0,0,1-31-1.964,8.492,8.492,0,0,1-7.561-8.645l.73-31.567.085-.123c8.694-12.593,4.459-21.791,1.2-26.743a6.4,6.4,0,0,1-1.052-3.522h0c-.5-.7-.612.405-.3-.379a2.51,2.51,0,0,1,1.836-1.538l4.964-.992a2.5,2.5,0,0,1,2.858,1.651c1.668,4.977,8.169,20.991,23.066,20.987,12.445,0,17.089-17.611,17.367-21.681a2.5,2.5,0,0,1,2.493-2.322h6.2a2.617,2.617,0,0,1,2.066.934,2.48,2.48,0,0,1,.244,2.751c-7.076,13.01,3.265,33.6,3.37,33.8l.066.13-2.918,30.293a8.541,8.541,0,0,1-6.574,7.477C884.336,702.206,878.239,702.6,871.707,702.6Z" transform="translate(1427.781 1103.888)" fill="#ccc"/>
|
||||
<path id="Path_161-386" data-name="Path 161" d="M814.6,772.888c-6.72,0-12.872-.74-17.76-2.489-5.81-2.079-9.458-5.441-10.844-10a13.2,13.2,0,0,1,1.606-11.673c8.595-12.87,38.421-16.5,42.423-16.929l2.12-17.667a8.531,8.531,0,0,1,8.307-7.486l50.232-.784a8.421,8.421,0,0,1,8.5,6.977l2.854,15.7L869.1,744.512l4.98,13.945-.325.217C864.115,765.1,836.392,772.887,814.6,772.888Z" transform="translate(1427.781 1103.888)" fill="#2f2e41"/>
|
||||
<path id="Path_162-387" data-name="Path 162" d="M897.478,726.258s46-6,43,18-38,41-91,0l6-12s30,8,42,2Z" transform="translate(1427.781 1103.888)" fill="#2f2e41"/>
|
||||
<path id="Path_163-388" data-name="Path 163" d="M856.478,735.258s0-12-10-11-28,2-28,2-6,4,2,6,32,12,32,12Z" transform="translate(1427.781 1103.888)" fill="#feb8b8"/>
|
||||
<path id="Path_164-389" data-name="Path 164" d="M873.478,733.258s0-12,10-11,28,2,28,2,6,4-2,6-32,12-32,12Z" transform="translate(1427.781 1103.888)" fill="#feb8b8"/>
|
||||
<path id="Path_165-390" data-name="Path 165" d="M933.4,730.985l-14.921-51.727s-3-18-6-35-14-15-14-15l-2,1v27l2,25,19.965,54.9a10.5,10.5,0,1,0,14.956-6.177Z" transform="translate(1427.781 1103.888)" fill="#feb8b8"/>
|
||||
<path id="Path_166-391" data-name="Path 166" d="M832.478,631.258s-11-2-14,15-6,35-6,35l-14.921,51.727a10.5,10.5,0,1,0,14.956,6.177l19.965-54.9,2-25V634.494a3.618,3.618,0,0,0-2-3.236Z" transform="translate(1427.781 1103.888)" fill="#feb8b8"/>
|
||||
<path id="Path_167-392" data-name="Path 167" d="M841.335,579.024a73.043,73.043,0,0,0,31.6,10.412l-3.331-3.991a24.48,24.48,0,0,0,7.561,1.5,8.281,8.281,0,0,0,6.75-3.159,7.7,7.7,0,0,0,.516-7.115,14.588,14.588,0,0,0-4.589-5.739,27.323,27.323,0,0,0-25.431-4.545,16.33,16.33,0,0,0-7.6,4.872,9.236,9.236,0,0,0-1.863,8.561" transform="translate(1427.781 1103.888)" fill="#2f2e41"/>
|
||||
<path id="Path_168-393" data-name="Path 168" d="M868.084,553.421a75.485,75.485,0,0,0-27.463-17.759c-6.639-2.459-13.865-3.979-20.805-2.582s-13.5,6.2-15.44,13.008c-1.583,5.568.052,11.564,2.509,16.806s5.738,10.1,7.725,15.54A35.468,35.468,0,0,1,778.92,626c6.819.914,13.105,4.119,19.771,5.825s14.533,1.59,19.486-3.185c5.241-5.052,5.346-13.267,5.092-20.542l-1.13-32.445c-.192-5.515-.356-11.208,1.633-16.356s6.716-9.656,12.235-9.609c4.183.036,7.884,2.569,11.239,5.068s6.9,5.165,11.071,5.536,8.923-2.711,8.611-6.883" transform="translate(1427.781 1103.888)" fill="#2f2e41"/>
|
||||
</g>
|
||||
<path id="Path_133-394" data-name="Path 133" d="M318.432,675.924a158.394,158.394,0,0,1-7.4,43.785c-.1.329-.211.653-.319.982H283.1c.029-.295.059-.624.088-.982,1.841-21.166-8.677-148.453-21.369-170.483C262.931,551.013,320.573,608.67,318.432,675.924Z" transform="translate(697.642 58.945)" fill="#e6e6e6"/>
|
||||
<path id="Path_134-395" data-name="Path 134" d="M284.933,719.709c-.231.329-.471.658-.717.982H263.5c.157-.28.339-.609.55-.982,3.422-6.176,13.551-24.642,22.953-43.785,10.1-20.572,19.374-41.924,18.593-49.652C305.838,628.014,312.83,681.148,284.933,719.709Z" transform="translate(729.069 58.945)" fill="#e6e6e6"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.2 KiB |
35
js/pages/backoffice/itop-newsroom.view-all.js
Normal file
35
js/pages/backoffice/itop-newsroom.view-all.js
Normal file
@@ -0,0 +1,35 @@
|
||||
$('body').on('change', '.ibo-toggler', function() {
|
||||
$('.ibo-notifications--view-all--bulk-buttons').toggleClass('ibo-is-hidden');
|
||||
$('.ibo-object-summary').toggleClass('ibo-is-selectable').removeClass('ibo-is-selected');
|
||||
});
|
||||
|
||||
$('body').on('click', '.ibo-object-summary.ibo-is-selectable', function() {
|
||||
$(this).toggleClass('ibo-is-selected');
|
||||
});
|
||||
|
||||
$('body').on('itop.notification.deleted', '.ibo-notifications--view-all--container', function() {
|
||||
if($(this).find('.ibo-object-summary').length === 0) {
|
||||
$('.ibo-notifications--view-all--empty').removeClass('ibo-is-hidden');
|
||||
$('.ibo-notifications--view-all--container').addClass('ibo-is-hidden');
|
||||
$('.ibo-notifications--view-all--read-action').attr('disabled', 'disabled');
|
||||
$('.ibo-notifications--view-all--unread-action').attr('disabled', 'disabled');
|
||||
$('.ibo-notifications--view-all--delete-action').attr('disabled', 'disabled');
|
||||
}
|
||||
});
|
||||
|
||||
let fReadUnreadDisabled = function() {
|
||||
if($('.ibo-object-summary.ibo-notifications--view-all--item--unread').length === 0) {
|
||||
$('.ibo-notifications--view-all--read-action').attr('disabled', 'disabled');
|
||||
$('.ibo-notifications--view-all--unread-action').removeAttr('disabled');
|
||||
} else if ($('.ibo-object-summary.ibo-notifications--view-all--item--read').length === 0) {
|
||||
$('.ibo-notifications--view-all--read-action').removeAttr('disabled');
|
||||
$('.ibo-notifications--view-all--unread-action').attr('disabled', 'disabled');
|
||||
} else {
|
||||
$('.ibo-notifications--view-all--read-action').removeAttr('disabled');
|
||||
$('.ibo-notifications--view-all--unread-action').removeAttr('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
$('body').on('itop.notification.read itop.notification.unread', '.ibo-notifications--view-all--container', fReadUnreadDisabled);
|
||||
|
||||
$('body').on('itop.notification.unread', '.ibo-notifications--view-all--container', fReadUnreadDisabled);
|
||||
@@ -265,6 +265,7 @@ return array(
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Input\\Set\\Set' => $baseDir . '/sources/Application/UI/Base/Component/Input/Set/Set.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Input\\Set\\SetUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/Input/Set/SetUIBlockFactory.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Input\\TextArea' => $baseDir . '/sources/Application/UI/Base/Component/Input/TextArea.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Input\\Toggler' => $baseDir . '/sources/Application/UI/Base/Component/Input/Toggler.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Input\\tInputLabel' => $baseDir . '/sources/Application/UI/Base/Component/Input/tInputLabel.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\MedallionIcon\\MedallionIcon' => $baseDir . '/sources/Application/UI/Base/Component/MedallionIcon/MedallionIcon.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Modal\\DoNotShowAgainOptionBlock' => $baseDir . '/sources/Application/UI/Base/Component/Modal/DoNotShowAgainOptionBlock.php',
|
||||
|
||||
@@ -640,6 +640,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Input\\Set\\Set' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Input/Set/Set.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Input\\Set\\SetUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Input/Set/SetUIBlockFactory.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Input\\TextArea' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Input/TextArea.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Input\\Toggler' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Input/Toggler.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Input\\tInputLabel' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Input/tInputLabel.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\MedallionIcon\\MedallionIcon' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/MedallionIcon/MedallionIcon.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Modal\\DoNotShowAgainOptionBlock' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Modal/DoNotShowAgainOptionBlock.php',
|
||||
|
||||
@@ -3652,12 +3652,15 @@ EOF;
|
||||
$this->CompileFiles($oBrandingNode, $sTempTargetDir.'/branding', $sFinalTargetDir.'/branding', 'branding');
|
||||
$aDataBranding = [];
|
||||
|
||||
$aLogosToCompile = [
|
||||
['sNodeName' => 'login_logo', 'sTargetFile' => 'login-logo', 'sType' => Branding::ENUM_LOGO_TYPE_LOGIN_LOGO],
|
||||
['sNodeName' => 'main_logo', 'sTargetFile' => 'main-logo-full', 'sType' => Branding::ENUM_LOGO_TYPE_MAIN_LOGO_FULL],
|
||||
['sNodeName' => 'main_logo_compact', 'sTargetFile' => 'main-logo-compact', 'sType' => Branding::ENUM_LOGO_TYPE_MAIN_LOGO_COMPACT],
|
||||
['sNodeName' => 'portal_logo', 'sTargetFile' =>'portal-logo', 'sType' => Branding::ENUM_LOGO_TYPE_PORTAL_LOGO],
|
||||
];
|
||||
$aLogosToCompile = [
|
||||
['sNodeName' => 'login_logo', 'sTargetFile' => 'login-logo', 'sType' => Branding::ENUM_LOGO_TYPE_LOGIN_LOGO],
|
||||
['sNodeName' => 'main_logo', 'sTargetFile' => 'main-logo-full', 'sType' => Branding::ENUM_LOGO_TYPE_MAIN_LOGO_FULL],
|
||||
['sNodeName' => 'main_logo_compact', 'sTargetFile' => 'main-logo-compact', 'sType' => Branding::ENUM_LOGO_TYPE_MAIN_LOGO_COMPACT],
|
||||
['sNodeName' => 'portal_logo', 'sTargetFile' => 'portal-logo', 'sType' => Branding::ENUM_LOGO_TYPE_PORTAL_LOGO],
|
||||
['sNodeName' => 'login_favicon', 'sTargetFile' => 'login_favicon', 'sType' => Branding::ENUM_LOGO_TYPE_LOGIN_FAVICON],
|
||||
['sNodeName' => 'main_favicon', 'sTargetFile' => 'main_favicon', 'sType' => Branding::ENUM_LOGO_TYPE_MAIN_FAVICON],
|
||||
['sNodeName' => 'portal_favicon', 'sTargetFile' => 'portal_favicon', 'sType' => Branding::ENUM_LOGO_TYPE_PORTAL_FAVICON],
|
||||
];
|
||||
foreach ($aLogosToCompile as $aLogo) {
|
||||
$sLogo = $this->CompileLogo($oBrandingNode, $sTempTargetDir, $sFinalTargetDir, $aLogo['sNodeName'], $aLogo['sTargetFile']);
|
||||
if ($sLogo != null) {
|
||||
@@ -3677,7 +3680,7 @@ EOF;
|
||||
{
|
||||
SetupUtils::rrmdir($sTempTargetDir.'/branding/images');
|
||||
}
|
||||
|
||||
|
||||
// Compile themes
|
||||
$this->CompileThemes($oBrandingNode, $sTempTargetDir);
|
||||
}
|
||||
|
||||
@@ -910,8 +910,8 @@ class iTopDesignFormat
|
||||
$oNodeList = $oXPath->query('/itop_design/branding/themes/theme[@id="test-red"]/variables/variable[@id="backoffice-environment-banner-background-color"]');
|
||||
foreach ($oNodeList as $oNode) {
|
||||
$oNode->setAttribute('id', 'ibo-page-banner--background-color');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$oNodeList = $oXPath->query( '/itop_design/branding/themes/theme[@id="test-red"]/variables/variable[@id="backoffice-environment-banner-text-color"]');
|
||||
foreach ($oNodeList as $oNode) {
|
||||
$oNode->setAttribute('id', 'ibo-page-banner--text-color');
|
||||
@@ -1108,7 +1108,10 @@ class iTopDesignFormat
|
||||
*/
|
||||
protected function From32To31($oFactory)
|
||||
{
|
||||
// Nothing for now...
|
||||
// N°3363 - Add favicon in branding
|
||||
$this->RemoveNodeFromXPath('/itop_design/branding/main_favicon');
|
||||
$this->RemoveNodeFromXPath('/itop_design/branding/portal_favicon');
|
||||
$this->RemoveNodeFromXPath('/itop_design/branding/login_favicon');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
namespace Combodo\iTop\Application;
|
||||
|
||||
use MetaModel;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
@@ -38,27 +39,52 @@ class Branding
|
||||
public const ENUM_LOGO_TYPE_PORTAL_LOGO = 'portal_logo';
|
||||
/** @var string Logo used in the login pages */
|
||||
public const ENUM_LOGO_TYPE_LOGIN_LOGO = 'login_logo';
|
||||
/**
|
||||
* @var string Logo used in most pages (backoffice, ...)
|
||||
* @since 3.2.0 N°3363
|
||||
*/
|
||||
public const ENUM_LOGO_TYPE_MAIN_FAVICON = 'main_favicon';
|
||||
/**
|
||||
* @var string Logo used in the end-users portals
|
||||
* @since 3.2.0 N°3363
|
||||
*/
|
||||
public const ENUM_LOGO_TYPE_PORTAL_FAVICON = 'portal_favicon';
|
||||
/**
|
||||
* @var string Logo used in the login page
|
||||
* @since 3.2.0 N°3363
|
||||
*/
|
||||
public const ENUM_LOGO_TYPE_LOGIN_FAVICON = 'login_favicon';
|
||||
/** @var string Default logo */
|
||||
public const DEFAULT_LOGO_TYPE = self::ENUM_LOGO_TYPE_MAIN_LOGO_FULL;
|
||||
|
||||
/** @var \string[][] Relative paths to the logos from the current environment folder */
|
||||
public static $aLogoPaths = [
|
||||
self::ENUM_LOGO_TYPE_MAIN_LOGO_FULL => [
|
||||
self::ENUM_LOGO_TYPE_MAIN_LOGO_FULL => [
|
||||
'default' => 'images/itop-logo.png',
|
||||
],
|
||||
self::ENUM_LOGO_TYPE_MAIN_LOGO_COMPACT => [
|
||||
'default' => 'images/itop-logo-square.png',
|
||||
],
|
||||
self::ENUM_LOGO_TYPE_PORTAL_LOGO => [
|
||||
self::ENUM_LOGO_TYPE_PORTAL_LOGO => [
|
||||
'default' => 'images/logo-itop-dark-bg.svg',
|
||||
],
|
||||
self::ENUM_LOGO_TYPE_LOGIN_LOGO => [
|
||||
self::ENUM_LOGO_TYPE_LOGIN_LOGO => [
|
||||
'default' => 'images/itop-logo-external.png',
|
||||
],
|
||||
self::ENUM_LOGO_TYPE_MAIN_FAVICON => [
|
||||
'default' => 'images/favicon.ico',
|
||||
],
|
||||
self::ENUM_LOGO_TYPE_PORTAL_FAVICON => [
|
||||
'default' => 'images/favicon.ico',
|
||||
],
|
||||
self::ENUM_LOGO_TYPE_LOGIN_FAVICON => [
|
||||
'default' => 'images/favicon.ico',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Return url or path of logo defined by $sType
|
||||
*
|
||||
* @param string $sType
|
||||
* @param string $sAppPath
|
||||
* @param ?string $sModulePath
|
||||
@@ -73,7 +99,7 @@ class Branding
|
||||
if (isset($aThemeParameters[$sType])) {
|
||||
$sCustomLogoPath = $aThemeParameters[$sType];
|
||||
if (file_exists($sWorkingPath.$sCustomLogoPath)) {
|
||||
return ($sModulePath??$sAppPath).$sCustomLogoPath;
|
||||
return ($sModulePath ?? $sAppPath).$sCustomLogoPath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,4 +180,39 @@ class Branding
|
||||
return static::GetLogoAbsoluteUrl(static::ENUM_LOGO_TYPE_LOGIN_LOGO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the absolute URL for thefavicon
|
||||
*
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
* @since 3.2.0 N°3363
|
||||
*/
|
||||
public static function GetMainFavIconAbsoluteUrl(): string
|
||||
{
|
||||
return static::GetLogoAbsoluteUrl(static::ENUM_LOGO_TYPE_MAIN_FAVICON);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the absolute URL for thefavicon
|
||||
*
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
* @since 3.2.0 N°3363
|
||||
*/
|
||||
public static function GetPortalFavIconAbsoluteUrl(): string
|
||||
{
|
||||
return static::GetLogoAbsoluteUrl(static::ENUM_LOGO_TYPE_PORTAL_FAVICON);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the absolute URL for thefavicon
|
||||
*
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
* @since 3.2.0 N°3363
|
||||
*/
|
||||
public static function GetLoginFavIconAbsoluteUrl(): string
|
||||
{
|
||||
return static::GetLogoAbsoluteUrl(static::ENUM_LOGO_TYPE_LOGIN_FAVICON);
|
||||
}
|
||||
}
|
||||
@@ -38,14 +38,8 @@ class ButtonGroup extends UIBlock
|
||||
/**
|
||||
* Button constructor.
|
||||
*
|
||||
* @param string $sLabel
|
||||
* @param array $aButtons
|
||||
* @param string|null $sId
|
||||
* @param string $sTooltip
|
||||
* @param string $sIconClass
|
||||
* @param string $sActionType
|
||||
* @param string $sColor
|
||||
* @param string $sJsCode
|
||||
* @param string $sOnClickJsCode
|
||||
*/
|
||||
public function __construct(array $aButtons = [], ?string $sId = null)
|
||||
{
|
||||
|
||||
@@ -95,10 +95,11 @@ class InputUIBlockFactory extends AbstractUIBlockFactory
|
||||
* @param string $sLabel
|
||||
* @param \Combodo\iTop\Application\UI\Base\Component\Input\Input $oInput
|
||||
* @param string|null $sId
|
||||
* @since 3.2.0 method is now public
|
||||
*
|
||||
* @return \Combodo\iTop\Application\UI\Base\Component\Input\InputWithLabel
|
||||
*/
|
||||
private static function MakeInputWithLabel(string $sName, string $sLabel, Input $oInput, ?string $sId = null)
|
||||
public static function MakeInputWithLabel(string $sName, string $sLabel, Input $oInput, ?string $sId = null)
|
||||
{
|
||||
$oInput->SetName($sName);
|
||||
|
||||
|
||||
@@ -138,4 +138,8 @@ class InputWithLabel extends UIBlock
|
||||
return utils::IsNotNullOrEmptyString($this->sDescription);
|
||||
}
|
||||
|
||||
public function GetSubBlocks(): array
|
||||
{
|
||||
return [$this->oInput->GetId() => $this->oInput];
|
||||
}
|
||||
}
|
||||
37
sources/Application/UI/Base/Component/Input/Toggler.php
Normal file
37
sources/Application/UI/Base/Component/Input/Toggler.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Application\UI\Base\Component\Input;
|
||||
|
||||
|
||||
/**
|
||||
* @package Combodo\iTop\Application\UI\Base\Component\Input
|
||||
* @since 3.2.0
|
||||
*/
|
||||
class Toggler extends Input {
|
||||
|
||||
// Overloaded constants
|
||||
public const BLOCK_CODE = 'ibo-toggler';
|
||||
public const DEFAULT_HTML_TEMPLATE_REL_PATH = 'base/components/input/input-toggler';
|
||||
public const DEFAULT_JS_ON_READY_TEMPLATE_REL_PATH = 'base/components/input/input-toggler';
|
||||
|
||||
|
||||
public function __construct(?string $sId = null)
|
||||
{
|
||||
parent::__construct($sId);
|
||||
$this->SetType('checkbox');
|
||||
}
|
||||
|
||||
public function SetIsToggled(bool $bIsToggled): static
|
||||
{
|
||||
return $this->SetIsChecked($bIsToggled);
|
||||
}
|
||||
|
||||
public function IsToggled(): bool
|
||||
{
|
||||
return $this->IsChecked();
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ class NewsroomMenuFactory
|
||||
$sPlaceholderImageUrl= 'far fa-envelope';
|
||||
$aParams = array(
|
||||
'image_icon' => $sImageUrl,
|
||||
'no_message_icon' => file_get_contents(APPROOT.'images/illustrations/undraw_empty.svg'),
|
||||
'no_message_icon' => file_get_contents(APPROOT.'images/illustrations/undraw_social_serenity.svg'),
|
||||
'placeholder_image_icon' => $sPlaceholderImageUrl,
|
||||
'cache_uuid' => 'itop-newsroom-'.UserRights::GetUserId().'-'.md5(APPROOT),
|
||||
'providers' => $aProviderParams,
|
||||
|
||||
@@ -103,6 +103,17 @@ class ObjectDetails extends Panel implements iKeyboardShortcut
|
||||
return $this->sClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see self::$sClassLabel
|
||||
* @return $this
|
||||
*/
|
||||
public function SetClassLabel($sClassLabel)
|
||||
{
|
||||
$this->sClassLabel = $sClassLabel;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see self::$sClassLabel
|
||||
* @return string
|
||||
|
||||
@@ -102,8 +102,8 @@ class ObjectSummary extends ObjectDetails
|
||||
{
|
||||
$oRouter = Router::GetInstance();
|
||||
$oDetailsButton = null;
|
||||
if(UserRights::IsActionAllowed($this->sClassName, UR_ACTION_MODIFY)) {
|
||||
$sRootUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
// We can pass a DBObject to the UIBlock, so we check for the DisplayModifyForm method
|
||||
if(method_exists($this->oObject, 'DisplayModifyForm') && UserRights::IsActionAllowed($this->sClassName, UR_ACTION_MODIFY)) {
|
||||
$oPopoverMenu = new PopoverMenu();
|
||||
|
||||
$oDetailsAction = new URLPopupMenuItem(
|
||||
|
||||
@@ -41,7 +41,7 @@ class UnauthenticatedWebPage extends NiceWebPage
|
||||
private $sContent;
|
||||
private $sPanelTitle;
|
||||
private $sPanelIcon;
|
||||
|
||||
|
||||
// TODO 3.0 Find a clever way to allow theme customization for unauthenticated webpages
|
||||
private $sCustomThemeUrl;
|
||||
|
||||
@@ -288,4 +288,13 @@ class UnauthenticatedWebPage extends NiceWebPage
|
||||
$this->add_linked_stylesheet($this->sCustomThemeUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected function GetFaviconAbsoluteUrl()
|
||||
{
|
||||
return Branding::GetLoginFavIconAbsoluteUrl();
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
namespace Combodo\iTop\Application\WebPage;
|
||||
|
||||
use Combodo\iTop\Application\Branding;
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
use Combodo\iTop\Application\Helper\WebResourcesHelper;
|
||||
use Combodo\iTop\Application\TwigBase\Twig\TwigHelper;
|
||||
@@ -1761,17 +1762,13 @@ EOD
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the absolute URL for the favicon
|
||||
*
|
||||
* @return string
|
||||
* @return string Absolute URL for the favicon
|
||||
* @throws \Exception
|
||||
* @since 3.0.0
|
||||
*/
|
||||
protected function GetFaviconAbsoluteUrl()
|
||||
{
|
||||
// TODO 3.0.0: Make it a property so it can be changed programmatically
|
||||
// TODO 3.0.0: How to set both dark/light mode favicons
|
||||
return utils::GetAbsoluteUrlAppRoot().'images/favicon.ico';
|
||||
return Branding::GetMainFavIconAbsoluteUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1838,7 +1835,7 @@ EOD
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function GetAddJSDict(): bool
|
||||
@@ -1850,7 +1847,7 @@ EOD
|
||||
* @param bool $bAddJSDict
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function SetAddJSDict(bool $bAddJSDict)
|
||||
|
||||
@@ -5,15 +5,33 @@ namespace Combodo\iTop\Controller\Newsroom;
|
||||
use ArchivedObjectException;
|
||||
use Combodo\iTop\Application\Branding;
|
||||
use Combodo\iTop\Application\TwigBase\Controller\Controller;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\Button;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\ButtonGroup\ButtonGroupUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Input\Toggler;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Panel\Panel;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu;
|
||||
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenuItem\PopoverMenuItemFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\Object\ObjectSummary;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
|
||||
use Combodo\iTop\Application\WebPage\iTopWebPage;
|
||||
use Combodo\iTop\Application\WebPage\JsonPage;
|
||||
use Combodo\iTop\Application\WebPage\JsonPPage;
|
||||
use Combodo\iTop\Service\Notification\NotificationsRepository;
|
||||
use Combodo\iTop\Service\Router\Router;
|
||||
use CoreException;
|
||||
use DBObjectSearch;
|
||||
use DBObjectSet;
|
||||
use Dict;
|
||||
use DisplayBlock;
|
||||
use JSPopupMenuItem;
|
||||
use MetaModel;
|
||||
use SecurityException;
|
||||
use URLPopupMenuItem;
|
||||
use UserRights;
|
||||
use utils;
|
||||
|
||||
@@ -38,12 +56,476 @@ class iTopNewsroomController extends Controller
|
||||
public function OperationViewAll()
|
||||
{
|
||||
$oPage = new iTopWebPage(Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Title'));
|
||||
$oSearch = DBObjectSearch::FromOQL('SELECT EventiTopNotification WHERE read = "no"');
|
||||
$oSearch->AddCondition('contact_id', UserRights::GetContactId(), '=');
|
||||
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, []);
|
||||
$oBlock->Display($oPage, 0);
|
||||
$oPage->add("<div class='sf_results_area ibo-add-margin-top-250' data-target='search_results'>");
|
||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/pages/backoffice/itop-newsroom.view-all.js');
|
||||
// Add title block
|
||||
// Make bulk actions block
|
||||
$oBulkActionsBlock = PanelUIBlockFactory::MakeForInformation(Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Title'));
|
||||
$oToolbar = ToolbarUIBlockFactory::MakeStandard();
|
||||
$oToolbar->AddCSSClass('ibo-notifications--view-all--toolbar');
|
||||
$oAllModeButtonsContainer = new UIContentBlock('ibo-notifications--view-all--all-mode-buttons', ['ibo-notifications--view-all--bulk-buttons', 'ibo-notifications--view-all--all-mode-buttons']);
|
||||
// Create CSRF token we'll use in this page
|
||||
$sCSRFToken = utils::GetNewTransactionId();
|
||||
// Make button to mark all as read
|
||||
$sMarkMultipleAsReadUrl = Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.mark_multiple_as_read', ['token' => $sCSRFToken]);
|
||||
$sMarkMultipleAsUnreadUrl = Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.mark_multiple_as_unread', ['token' => $sCSRFToken]);
|
||||
$sDeleteMultipleUrl = Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.delete_multiple', ['token' => $sCSRFToken]);
|
||||
|
||||
$oMarkAllAsReadButton = ButtonUIBlockFactory::MakeForSecondaryAction(
|
||||
Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAllAsRead:Label'),
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAllAsRead:Label',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAllAsRead:Label'
|
||||
);
|
||||
$oMarkAllAsReadButton->SetIconClass('far fa-envelope-open')
|
||||
->AddCSSClass('ibo-notifications--view-all--read-action')
|
||||
->SetOnClickJsCode(
|
||||
<<<JS
|
||||
let oSelf = this;
|
||||
let oNotificationToMarkAsRead = $('.ibo-notifications--view-all--container [data-role="ibo-object-summary"].ibo-notifications--view-all--item--unread');
|
||||
let aNotificationIds = [];
|
||||
oNotificationToMarkAsRead.each(function(){
|
||||
aNotificationIds.push($(this).attr('data-object-id'));
|
||||
});
|
||||
$.ajax({
|
||||
url: '{$sMarkMultipleAsReadUrl}',
|
||||
data: {
|
||||
notification_ids: aNotificationIds
|
||||
},
|
||||
type: 'POST',
|
||||
success: function(data) {
|
||||
if (data.status === 'success') {
|
||||
let MarkAsReadButton = oNotificationToMarkAsRead.find('.ibo-button-group:not(.ibo-is-hidden)');
|
||||
let MarkAsUnreadButton = oNotificationToMarkAsRead.find('.ibo-button-group.ibo-is-hidden');
|
||||
MarkAsReadButton.addClass('ibo-is-hidden');
|
||||
MarkAsUnreadButton.removeClass('ibo-is-hidden');
|
||||
oNotificationToMarkAsRead.removeClass('ibo-notifications--view-all--item--unread').addClass('ibo-notifications--view-all--item--read');
|
||||
CombodoToast.OpenSuccessToast(data.message);
|
||||
$('.ibo-notifications--view-all--container').trigger('itop.notification.read');
|
||||
}
|
||||
else {
|
||||
CombodoToast.OpenErrorToast(data.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
JS
|
||||
);
|
||||
|
||||
// Make button to mark all as unread
|
||||
$oMarkAllAsUnreadButton = ButtonUIBlockFactory::MakeForSecondaryAction(
|
||||
Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAllAsUnread:Label'),
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAllAsUnread:Label',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAllAsUnread:Label',
|
||||
);
|
||||
$oMarkAllAsUnreadButton->SetIconClass('far fa-envelope')
|
||||
->AddCSSClass('ibo-notifications--view-all--unread-action')
|
||||
->SetOnClickJsCode(
|
||||
<<<JS
|
||||
let oSelf = this;
|
||||
let oNotificationToMarkAsUnread = $('.ibo-notifications--view-all--container [data-role="ibo-object-summary"].ibo-notifications--view-all--item--read');
|
||||
let aNotificationIds = [];
|
||||
oNotificationToMarkAsUnread.each(function(){
|
||||
aNotificationIds.push($(this).attr('data-object-id'));
|
||||
});
|
||||
$.ajax({
|
||||
url: '{$sMarkMultipleAsUnreadUrl}',
|
||||
data: {
|
||||
notification_ids: aNotificationIds
|
||||
},
|
||||
type: 'POST',
|
||||
success: function(data) {
|
||||
if (data.status === 'success') {
|
||||
let MarkAsUnreadButton = oNotificationToMarkAsUnread.find('.ibo-button-group:not(.ibo-is-hidden)');
|
||||
let MarkAsReadButton = oNotificationToMarkAsUnread.find('.ibo-button-group.ibo-is-hidden');
|
||||
|
||||
MarkAsReadButton.removeClass('ibo-is-hidden');
|
||||
MarkAsUnreadButton.addClass('ibo-is-hidden');
|
||||
oNotificationToMarkAsUnread.removeClass('ibo-notifications--view-all--item--read').addClass('ibo-notifications--view-all--item--unread');
|
||||
CombodoToast.OpenSuccessToast(data.message);
|
||||
$('.ibo-notifications--view-all--container').trigger('itop.notification.unread');
|
||||
}
|
||||
else {
|
||||
CombodoToast.OpenErrorToast(data.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
JS
|
||||
);
|
||||
|
||||
// Make button to delete all
|
||||
$oDeleteAllButton = ButtonUIBlockFactory::MakeForDestructiveAction(
|
||||
Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Label'),
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Label',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Label'
|
||||
);
|
||||
$oDeleteAllButtonConfirmTitle = Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Confirmation:Title');
|
||||
$oDeleteAllButtonConfirmMessage = Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Confirmation:Message');
|
||||
$oDeleteAllButton->SetActionType(Button::ENUM_ACTION_TYPE_ALTERNATIVE);
|
||||
$oDeleteAllButton->SetIconClass('fas fa-trash-alt')
|
||||
->AddCSSClass('ibo-notifications--view-all--delete-action')
|
||||
->SetOnClickJsCode(
|
||||
<<<JS
|
||||
let oSelf = this;
|
||||
let oNotificationToDelete = $('.ibo-notifications--view-all--container [data-role="ibo-object-summary"]');
|
||||
let aNotificationIds = [];
|
||||
oNotificationToDelete.each(function(){
|
||||
aNotificationIds.push($(this).attr('data-object-id'));
|
||||
});
|
||||
CombodoModal.OpenConfirmationModal({
|
||||
title: '$oDeleteAllButtonConfirmTitle',
|
||||
content: '$oDeleteAllButtonConfirmMessage',
|
||||
callback_on_confirm: function() {
|
||||
$.ajax({
|
||||
url: '{$sDeleteMultipleUrl}',
|
||||
data: {
|
||||
notification_ids: aNotificationIds
|
||||
},
|
||||
type: 'POST',
|
||||
success: function(data) {
|
||||
if (data.status === 'success') {
|
||||
oNotificationToDelete.remove();
|
||||
CombodoToast.OpenSuccessToast(data.message);
|
||||
$('.ibo-notifications--view-all--container').trigger('itop.notification.deleted');
|
||||
}
|
||||
else {
|
||||
CombodoToast.OpenErrorToast(data.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: '$oDeleteAllButtonConfirmTitle',
|
||||
classes: ['ibo-is-danger']
|
||||
}
|
||||
},
|
||||
do_not_show_again_pref_key: 'notifications-center.delete-all-confirmation-modal.do-not-show-again',
|
||||
}, []);
|
||||
|
||||
JS
|
||||
);
|
||||
// Add "all" buttons to their container
|
||||
$oAllModeButtonsContainer->AddSubBlock($oMarkAllAsReadButton);
|
||||
$oAllModeButtonsContainer->AddSubBlock($oMarkAllAsUnreadButton);
|
||||
$oAllModeButtonsContainer->AddSubBlock($oDeleteAllButton);
|
||||
$oToolbar->AddSubBlock($oAllModeButtonsContainer);
|
||||
|
||||
$oSelectedModelButtonsContainer = new UIContentBlock('ibo-notifications--view-all--selected-mode-buttons', ['ibo-is-hidden', 'ibo-notifications--view-all--bulk-buttons', 'ibo-notifications--view-all--selected-mode-buttons']);
|
||||
// Make button mark all selected as read
|
||||
$oMarkSelectedAsReadButton = ButtonUIBlockFactory::MakeForSecondaryAction(
|
||||
Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkSelectedAsRead:Label'),
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkSelectedAsRead:Label',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkSelectedAsRead:Label'
|
||||
);
|
||||
$oMarkSelectedAsReadButton->SetIconClass('far fa-envelope-open')
|
||||
->AddCSSClass('ibo-notifications--view-all--read-action')
|
||||
->SetOnClickJsCode(
|
||||
<<<JS
|
||||
let oSelf = this;
|
||||
let oNotificationToMarkAsRead = $('.ibo-notifications--view-all--container [data-role="ibo-object-summary"].ibo-notifications--view-all--item--unread.ibo-is-selected');
|
||||
let aNotificationIds = [];
|
||||
oNotificationToMarkAsRead.each(function(){
|
||||
aNotificationIds.push($(this).attr('data-object-id'));
|
||||
});
|
||||
$.ajax({
|
||||
url: '{$sMarkMultipleAsReadUrl}',
|
||||
data: {
|
||||
notification_ids: aNotificationIds
|
||||
},
|
||||
type: 'POST',
|
||||
success: function(data) {
|
||||
if (data.status === 'success') {
|
||||
let MarkAsReadButton = oNotificationToMarkAsRead.find('.ibo-button-group:not(.ibo-is-hidden)');
|
||||
let MarkAsUnreadButton = oNotificationToMarkAsRead.find('.ibo-button-group.ibo-is-hidden');
|
||||
MarkAsReadButton.addClass('ibo-is-hidden');
|
||||
MarkAsUnreadButton.removeClass('ibo-is-hidden');
|
||||
oNotificationToMarkAsRead.removeClass('ibo-notifications--view-all--item--unread').addClass('ibo-notifications--view-all--item--read');
|
||||
CombodoToast.OpenSuccessToast(data.message);
|
||||
$('.ibo-notifications--view-all--container').trigger('itop.notification.read');
|
||||
}
|
||||
else {
|
||||
CombodoToast.OpenErrorToast(data.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
JS
|
||||
);
|
||||
|
||||
// Make button mark all selected as unread
|
||||
$oMarkSelectedAsUnreadButton = ButtonUIBlockFactory::MakeForSecondaryAction(
|
||||
Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkSelectedAsUnread:Label'),
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkSelectedAsUnread:Label',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkSelectedAsUnread:Label'
|
||||
);
|
||||
$oMarkSelectedAsUnreadButton->SetIconClass('far fa-envelope')
|
||||
->AddCSSClass('ibo-notifications--view-all--unread-action')
|
||||
->SetOnClickJsCode(
|
||||
<<<JS
|
||||
let oSelf = this;
|
||||
let oNotificationToMarkAsUnread = $('.ibo-notifications--view-all--container [data-role="ibo-object-summary"].ibo-notifications--view-all--item--read.ibo-is-selected');
|
||||
let aNotificationIds = [];
|
||||
oNotificationToMarkAsUnread.each(function(){
|
||||
aNotificationIds.push($(this).attr('data-object-id'));
|
||||
});
|
||||
$.ajax({
|
||||
url: '{$sMarkMultipleAsUnreadUrl}',
|
||||
data: {
|
||||
notification_ids: aNotificationIds
|
||||
},
|
||||
type: 'POST',
|
||||
success: function(data) {
|
||||
if (data.status === 'success') {
|
||||
let MarkAsUnreadButton = oNotificationToMarkAsUnread.find('.ibo-button-group:not(.ibo-is-hidden)');
|
||||
let MarkAsReadButton = oNotificationToMarkAsUnread.find('.ibo-button-group.ibo-is-hidden');
|
||||
|
||||
MarkAsReadButton.removeClass('ibo-is-hidden');
|
||||
MarkAsUnreadButton.addClass('ibo-is-hidden');
|
||||
|
||||
oNotificationToMarkAsUnread.removeClass('ibo-notifications--view-all--item--read').addClass('ibo-notifications--view-all--item--unread');
|
||||
CombodoToast.OpenSuccessToast(data.message);
|
||||
$('.ibo-notifications--view-all--container').trigger('itop.notification.unread');
|
||||
}
|
||||
else {
|
||||
CombodoToast.OpenErrorToast(data.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
JS
|
||||
);
|
||||
|
||||
// Make button delete all selected
|
||||
$oDeleteSelectedButton = ButtonUIBlockFactory::MakeForDestructiveAction(
|
||||
Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Label'),
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Label',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Label'
|
||||
);
|
||||
$oDeleteSelectedButtonConfirmTitle = Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Confirmation:Title');
|
||||
$oDeleteSelectedButtonConfirmMessage = Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Confirmation:Message');
|
||||
$oDeleteSelectedButton->SetActionType(Button::ENUM_ACTION_TYPE_ALTERNATIVE);
|
||||
$oDeleteSelectedButton->SetIconClass('fas fa-trash-alt')
|
||||
->AddCSSClass('ibo-notifications--view-all--delete-action')
|
||||
->SetOnClickJsCode(
|
||||
<<<JS
|
||||
let oSelf = this;
|
||||
let oNotificationToDelete = $('.ibo-notifications--view-all--container [data-role="ibo-object-summary"].ibo-is-selected');
|
||||
let aNotificationIds = [];
|
||||
oNotificationToDelete.each(function(){
|
||||
aNotificationIds.push($(this).attr('data-object-id'));
|
||||
});
|
||||
CombodoModal.OpenConfirmationModal({
|
||||
title: '$oDeleteSelectedButtonConfirmTitle',
|
||||
content: '$oDeleteSelectedButtonConfirmMessage',
|
||||
callback_on_confirm: function() {
|
||||
$.ajax({
|
||||
url: '{$sDeleteMultipleUrl}',
|
||||
data: {
|
||||
notification_ids: aNotificationIds
|
||||
},
|
||||
type: 'POST',
|
||||
success: function(data) {
|
||||
if (data.status === 'success') {
|
||||
oNotificationToDelete.remove();
|
||||
CombodoToast.OpenSuccessToast(data.message);
|
||||
$('.ibo-notifications--view-all--container').trigger('itop.notification.deleted');
|
||||
}
|
||||
else {
|
||||
CombodoToast.OpenErrorToast(data.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: '$oDeleteSelectedButtonConfirmTitle',
|
||||
classes: ['ibo-is-danger']
|
||||
}
|
||||
},
|
||||
do_not_show_again_pref_key: 'notifications-center.delete-all-confirmation-modal.do-not-show-again',
|
||||
}, []);
|
||||
JS
|
||||
);
|
||||
|
||||
// Add "selected" buttons to their container
|
||||
$oSelectedModelButtonsContainer->AddSubBlock($oMarkSelectedAsReadButton);
|
||||
$oSelectedModelButtonsContainer->AddSubBlock($oMarkSelectedAsUnreadButton);
|
||||
$oSelectedModelButtonsContainer->AddSubBlock($oDeleteSelectedButton);
|
||||
|
||||
$oToolbar->AddSubBlock($oSelectedModelButtonsContainer);
|
||||
|
||||
// Make toggler to switch between "all" and "selected" mode
|
||||
$oTogglerContentBlock = new UIContentBlock('ibo-notifications--view-all--toggler', ['ibo-notifications--view-all--toggler']);
|
||||
$oToggler = new Toggler();
|
||||
$oInputWithLabel = InputUIBlockFactory::MakeInputWithLabel('slider', Dict::S('UI:Newsroom:iTopNotification:SelectMode:Label'), $oToggler);
|
||||
$oTogglerContentBlock->AddSubBlock($oInputWithLabel);
|
||||
$oToolbar->AddSubBlock($oTogglerContentBlock);
|
||||
|
||||
$oBulkActionsBlock->AddSubBlock($oToolbar);
|
||||
$oPage->AddUiBlock($oBulkActionsBlock);
|
||||
|
||||
// Search for all notifications for the current user
|
||||
$oSearch = DBObjectSearch::FromOQL('SELECT EventiTopNotification');
|
||||
$oSearch->AddCondition('contact_id', UserRights::GetContactId(), '=');
|
||||
$oSet = new DBObjectSet($oSearch, array('read' => true, 'date' => true), array());
|
||||
|
||||
// Add main content block
|
||||
$oMainContentBlock = new UIContentBlock(null, ['ibo-notifications--view-all--container']);
|
||||
$oPage->AddUiBlock($oMainContentBlock);
|
||||
|
||||
while ($oEvent = $oSet->Fetch()) {
|
||||
$iEventId = $oEvent->GetKey();
|
||||
// Prepare object summary block
|
||||
$sReadColor = $oEvent->Get('read') === 'no' ? 'ibo-notifications--view-all--item--unread' : 'ibo-notifications--view-all--item--read';
|
||||
$sReadLabel = $oEvent->Get('read') === 'no' ? Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Unread:Label') : Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Read:Label');
|
||||
$oEventBlock = new ObjectSummary($oEvent);
|
||||
$oEventBlock->SetCSSColorClass($sReadColor);
|
||||
$oEventBlock->SetSubTitle($sReadLabel);
|
||||
$oEventBlock->SetClassLabel('');
|
||||
$oImage = $oEvent->Get('icon');
|
||||
if (!$oImage->IsEmpty()) {
|
||||
$sIconUrl = $oImage->GetDisplayURL(get_class($oEvent), $iEventId, 'icon');
|
||||
$oEventBlock->SetIcon($sIconUrl, Panel::ENUM_ICON_COVER_METHOD_COVER,true);
|
||||
}
|
||||
|
||||
// Prepare Event actions
|
||||
$oMarkAsReadPopoverMenu = new PopoverMenu();
|
||||
$oMarkAsUnreadPopoverMenu = new PopoverMenu();
|
||||
|
||||
// Common actions
|
||||
$sDeleteUrl = Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.delete_event', ['notification_id' => $oEvent->GetKey(), 'token' => $sCSRFToken]);
|
||||
$oDeleteButton = new JSPopupMenuItem(
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:Delete:Label',
|
||||
Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:Delete:Label'),
|
||||
<<<JS
|
||||
let oSelf = this;
|
||||
|
||||
$.ajax({
|
||||
url: '{$sDeleteUrl}',
|
||||
type: 'POST',
|
||||
success: function(data) {
|
||||
if (data.status === 'success') {
|
||||
$(oSelf).parents('.ibo-object-summary').remove();
|
||||
CombodoToast.OpenSuccessToast(data.message);
|
||||
$('.ibo-notifications--view-all--container').trigger('itop.notification.deleted');
|
||||
}
|
||||
else {
|
||||
CombodoToast.OpenErrorToast(data.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
JS,
|
||||
'_blank'
|
||||
);
|
||||
$oViewButton = new URLPopupMenuItem(
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:ViewObject:Label',
|
||||
Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:ViewObject:Label'),
|
||||
Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.view_event', ['event_id' => $oEvent->GetKey()]),
|
||||
'_blank'
|
||||
);
|
||||
|
||||
// Mark as read action
|
||||
$oMarkAsReadButton = ButtonUIBlockFactory::MakeForAlternativeSecondaryAction(
|
||||
Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsRead:Label'),
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsRead:Label',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsRead:Label',
|
||||
);
|
||||
|
||||
// Mark as read action
|
||||
$oMarkAsReadPopoverMenu->AddItem('more-actions', PopoverMenuItemFactory::MakeFromApplicationPopupMenuItem($oViewButton))->SetContainer(PopoverMenu::ENUM_CONTAINER_PARENT);
|
||||
$oMarkAsReadPopoverMenu->AddItem('more-actions', PopoverMenuItemFactory::MakeFromApplicationPopupMenuItem($oDeleteButton))->SetContainer(PopoverMenu::ENUM_CONTAINER_PARENT);
|
||||
|
||||
// Mark as unread action
|
||||
$oMarkAsUnreadPopoverMenu->AddItem('more-actions', PopoverMenuItemFactory::MakeFromApplicationPopupMenuItem($oViewButton))->SetContainer(PopoverMenu::ENUM_CONTAINER_PARENT);
|
||||
$oMarkAsUnreadPopoverMenu->AddItem('more-actions', PopoverMenuItemFactory::MakeFromApplicationPopupMenuItem($oDeleteButton))->SetContainer(PopoverMenu::ENUM_CONTAINER_PARENT);
|
||||
|
||||
|
||||
// Mark as unread action
|
||||
$sMarkAsReadUrl = Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.mark_as_read', ['notification_id' => $oEvent->GetKey(), 'token' => $sCSRFToken]);
|
||||
$oMarkAsReadButton->SetOnClickJsCode(
|
||||
<<<JS
|
||||
let oSelf = this;
|
||||
$.ajax({
|
||||
url: '{$sMarkAsReadUrl}',
|
||||
type: 'POST',
|
||||
success: function(data) {
|
||||
if (data.status === 'success') {
|
||||
$(oSelf).parent('.ibo-button-group').addClass('ibo-is-hidden');
|
||||
$(oSelf).parent('.ibo-button-group').siblings('.ibo-button-group').removeClass('ibo-is-hidden');
|
||||
$(oSelf).parents('.ibo-object-summary').removeClass('ibo-notifications--view-all--item--unread').addClass('ibo-notifications--view-all--item--read');
|
||||
CombodoToast.OpenSuccessToast(data.message);
|
||||
$('.ibo-notifications--view-all--container').trigger('itop.notification.read');
|
||||
}
|
||||
else {
|
||||
CombodoToast.OpenErrorToast(data.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
JS
|
||||
);
|
||||
|
||||
$oMarkAsReadButtonGroup = ButtonGroupUIBlockFactory::MakeButtonWithOptionsMenu($oMarkAsReadButton, $oMarkAsReadPopoverMenu);
|
||||
|
||||
$oMarkAsUnreadButton = ButtonUIBlockFactory::MakeForAlternativeSecondaryAction(
|
||||
Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsUnread:Label'),
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsUnread:Label',
|
||||
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsUnread:Label'
|
||||
);
|
||||
$sMarkAsUnreadUrl = Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.mark_as_unread', ['notification_id' => $oEvent->GetKey(), 'token' => $sCSRFToken]);
|
||||
$oMarkAsUnreadButton->SetOnClickJsCode(
|
||||
<<<JS
|
||||
let oSelf = this;
|
||||
$.ajax({
|
||||
url: '{$sMarkAsUnreadUrl}',
|
||||
type: 'POST',
|
||||
success: function(data) {
|
||||
if (data.status === 'success') {
|
||||
$(oSelf).parent('.ibo-button-group').addClass('ibo-is-hidden');
|
||||
$(oSelf).parent('.ibo-button-group').siblings('.ibo-button-group').removeClass('ibo-is-hidden');
|
||||
$(oSelf).parents('.ibo-object-summary').removeClass('ibo-notifications--view-all--item--read').addClass('ibo-notifications--view-all--item--unread');
|
||||
CombodoToast.OpenSuccessToast(data.message);
|
||||
$('.ibo-notifications--view-all--container').trigger('itop.notification.unread');
|
||||
}
|
||||
else {
|
||||
CombodoToast.OpenErrorToast(data.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
JS
|
||||
);
|
||||
|
||||
$oMarkAsUnreadButtonGroup = ButtonGroupUIBlockFactory::MakeButtonWithOptionsMenu($oMarkAsUnreadButton, $oMarkAsUnreadPopoverMenu);
|
||||
|
||||
// Add actions to the object summary block and remove old button
|
||||
$oOldButtonId = $oEventBlock->GetActions()->GetId();
|
||||
$oEventBlock->RemoveSubBlock($oOldButtonId);
|
||||
$oEventBlock->SetToolBlocks([$oMarkAsReadButtonGroup, $oMarkAsUnreadButtonGroup]);
|
||||
$oActionsBlock = new UIContentBlock();
|
||||
$oActionsBlock->AddSubBlock($oMarkAsReadButtonGroup);
|
||||
$oActionsBlock->AddSubBlock($oMarkAsUnreadButtonGroup);
|
||||
$oEventBlock->SetActions($oActionsBlock);
|
||||
|
||||
// Display the right button depending on the read status
|
||||
if($oEvent->Get('read') === 'no'){
|
||||
$oMarkAsUnreadButtonGroup->SetCSSClasses(['ibo-is-hidden']);
|
||||
}
|
||||
else{
|
||||
$oMarkAsReadButtonGroup->SetCSSClasses(['ibo-is-hidden']);
|
||||
}
|
||||
|
||||
$oMainContentBlock->AddSubBlock($oEventBlock);
|
||||
}
|
||||
|
||||
// Add empty content block
|
||||
$oEmptyContentBlock = new UIContentBlock('ibo-notifications--view-all--empty', ['ibo-notifications--view-all--empty', 'ibo-svg-illustration--container']);
|
||||
$oEmptyContentBlock->AddSubBlock(new Html(file_get_contents(APPROOT.'/images/illustrations/undraw_social_serenity.svg')));
|
||||
$oEmptyContentBlock->AddSubBlock(TitleUIBlockFactory::MakeNeutral(Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Empty:Title')));
|
||||
$oPage->AddUiBlock($oEmptyContentBlock);
|
||||
|
||||
// Hide empty content block if there are notifications
|
||||
if($oSet->Count() === 0){
|
||||
$oMainContentBlock->AddCSSClass('ibo-is-hidden');
|
||||
}
|
||||
else {
|
||||
$oEmptyContentBlock->AddCSSClass('ibo-is-hidden');
|
||||
}
|
||||
|
||||
return $oPage;
|
||||
}
|
||||
|
||||
@@ -159,4 +641,163 @@ HTML;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Combodo\iTop\Application\WebPage\JsonPage
|
||||
*/
|
||||
public function OperationMarkAsUnread(): JsonPage
|
||||
{
|
||||
$oPage = new JsonPage();
|
||||
$oPage->SetData($this->PerformActionOnSingleNotification('mark_as_unread'));
|
||||
$oPage->SetOutputDataOnly(true);
|
||||
return $oPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Combodo\iTop\Application\WebPage\JsonPage
|
||||
*/
|
||||
public function OperationMarkAsRead(): JsonPage
|
||||
{
|
||||
$oPage = new JsonPage();
|
||||
$oPage->SetData($this->PerformActionOnSingleNotification('mark_as_read'));
|
||||
$oPage->SetOutputDataOnly(true);
|
||||
return $oPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Combodo\iTop\Application\WebPage\JsonPage
|
||||
*/
|
||||
public function OperationDeleteEvent(): JsonPage
|
||||
{
|
||||
$oPage = new JsonPage();
|
||||
$oPage->SetData($this->PerformActionOnSingleNotification('delete'));
|
||||
$oPage->SetOutputDataOnly(true);
|
||||
return $oPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Combodo\iTop\Application\WebPage\JsonPage
|
||||
*/
|
||||
public function OperationMarkMultipleAsRead(): JsonPage
|
||||
{
|
||||
$oPage = new JsonPage();
|
||||
$oPage->SetData($this->PerformActionOnMultipleNotifications('mark_as_read'));
|
||||
$oPage->SetOutputDataOnly(true);
|
||||
return $oPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Combodo\iTop\Application\WebPage\JsonPage
|
||||
*/
|
||||
public function OperationMarkMultipleAsUnread(): JsonPage
|
||||
{
|
||||
$oPage = new JsonPage();
|
||||
$oPage->SetData($this->PerformActionOnMultipleNotifications('mark_as_unread'));
|
||||
$oPage->SetOutputDataOnly(true);
|
||||
return $oPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Combodo\iTop\Application\WebPage\JsonPage
|
||||
*/
|
||||
public function OperationDeleteMultiple(): JsonPage
|
||||
{
|
||||
$oPage = new JsonPage();
|
||||
$oPage->SetData($this->PerformActionOnMultipleNotifications('delete'));
|
||||
$oPage->SetOutputDataOnly(true);
|
||||
return $oPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sAction
|
||||
*
|
||||
* @return string[]
|
||||
* @throws \SecurityException
|
||||
*/
|
||||
protected function PerformActionOnSingleNotification(string $sAction): array
|
||||
{
|
||||
$iNotificationId = utils::ReadParam('notification_id', 0, false, utils::ENUM_SANITIZATION_FILTER_INTEGER);
|
||||
return $this->PerformAction($sAction, [$iNotificationId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sAction
|
||||
*
|
||||
* @return string[]
|
||||
* @throws \SecurityException
|
||||
*/
|
||||
protected function PerformActionOnMultipleNotifications(string $sAction): array
|
||||
{
|
||||
$aNotificationIds = utils::ReadParam('notification_ids', []);
|
||||
return $this->PerformAction($sAction, $aNotificationIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sAction
|
||||
* @param array $aNotificationIds
|
||||
*
|
||||
* @return string[]
|
||||
* @throws \SecurityException
|
||||
*/
|
||||
protected function PerformAction(string $sAction, array $aNotificationIds): array
|
||||
{
|
||||
$sCSRFToken = utils::ReadParam('token', '', false, 'raw_data');
|
||||
if(utils::IsTransactionValid($sCSRFToken, false) === false){
|
||||
throw new SecurityException('Invalid CSRF token');
|
||||
}
|
||||
|
||||
$sActionAsCamelCase = utils::ToCamelCase($sAction);
|
||||
$aReturnData = [
|
||||
'status' => 'error',
|
||||
'message' => 'Invalid notification(s)'
|
||||
];
|
||||
|
||||
// Check action type
|
||||
if (false === in_array($sAction, ['mark_as_read', 'mark_as_unread', 'delete'])) {
|
||||
$aReturnData['message'] = Dict::S("UI:Newsroom:iTopNotification:ViewAllPage:Action:InvalidAction:Message");
|
||||
return $aReturnData;
|
||||
}
|
||||
|
||||
// No ID passed to the API
|
||||
if (count($aNotificationIds) === 0) {
|
||||
$aReturnData['message'] = Dict::S("UI:Newsroom:iTopNotification:ViewAllPage:Action:$sActionAsCamelCase:NoEvent:Message");
|
||||
return $aReturnData;
|
||||
}
|
||||
|
||||
try {
|
||||
$sRepositoryMethodName = "SearchNotificationsTo{$sActionAsCamelCase}ByContact";
|
||||
$oSet = NotificationsRepository::GetInstance()->$sRepositoryMethodName(UserRights::GetContactId(), $aNotificationIds);
|
||||
|
||||
// No notification found
|
||||
$iCount = $oSet->Count();
|
||||
if($iCount === 0) {
|
||||
$aReturnData['message'] = Dict::S("UI:Newsroom:iTopNotification:ViewAllPage:Action:$sActionAsCamelCase:NoEvent:Message");
|
||||
return $aReturnData;
|
||||
}
|
||||
|
||||
while ($oEvent = $oSet->Fetch()) {
|
||||
if ($sAction === 'mark_as_read') {
|
||||
$oEvent->Set('read', 'yes');
|
||||
$oEvent->SetCurrentDate('read_date');
|
||||
$oEvent->DBWrite();
|
||||
} elseif ($sAction === 'mark_as_unread') {
|
||||
$oEvent->Set('read', 'no');
|
||||
$oEvent->DBWrite();
|
||||
} elseif ($sAction === 'delete') {
|
||||
$oEvent->DBDelete();
|
||||
}
|
||||
}
|
||||
|
||||
$aReturnData['status'] = 'success';
|
||||
if ($iCount === 1) {
|
||||
$aReturnData['message'] = Dict::S("UI:Newsroom:iTopNotification:ViewAllPage:Action:{$sActionAsCamelCase}:Success:Message");
|
||||
} else {
|
||||
$aReturnData['message'] = Dict::Format("UI:Newsroom:iTopNotification:ViewAllPage:Action:{$sActionAsCamelCase}Multiple:Success:Message", $iCount);
|
||||
}
|
||||
} catch (Exception $oException) {
|
||||
$aReturnData['message'] = $oException->getMessage();
|
||||
}
|
||||
|
||||
return $aReturnData;
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,12 @@
|
||||
|
||||
namespace Combodo\iTop\Service\Notification;
|
||||
|
||||
use BinaryExpression;
|
||||
use DBObjectSearch;
|
||||
use DBObjectSet;
|
||||
use Expression;
|
||||
use FieldExpression;
|
||||
use VariableExpression;
|
||||
|
||||
/**
|
||||
* Class NotificationsRepository
|
||||
@@ -44,6 +48,74 @@ class NotificationsRepository
|
||||
// Don't do anything, we don't want to be initialized
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iContactId ID of the contact to retrieve notifications for
|
||||
* @param array $aNotificationIds Optional IDs of the notifications to retrieve, if omitted all notifications will be retrieved
|
||||
*
|
||||
* @return \DBObjectSet Set of notifications for $iContactId, optionally limited to $aNotificationIds
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function SearchNotificationsByContact(int $iContactId, array $aNotificationIds = []): DBObjectSet
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL("SELECT EventiTopNotification WHERE contact_id = :contact_id");
|
||||
$aParams = [
|
||||
"contact_id" => $iContactId,
|
||||
];
|
||||
|
||||
if (count($aNotificationIds) > 0) {
|
||||
$oSearch->AddConditionExpression(Expression::FromOQL("{$oSearch->GetClassAlias()}.id IN (:notification_ids)"));
|
||||
$aParams["notification_ids"] = $aNotificationIds;
|
||||
}
|
||||
|
||||
return new DBObjectSet($oSearch, [], $aParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iContactId ID of the contact to retrieve unread notifications for
|
||||
* @param array $aNotificationIds Optional IDs of the unread notifications to retrieve, if omitted all unread notifications will be retrieved
|
||||
*
|
||||
* @return \DBObjectSet Set of unread notifications for $iContactId, optionally limited to $aNotificationIds
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function SearchNotificationsToMarkAsReadByContact(int $iContactId, array $aNotificationIds = []): DBObjectSet
|
||||
{
|
||||
$oSet = $this->SearchNotificationsByContact($iContactId, $aNotificationIds);
|
||||
$oSet->GetFilter()->AddCondition("read", "=", "no");
|
||||
|
||||
return $oSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iContactId ID of the contact to retrieve read notifications for
|
||||
* @param array $aNotificationIds Optional IDs of the read notifications to retrieve, if omitted all read notifications will be retrieved
|
||||
*
|
||||
* @return \DBObjectSet Set of read notifications for $iContactId, optionally limited to $aNotificationIds
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function SearchNotificationsToMarkAsUnreadByContact(int $iContactId, array $aNotificationIds = []): DBObjectSet
|
||||
{
|
||||
$oSet = $this->SearchNotificationsByContact($iContactId, $aNotificationIds);
|
||||
$oSet->GetFilter()->AddCondition("read", "=", "yes");
|
||||
|
||||
return $oSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iContactId ID of the contact to retrieve read notifications for
|
||||
* @param array $aNotificationIds Optional IDs of the notifications to retrieve, if omitted all notifications will be retrieved
|
||||
*
|
||||
* @return \DBObjectSet Set of notifications for $iContactId, optionally limited to $aNotificationIds
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function SearchNotificationsToDeleteByContact(int $iContactId, array $aNotificationIds = []): DBObjectSet
|
||||
{
|
||||
return $this->SearchNotificationsByContact($iContactId, $aNotificationIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for subscriptions by contact ID.
|
||||
*
|
||||
|
||||
7
templates/base/components/input/input-toggler.html.twig
Normal file
7
templates/base/components/input/input-toggler.html.twig
Normal file
@@ -0,0 +1,7 @@
|
||||
{% extends "base/components/input/layout.html.twig" %}
|
||||
{% block iboInput %}
|
||||
<span class="ibo-toggler--wrapper">
|
||||
{{ parent() }}
|
||||
<span class="ibo-toggler--slider"></span>
|
||||
</span>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,5 @@
|
||||
$('#{{ oUIBlock.GetId() }}').parent().on('click', function() {
|
||||
let oInput = $(this).find('.ibo-toggler');
|
||||
oInput.prop('checked', !oInput.prop('checked'));
|
||||
oInput.trigger('change');
|
||||
});
|
||||
@@ -5,6 +5,7 @@ namespace Combodo\iTop\Test\UnitTest\Core;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use CoreException;
|
||||
use DBObjectSearch;
|
||||
use MetaModel;
|
||||
|
||||
/**
|
||||
@@ -456,6 +457,31 @@ class MetaModelTest extends ItopDataTestCase
|
||||
'Non existing person' => [10, false],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws CoreException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function testPurgeData(){
|
||||
// Set max_chunk_size to 2 (default 1000) to test chunk deletion with only 10 items
|
||||
$oConfig = MetaModel::GetConfig();
|
||||
$oConfig->Set('purge_data.max_chunk_size', 2);
|
||||
MetaModel::SetConfig($oConfig);
|
||||
|
||||
$aPkPerson = [];
|
||||
for ($i=0; $i < 10; $i++) {
|
||||
$oPerson = $this->CreatePerson($i, 1);
|
||||
$sClass = get_class($oPerson);
|
||||
$aPkPerson[] = $oPerson->GetKey();
|
||||
}
|
||||
|
||||
$sDeleteOQL = 'SELECT '.$sClass.' WHERE id IN ('.implode(',', $aPkPerson).')';
|
||||
$oFilter = DBObjectSearch::FromOQL($sDeleteOQL);
|
||||
|
||||
$iNbDelete = MetaModel::PurgeData($oFilter);
|
||||
$this->assertEquals($iNbDelete, 10, 'MetaModel::PurgeData must delete 10 objects per batch of 2 items');
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Wizzard
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
|
||||
</itop_design>
|
||||
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
|
||||
</itop_design>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
|
||||
<branding/>
|
||||
</itop_design>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
|
||||
<branding>
|
||||
<main_favicon _delta="define">
|
||||
<fileref ref="logo_rvb"/>
|
||||
</main_favicon>
|
||||
<login_favicon _delta="define">
|
||||
<fileref ref="logo_log"/>
|
||||
</login_favicon>
|
||||
<portal_favicon _delta="define">
|
||||
<fileref ref="logo_log"/>
|
||||
</portal_favicon>
|
||||
</branding>
|
||||
</itop_design>
|
||||
@@ -143,6 +143,8 @@ class iTopDesignFormatTest extends ItopTestCase
|
||||
'3.0 to 1.7' => ['sXmlFileName' => '3.0_to_1.7'],
|
||||
'3.0 to 3.1' => ['sXmlFileName' => '3.0_to_3.1'],
|
||||
'3.1 to 3.0' => ['sXmlFileName' => '3.1_to_3.0'],
|
||||
'3.1 to 3.2' => ['sXmlFileName' => '3.1_to_3.2'],
|
||||
'3.2 to 3.1' => ['sXmlFileName' => '3.2_to_3.1'],
|
||||
'Bug_4569' => ['sXmlFileName' => 'Bug_4569'],
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user