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:
@@ -29,6 +29,7 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use UserRights;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Class UserProvider
|
||||
@@ -44,6 +45,8 @@ class UserProvider
|
||||
private $sPortalId;
|
||||
/** @var \User $oUser */
|
||||
private $oUser;
|
||||
/** @var bool $bUserCanLogOff Whether the current user can log off or not */
|
||||
private $bUserCanLogOff;
|
||||
/** @var array $aAllowedPortals */
|
||||
private $aAllowedPortals;
|
||||
|
||||
@@ -89,6 +92,9 @@ class UserProvider
|
||||
throw new Exception('Could not load connected user.');
|
||||
}
|
||||
|
||||
// User allowed to log off or not
|
||||
$this->bUserCanLogOff = utils::CanLogOff();
|
||||
|
||||
// Allowed portals
|
||||
$aAllowedPortals = UserRights::GetAllowedPortals();
|
||||
|
||||
@@ -119,6 +125,15 @@ class UserProvider
|
||||
return $this->oUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool {@see static::$bUserCanLogOff}
|
||||
* @since 3.1.2 3.2.0
|
||||
*/
|
||||
public function getCurrentUserCanLogOff(): bool
|
||||
{
|
||||
return $this->bUserCanLogOff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get allowed portals.
|
||||
*
|
||||
|
||||
@@ -61,4 +61,13 @@ class CurrentUserAccessor
|
||||
{
|
||||
return $this->userProvider->getCurrentUser()->Get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @since 3.1.2 3.2.0
|
||||
*/
|
||||
public function CanLogOff(): bool
|
||||
{
|
||||
return $this->userProvider->getCurrentUserCanLogOff();
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,13 @@
|
||||
|
||||
{% if app['combodo.current_user'] is defined and app['combodo.current_user'] is not null %}
|
||||
{% set bUserConnected = true %}
|
||||
{% set bUserCanLogOff = app['combodo.current_user'].CanLogOff() %}
|
||||
{% set sUserFullname = app['combodo.current_user'].Get('first_name') ~ ' ' ~ app['combodo.current_user'].Get('last_name') %}
|
||||
{% set sUserEmail = app['combodo.current_user'].Get('email') %}
|
||||
{% set sUserPhotoUrl = app['combodo.current_contact.photo_url'] %}
|
||||
{% else %}
|
||||
{% set bUserConnected = false %}
|
||||
{% set bUserCanLogOff = false %}
|
||||
{% set sUserFullname = '' %}
|
||||
{% set sUserEmail = '' %}
|
||||
{% set sUserPhotoUrl = app['combodo.portal.base.absolute_url'] ~ 'img/user-profile-default-256px.png' %}
|
||||
@@ -127,15 +129,13 @@
|
||||
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/ckeditor/adapters/jquery.js'|add_itop_version }}"></script>
|
||||
{# - Hilighter for code snippets created with CKEditor #}
|
||||
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/ckeditor/plugins/codesnippet/lib/highlight/highlight.pack.js'|add_itop_version }}"></script>
|
||||
{# - Custom settings #}
|
||||
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/ckeditor.on-init.js'|add_itop_version }}"></script>
|
||||
{# Date-time picker for Bootstrap #}
|
||||
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js'|add_itop_version }}"></script>
|
||||
{# Typeahead files for autocomplete #}
|
||||
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/typeahead/js/typeahead.bundle.min.js'|add_itop_version }}"></script>
|
||||
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/handlebars/js/handlebars.min-768ddbd.js'|add_itop_version }}"></script>
|
||||
|
||||
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/leave_handler.js'|add_itop_version }}"></script>
|
||||
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'js/portal_leave_handler.js'|add_itop_version }}"></script>
|
||||
|
||||
{# Selectize for sets #}
|
||||
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/selectize.js'|add_itop_version }}"></script>
|
||||
@@ -232,11 +232,13 @@
|
||||
<li><a href="{{ aPortal.url }}" target="_blank"><span class="brick_icon {{ sIconClass }} fa-2x fa-fw"></span>{{ aPortal.label|dict_s }}</a></li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{# We display the separator only if the user has more then 1 portal. Meaning default portal + console or several portal at least #}
|
||||
{% if allowed_portals|length > 1 %}
|
||||
<li role="separator" class="divider"></li>
|
||||
{% endif %}
|
||||
<li><a href="{{ app['combodo.absolute_url'] }}pages/logoff.php"><span class="brick_icon fas fa-sign-out-alt fa-2x fa-fw"></span>{{ 'Brick:Portal:UserProfile:Navigation:Dropdown:Logout'|dict_s }}</a></li>
|
||||
{% if bUserCanLogOff %}
|
||||
{# We display the separator only if the user has more then 1 portal. Meaning default portal + console or several portal at least #}
|
||||
{% if allowed_portals|length > 1 %}
|
||||
<li role="separator" class="divider"></li>
|
||||
{% endif %}
|
||||
<li><a href="{{ app['combodo.absolute_url'] }}pages/logoff.php"><span class="brick_icon fas fa-sign-out-alt fa-2x fa-fw"></span>{{ 'Brick:Portal:UserProfile:Navigation:Dropdown:Logout'|dict_s }}</a></li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -271,11 +273,13 @@
|
||||
<li><a href="{{ aPortal.url }}" {% if app['combodo.portal.instance.conf'].properties.allowed_portals.opening_mode == 'tab' %}target="_blank"{% endif %} title="{{ aPortal.label|dict_s }}"><span class="brick_icon {{ sGlyphiconClass }} fa-lg fa-fw"></span>{{ aPortal.label|dict_s }}</a></li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{# We display the separator only if the user has more then 1 portal. Meaning default portal + console or several portal at least #}
|
||||
{% if allowed_portals|length > 1 %}
|
||||
<li role="separator" class="divider"></li>
|
||||
{% endif %}
|
||||
<li><a href="{{ app['combodo.absolute_url'] }}pages/logoff.php"><span class="brick_icon fas fa-sign-out-alt fa-lg fa-fw"></span>{{ 'Brick:Portal:UserProfile:Navigation:Dropdown:Logout'|dict_s }}</a></li>
|
||||
{% if bUserCanLogOff %}
|
||||
{# We display the separator only if the user has more then 1 portal. Meaning default portal + console or several portal at least #}
|
||||
{% if allowed_portals|length > 1 %}
|
||||
<li role="separator" class="divider"></li>
|
||||
{% endif %}
|
||||
<li><a href="{{ app['combodo.absolute_url'] }}pages/logoff.php"><span class="brick_icon fas fa-sign-out-alt fa-lg fa-fw"></span>{{ 'Brick:Portal:UserProfile:Navigation:Dropdown:Logout'|dict_s }}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -539,12 +543,7 @@
|
||||
});
|
||||
|
||||
// Initialize confirmation message handler when a form with touched fields is closed
|
||||
oBodyElem.leave_handler({
|
||||
'message': '{{ 'Portal:Form:Close:Warning'|dict_s }}',
|
||||
'extra_events': {
|
||||
'body': ['hide.bs.modal']
|
||||
}
|
||||
});
|
||||
oBodyElem.portal_leave_handler({'message': '{{ 'Portal:Form:Close:Warning'|dict_s }}'});
|
||||
{% endblock %}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -59,9 +59,6 @@ class NotificationsCenterController extends Controller
|
||||
$oNotificationsPanel = new Panel(Dict::S('UI:NotificationsCenter:Panel:Title'), array(), 'grey', 'ibo-notifications-center');
|
||||
$oNotificationsPanel->AddCSSClass('ibo-datatable-panel');
|
||||
$oSubtitleBlock = new UIContentBlock(null, ['ibo-notifications-center--sub-title']);
|
||||
$sDisplayAdvancedPageUrl = Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.display_advanced_page', [], true);
|
||||
$oSubtitleBlock->AddSubBlock(new Html(Dict::Format('UI:NotificationsCenter:Panel:SubTitle', $sDisplayAdvancedPageUrl)));
|
||||
$oNotificationsPanel->SetSubTitleBlock($oSubtitleBlock);
|
||||
$oNotificationsCenterTableColumns = [
|
||||
'trigger' => array('label' => MetaModel::GetName('Trigger')),
|
||||
'trigger_class' => array('label' => MetaModel::GetAttributeDef('Trigger', 'finalclass')->GetLabel()),
|
||||
@@ -267,205 +264,6 @@ JS
|
||||
|
||||
return $oPage;
|
||||
}
|
||||
|
||||
public function OperationDisplayAdvancedPage(){
|
||||
$oPage = new iTopWebPage(Dict::S('UI:NotificationsCenter:Page:Title'));
|
||||
// Create a panel that will contain the table
|
||||
$oNotificationsPanel = new Panel(Dict::S('UI:NotificationsCenter:Panel:Title'), array(), 'grey', 'ibo-notifications-center');
|
||||
$oSubtitleBlock = new UIContentBlock(null, ['ibo-notifications-center--sub-title']);
|
||||
$sDisplayAdvancedPageUrl = Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.display_page', [], true);
|
||||
$oSubtitleBlock->AddSubBlock(new Html(Dict::Format('UI:NotificationsCenter:Panel:Advanced:SubTitle', $sDisplayAdvancedPageUrl)));
|
||||
$oNotificationsPanel->SetSubTitleBlock($oSubtitleBlock);
|
||||
$oPage->AddUiBlock($oNotificationsPanel);
|
||||
|
||||
// Get all subscribed/unsubscribed actions notifications for the current user
|
||||
$oLnkNotificationsSet = NotificationsRepository::GetInstance()->SearchSubscriptionsByContact(\UserRights::GetContactId());
|
||||
$oActionsNotificationsByTrigger = [];
|
||||
$aSubscribedActionsNotificationsByTrigger = [];
|
||||
while ($oLnkActionsNotifications = $oLnkNotificationsSet->Fetch()) {
|
||||
$oSubscribedActionNotification = MetaModel::GetObject(ActionNotification::class, $oLnkActionsNotifications->Get('action_id'));
|
||||
$oTrigger = MetaModel::GetObject('Trigger', $oLnkActionsNotifications->Get('trigger_id'));
|
||||
$iTriggerId = $oTrigger->GetKey();
|
||||
// Create a new array for the trigger if it doesn't exist
|
||||
if (!isset($oActionsNotificationsByTrigger[$iTriggerId])) {
|
||||
$oActionsNotificationsByTrigger[$iTriggerId] = [];
|
||||
$aSubscribedActionsNotificationsByTrigger[$iTriggerId] = [];
|
||||
}
|
||||
// Add the action notification to the list of actions notifications for the trigger
|
||||
$oActionsNotificationsByTrigger[$iTriggerId][] = $oSubscribedActionNotification;
|
||||
// Add the subscribed status to the list of subscribed actions notifications for the trigger
|
||||
$aSubscribedActionsNotificationsByTrigger[$iTriggerId][$oSubscribedActionNotification->GetKey()] = $oLnkActionsNotifications->Get('subscribed') || $oTrigger->Get('subscription_policy') === SubscriptionPolicy::ForceAllChannels->value;
|
||||
}
|
||||
|
||||
$oPage->AddTabContainer('NotificationsCenter', '', $oNotificationsPanel);
|
||||
$oPage->SetCurrentTabContainer('NotificationsCenter');
|
||||
// Create a new tab for each trigger
|
||||
foreach ($oActionsNotificationsByTrigger as $iTriggerId => $aActionsNotifications) {
|
||||
$oTrigger = MetaModel::GetObject('Trigger', $iTriggerId, false);
|
||||
if ($oTrigger === null) {
|
||||
continue;
|
||||
}
|
||||
foreach ($aActionsNotifications as $oActionNotification) {
|
||||
$oPage->SetCurrentTab(MetaModel::GetName(get_class($oActionNotification)));
|
||||
$oCheckBox = InputUIBlockFactory::MakeForInputWithLabel(
|
||||
Dict::Format('UI:NotificationsCenter:Advanced:Input:Label', $oTrigger->Get('description'), $oActionNotification->Get('name')),
|
||||
$oTrigger->GetKey().'|'.$oActionNotification->GetKey(),
|
||||
"",
|
||||
$oTrigger->GetKey().'|'.$oActionNotification->GetKey(),
|
||||
"checkbox"
|
||||
);
|
||||
$oCheckBox->GetInput()->SetIsChecked($aSubscribedActionsNotificationsByTrigger[$iTriggerId][$oActionNotification->GetKey()] === true);
|
||||
$oCheckBox->SetBeforeInput(false);
|
||||
$oCheckBox->GetInput()->AddCSSClass('ibo-input--label-right');
|
||||
$oCheckBox->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
$oContainer = new UIContentBlock(null, ['ibo-notifications-center-advanced--input--container']);
|
||||
$oContainer->AddSubBlock($oCheckBox);
|
||||
$oPage->AddUiBlock($oContainer);
|
||||
}
|
||||
}
|
||||
$sSubscribeUrl = Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.subscribe', [], true);
|
||||
$sUnsubscribeUrl = Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.unsubscribe', [], true);
|
||||
$sCSRFToken = utils::GetNewTransactionId();
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
$('.ibo-notifications-center-advanced--input--container .ibo-input-checkbox').on('change', function(){
|
||||
let sUrl = '{$sUnsubscribeUrl}';
|
||||
if ($(this).prop("checked")) {
|
||||
sUrl = '{$sSubscribeUrl}'
|
||||
}
|
||||
$.ajax({
|
||||
url: sUrl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
channel: $(this).attr('name'),
|
||||
token: '{$sCSRFToken}',
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
if (data.status === 'success') {
|
||||
// Display success message
|
||||
CombodoToast.OpenSuccessToast(data.message);
|
||||
}
|
||||
else {
|
||||
CombodoToast.OpenErrorToast(data.message);
|
||||
}
|
||||
},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
CombodoToast.OpenErrorToast(data.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
JS
|
||||
);
|
||||
|
||||
return $oPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \JsonPage
|
||||
*/
|
||||
function OperationUnsubscribe()
|
||||
{
|
||||
// Get the CSRF token from the request and check if it's valid
|
||||
if (!$this->CheckPostedCSRF()) {
|
||||
throw new \Exception('Invalid token');
|
||||
}
|
||||
|
||||
$sChannel = utils::ReadParam('channel', '', true, 'raw_data');
|
||||
$aChannel = explode('|', $sChannel);
|
||||
$oPage = new \JsonPage();
|
||||
$aReturnData = [];
|
||||
try {
|
||||
if (count($aChannel) !== 2) {
|
||||
throw new \Exception('Invalid channel');
|
||||
}
|
||||
$iTriggerKey = $aChannel[0];
|
||||
$iActionNotificationKey = $aChannel[1];
|
||||
$oTrigger = MetaModel::GetObject('Trigger', $iTriggerKey, false);
|
||||
if ($oTrigger === null) {
|
||||
throw new \Exception('Invalid trigger');
|
||||
}
|
||||
$oActionNotification = MetaModel::GetObject('ActionNotification', $iActionNotificationKey, false);
|
||||
if ($oActionNotification === null) {
|
||||
throw new \Exception('Invalid action notification');
|
||||
}
|
||||
$oSubscribedActionsNotificationsSet = NotificationsRepository::GetInstance()->SearchSubscribedSubscriptionsByTriggerContactAndAction($iTriggerKey, $iActionNotificationKey);
|
||||
if ($oSubscribedActionsNotificationsSet->Count() === 0) {
|
||||
throw new \Exception('You are not subscribed to this channel');
|
||||
}
|
||||
while ($oSubscribedActionsNotifications = $oSubscribedActionsNotificationsSet->Fetch()) {
|
||||
$oSubscribedActionsNotifications->Set('subscribed', false);
|
||||
$oSubscribedActionsNotifications->DBUpdate();
|
||||
}
|
||||
$aReturnData = [
|
||||
'status' => 'success',
|
||||
'message' => Dict::S('UI:NotificationsCenter:Unsubscribe:Success'),
|
||||
];
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$aReturnData = [
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage(),
|
||||
];
|
||||
}
|
||||
$oPage->SetData($aReturnData);
|
||||
$oPage->SetOutputDataOnly(true);
|
||||
|
||||
return $oPage;
|
||||
}
|
||||
|
||||
function OperationSubscribe()
|
||||
{
|
||||
|
||||
// Get the CSRF token from the request and check if it's valid
|
||||
if (!$this->CheckPostedCSRF()) {
|
||||
throw new \Exception('Invalid token');
|
||||
}
|
||||
|
||||
$sChannel = utils::ReadParam('channel', '', true, 'raw_data');
|
||||
$aChannel = explode('|', $sChannel);
|
||||
|
||||
$oPage = new \JsonPage();
|
||||
$aReturnData = [];
|
||||
try {
|
||||
if (count($aChannel) !== 2) {
|
||||
throw new \Exception('Invalid channel');
|
||||
}
|
||||
$iTriggerKey = $aChannel[0];
|
||||
$iActionNotificationKey = $aChannel[1];
|
||||
$oTrigger = MetaModel::GetObject('Trigger', $iTriggerKey, false);
|
||||
if ($oTrigger === null) {
|
||||
throw new \Exception('Invalid trigger');
|
||||
}
|
||||
$oActionNotification = MetaModel::GetObject('ActionNotification', $iActionNotificationKey, false);
|
||||
if ($oActionNotification === null) {
|
||||
throw new \Exception('Invalid action notification');
|
||||
}
|
||||
$oSubscribedActionsNotificationsSet = NotificationsRepository::GetInstance()->SearchUnsubscribedSubscriptionsByTriggerContactAndAction($iTriggerKey, $iActionNotificationKey);
|
||||
if ($oSubscribedActionsNotificationsSet->Count() === 0) {
|
||||
throw new \Exception('You are not allow to subscribe to this channel');
|
||||
}
|
||||
while ($oSubscribedActionsNotifications = $oSubscribedActionsNotificationsSet->Fetch()) {
|
||||
$oSubscribedActionsNotifications->Set('subscribed', true);
|
||||
$oSubscribedActionsNotifications->DBUpdate();
|
||||
}
|
||||
$aReturnData = [
|
||||
'status' => 'success',
|
||||
'message' => Dict::S('UI:NotificationsCenter:Subscribe:Success'),
|
||||
];
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$aReturnData = [
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage(),
|
||||
];
|
||||
}
|
||||
$oPage->SetData($aReturnData);
|
||||
$oPage->SetOutputDataOnly(true);
|
||||
|
||||
return $oPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \JsonPage
|
||||
|
||||
@@ -1961,6 +1961,12 @@ class SynchroLog extends DBObject
|
||||
{
|
||||
$this->TraceToText();
|
||||
$sMemPeak = max($this->Get('memory_usage_peak'), ExecutionKPI::memory_get_peak_usage());
|
||||
|
||||
// memory peak overflow protection
|
||||
if($sMemPeak > 2147483647){
|
||||
$sMemPeak = 2147483647;
|
||||
}
|
||||
|
||||
$this->Set('memory_usage_peak', $sMemPeak);
|
||||
parent::OnUpdate();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user