PHRAS-3442_optimize-list-notifications_4.1-bis

WIP
poll only from menubar (Aina)
back : only one method/route to fetch notifs
front : fix exponential pagination
big refacto
todo : mark read notifications (button ? click ?)
This commit is contained in:
jygaulier
2021-06-15 17:46:14 +02:00
parent eec8a92ce9
commit 22c3b273f7
24 changed files with 710 additions and 628 deletions

View File

@@ -1,7 +1,7 @@
import $ from 'jquery';
import ui from '../ui';
import notify from '../notify';
// poll notification is now from menu bar
// so this is never called
const basket = () => {
const onUpdatedContent = (data) => {

View File

@@ -4,6 +4,8 @@ import notifyService from './notifyService';
import * as Rx from 'rx';
import merge from 'lodash.merge';
// this module is now unused, poll notification is from menu bar
const notify = (services) => {
const { configService, localeService, appEvents } = services;
@@ -63,9 +65,9 @@ const notify = (services) => {
appEvents.emit('notification.refresh', data);
}
// append notification content
notifyLayout(services).addNotifications(data.notifications);
// notifyLayout(services).addNotifications(data.notifications);
window.setTimeout(poll, defaultPollingTime, notificationInstance);
// window.setTimeout(poll, defaultPollingTime, notificationInstance);
return true;
};

View File

@@ -1,5 +1,4 @@
import $ from 'jquery';
import dialog from './../../phraseanet-common/components/dialog';
// import user from '../user/index.js';
@@ -8,34 +7,34 @@ const notifyLayout = (services) => {
const $notificationBoxContainer = $('#notification_box');
const $notificationTrigger = $('.notification_trigger');
let $notificationDialog = $('#notifications-dialog');
let $notificationsContent = null;
let $notificationsNavigation = null;
const initialize = () => {
/**
* click on menubar/notifications : drop a box with last 10 notification, and a button "see all"
* the box content is already set by poll notifications
*/
$notificationTrigger.on('mousedown', (event) => {
event.stopPropagation();
const $target = $(event.currentTarget);
if ($target.hasClass('open')) {
$notificationBoxContainer.hide();
$target.removeClass('open');
clear_notifications();
} else {
$notificationBoxContainer.show();
setBoxHeight();
$target.addClass('open');
read_notifications();
}
});
$(document).on('mousedown', () => {
if ($notificationTrigger.hasClass('open')) {
$notificationTrigger.trigger('click');
}
// toggle
if ($notificationTrigger.hasClass('open')) {
$notificationBoxContainer.hide();
$notificationTrigger.removeClass('open');
clear_notifications();
}
else {
$notificationTrigger.addClass('open');
$notificationBoxContainer.show();
setBoxHeight();
}
});
/**
* close on every mousedown
*/
$(document).on('mousedown', () => {
$notificationBoxContainer.hide();
$notificationTrigger.removeClass('open');
});
$notificationBoxContainer
@@ -48,11 +47,14 @@ const notifyLayout = (services) => {
.on('mouseout', '.notification', (event) => {
$(event.currentTarget).removeClass('hover');
})
/**
* click on "see all notification"
*/
.on('click', '.notification__print-action', (event) => {
event.preventDefault();
const $el = $(event.currentTarget);
const page = $el.data('page');
print_notifications(page);
$notificationBoxContainer.hide();
$notificationTrigger.removeClass('open');
print_notifications(0);
});
$(window).bind('resize', function () {
@@ -62,24 +64,24 @@ const notifyLayout = (services) => {
};
const addNotifications = (notificationContent) => {
// var box = $('#notification_box');
$notificationBoxContainer.empty().append(notificationContent);
if ($notificationBoxContainer.is(':visible')) {
setBoxHeight();
}
if ($('.notification.unread', $notificationBoxContainer).length > 0) {
$('.counter', $notificationTrigger)
.empty()
.append($('.notification.unread', $notificationBoxContainer).length);
$('.counter', $notificationTrigger).css('visibility', 'visible');
} else {
$('.notification_trigger .counter').css('visibility', 'hidden').empty();
}
};
// const addNotifications = (notificationContent) => {
// // var box = $('#notification_box');
// $notificationBoxContainer.empty().append(notificationContent);
//
// if ($notificationBoxContainer.is(':visible')) {
// setBoxHeight();
// }
//
// if ($('.notification.unread', $notificationBoxContainer).length > 0) {
// $('.counter', $notificationTrigger)
// .empty()
// .append($('.notification.unread', $notificationBoxContainer).length);
// $('.counter', $notificationTrigger).css('visibility', 'visible');
//
// } else {
// $('.notification_trigger .counter').css('visibility', 'hidden').empty();
// }
// };
const setBoxHeight = () => {
@@ -110,20 +112,32 @@ const notifyLayout = (services) => {
}
};
const print_notifications = (page) => {
/**
* add 10 notifications into the dlgbox
* display the button "load more" while relevant
*
* @param offset
*/
const print_notifications = (offset) => {
page = parseInt(page, 10);
offset = parseInt(offset, 10);
var buttons = {};
buttons[localeService.t('fermer')] = function () {
$notificationDialog.dialog('close');
};
// create the dlg div if it does not exists
//
if ($notificationDialog.length === 0) {
$('body').append('<div id="notifications-dialog" class="loading"></div>');
$('body').append('<div id="notifications-dialog"><div class="content"></div><div class="navigation"></div></div>');
$notificationDialog = $('#notifications-dialog');
$notificationsContent = $('.content', $notificationDialog);
$notificationsNavigation = $('.navigation', $notificationDialog);
}
// open the dlg (even if it is already opened when "load more")
//
$notificationDialog
.dialog({
title: $('#notification-title').val(),
@@ -141,20 +155,22 @@ const notifyLayout = (services) => {
close: function (event, ui) {
$notificationDialog.dialog('destroy').remove();
}
}).dialog('option', 'buttons', buttons).dialog('open').on('click','.notification_next .notification__print-action', function (event) {
event.preventDefault();
var $el = $(event.currentTarget);
var page = $el.data('page');
print_notifications(page);
});
})
.dialog('option', 'buttons', buttons)
.dialog('open');
// load 10 (more) notifications
//
$notificationDialog.addClass('loading');
$.ajax({
type: 'GET',
url: '/user/notifications/',
type: 'POST',
// url: '/user/notifications/',
url: '/session/notifications/',
dataType: 'json',
data: {
page: page
'offset': offset,
'limit': 10,
'what': 3, // 3 = read | unread
},
error: function (data) {
$notificationDialog.removeClass('loading');
@@ -165,47 +181,55 @@ const notifyLayout = (services) => {
success: function (data) {
$notificationDialog.removeClass('loading');
if (page === 0) {
$notificationDialog.empty();
} else {
$('.notification_next', $notificationDialog).remove();
if (offset === 0) {
$notificationsContent.empty();
}
const notifications = data.notifications.notifications;
let i = 0;
for (i in data.notifications) {
var id = 'notif_date_' + i;
var date_cont = $('#' + id);
if (date_cont.length === 0) {
$notificationDialog.append('<div id="' + id + '"><div class="notification_title">' + data.notifications[i].display + '</div></div>');
date_cont = $('#' + id);
}
for (i in notifications) {
const notification = notifications[i];
let j = 0;
for (j in data.notifications[i].notifications) {
var loc_dat = data.notifications[i].notifications[j];
var html = '<div style="position:relative;" id="notification_' + loc_dat.id + '" class="notification">' +
'<table style="width:100%;" cellspacing="0" cellpadding="0" border="0"><tr><td style="width:25px;">' +
loc_dat.icon +
'</td><td>' +
'<div style="position:relative;" class="' + loc_dat.classname + '">' +
loc_dat.text + ' <span class="time">' + loc_dat.time + '</span></div>' +
'</td></tr></table>' +
'</div>';
date_cont.append(html);
// group notifs by day
//
const date = notification.created_on_day;
const id = 'notif_date_' + date;
let date_cont = $('#' + id, $notificationsContent);
if (date_cont.length === 0) {
$notificationsContent.append('<div id="' + id + '"><div class="notification_title">' + notifications[i].created_on + '</div></div>');
date_cont = $('#' + id, $notificationsContent);
}
// write notif
let html = '<div style="position:relative;" id="notification_' + notification.id + '" class="notification">' +
'<table style="width:100%;" cellspacing="0" cellpadding="0" border="0"><tr style="border-top: 1px grey solid"><td style="width:25px; vertical-align: top;">' +
'<img src="' + notification.icon + '" style="vertical-align:middle;width:16px;margin:2px;" />' +
'</td><td style="vertical-align: top;">' +
'<div style="position:relative;" class="' + notification.classname + '">' +
notification.text + ' <span class="time">' + notification.time + '</span></div>' +
'</td></tr></table>' +
'</div>';
date_cont.append(html);
}
var next_ln = $.trim(data.next);
if (next_ln !== '') {
$notificationDialog.append('<div class="notification_next">' + next_ln + '</div>');
if (data.notifications.next_page_html) {
$notificationsNavigation
.off('click', '.notification__print-action');
$notificationsNavigation.empty().show().append(data.notifications.next_page_html);
$notificationsNavigation
.on('click', '.notification__print-action', function (event) {
event.preventDefault();
let $el = $(event.currentTarget);
let offset = $el.data('offset');
print_notifications(offset);
});
}
else {
$notificationsNavigation.empty().hide();
}
}
});
};
/* remove in favor of existing /session/ route
const read_notifications = () => {
var notifications = [];
@@ -236,9 +260,10 @@ const notifyLayout = (services) => {
$('.notification_trigger .counter').css('visibility', 'hidden').empty();
};
*/
return {
initialize,
addNotifications
initialize
};
};

View File

@@ -2,6 +2,7 @@ import {Observable} from 'rx';
// import {ajax} from 'jquery';
import $ from 'jquery';
// module unused now 06-2021
let recordService = (services) => {
const {configService} = services;
const url = configService.get('baseUrl');

View File

@@ -1,7 +1,4 @@
import $ from 'jquery';
import ui from '../ui';
import notify from '../notify';
import * as appCommons from './../../phraseanet-common';
const user = (services) => {
const { configService, localeService, appEvents } = services;
@@ -19,53 +16,43 @@ const user = (services) => {
'user.disconnected': onUserDisconnect
});
const manageSession = (...params) => {
let [data, showMessages] = params;
// const manageSession = (...params) => {
// let [data, showMessages] = params;
//
// if (typeof (showMessages) === 'undefined') {
// showMessages = false;
// }
//
// if (showMessages) {
// // @todo: to be moved
// if ($.trim(data.message) !== '') {
// if ($('#MESSAGE').length === 0) {
// $('body').append('<div id="#MESSAGE"></div>');
// }
// $('#MESSAGE')
// .empty()
// .append(data.message + '<div style="margin:20px;"><input type="checkbox" class="dialog_remove" />' + localeService.t('hideMessage') + '</div>')
// .attr('title', 'Global Message')
// .dialog({
// autoOpen: false,
// closeOnEscape: true,
// resizable: false,
// draggable: false,
// modal: true,
// close: function () {
// if ($('.dialog_remove:checked', $(this)).length > 0) {
// // @TODO get from module
// appCommons.userModule.setTemporaryPref('message', 0);
// }
// }
// })
// .dialog('open');
// }
// }
// return true;
// };
if (typeof (showMessages) === 'undefined') {
showMessages = false;
}
if (showMessages) {
// @todo: to be moved
if ($.trim(data.message) !== '') {
if ($('#MESSAGE').length === 0) {
$('body').append('<div id="#MESSAGE"></div>');
}
$('#MESSAGE')
.empty()
.append(data.message + '<div style="margin:20px;"><input style="margin-right:10px;" type="checkbox" class="dialog_remove" />' + localeService.t('hideMessage') + '</div>')
.attr('title', 'Global Message')
.dialog({
autoOpen: false,
closeOnEscape: true,
resizable: false,
draggable: false,
modal: true,
close: function () {
if ($('.dialog_remove:checked', $(this)).length > 0) {
// setTemporaryPref
$.ajax({
type: "POST",
url: "/user/preferences/temporary/",
data: {
prop: 'message',
value: 0
},
success: function (data) {
return;
}
});
}
}
})
.dialog('open');
}
}
return true;
};
return {initialize, manageSession};
return {initialize};
};
export default user;

View File

@@ -36,7 +36,7 @@
/* eslint-disable no-loop-func*/
import $ from 'jquery';
import dialog from './dialog';
let cookie = require('js-cookie');
const initialize = () => {
@@ -56,93 +56,92 @@ const initialize = () => {
};
// @deprecated
function manageSession(data, showMessages) {
if (typeof (showMessages) === 'undefined')
showMessages = false;
if (data.status === 'disconnected' || data.status === 'session') {
disconnected();
return false;
}
if (showMessages) {
let box = $('#notification_box');
box.empty().append(data.notifications);
if (box.is(':visible'))
fix_notification_height();
if ($('.notification.unread', box).length > 0) {
let trigger = $('#notification_trigger');
$('.counter', trigger)
.empty()
.append($('.notification.unread', box).length);
$('.counter', trigger).css('visibility', 'visible');
}
else
$('#notification_trigger .counter').css('visibility', 'hidden').empty();
if (data.changed.length > 0) {
let current_open = $('.SSTT.ui-state-active');
let current_sstt = current_open.length > 0 ? current_open.attr('id').split('_').pop() : false;
let main_open = false;
for (let i = 0; i !== data.changed.length; i++) {
let sstt = $('#SSTT_' + data.changed[i]);
if (sstt.size() === 0) {
if (main_open === false) {
$('#baskets .bloc').animate({'top': 30}, function () {
$('#baskets .alert_datas_changed:first').show();
});
main_open = true;
}
}
else {
if (!sstt.hasClass('active'))
sstt.addClass('unread');
else {
$('.alert_datas_changed', $('#SSTT_content_' + data.changed[i])).show();
}
}
}
}
if ($.trim(data.message) !== '') {
if ($('#MESSAGE').length === 0)
$('body').append('<div id="#MESSAGE"></div>');
$('#MESSAGE')
.empty()
.append('<div style="margin:30px 10px;"><h4><b>' + data.message + '</b></h4></div><div style="margin:20px 0px 10px;"><label class="checkbox"><input type="checkbox" class="dialog_remove" />' + language.hideMessage + '</label></div>')
.attr('title', 'Global Message')
.dialog({
autoOpen: false,
closeOnEscape: true,
resizable: false,
draggable: false,
modal: true,
close: function () {
if ($('.dialog_remove:checked', $(this)).length > 0) {
// setTemporaryPref
$.ajax({
type: 'POST',
url: '/user/preferences/temporary/',
data: {
prop: 'message',
value: 0
},
success: function (data) {
return;
}
});
}
}
})
.dialog('open');
}
}
return true;
}
// function manageSession(data, showMessages) {
// if (typeof (showMessages) === 'undefined')
// showMessages = false;
//
// if (data.status === 'disconnected' || data.status === 'session') {
// disconnected();
// return false;
// }
// if (showMessages) {
// let box = $('#notification_box');
// box.empty().append(data.notifications);
//
// if (box.is(':visible'))
// fix_notification_height();
//
// if ($('.notification.unread', box).length > 0) {
// let trigger = $('#notification_trigger');
// $('.counter', trigger)
// .empty()
// .append($('.notification.unread', box).length);
// $('.counter', trigger).css('visibility', 'visible');
//
// }
// else
// $('#notification_trigger .counter').css('visibility', 'hidden').empty();
//
// if (data.changed.length > 0) {
// let current_open = $('.SSTT.ui-state-active');
// let current_sstt = current_open.length > 0 ? current_open.attr('id').split('_').pop() : false;
//
// let main_open = false;
// for (let i = 0; i !== data.changed.length; i++) {
// let sstt = $('#SSTT_' + data.changed[i]);
// if (sstt.size() === 0) {
// if (main_open === false) {
// $('#baskets .bloc').animate({'top': 30}, function () {
// $('#baskets .alert_datas_changed:first').show();
// });
// main_open = true;
// }
// }
// else {
// if (!sstt.hasClass('active'))
// sstt.addClass('unread');
// else {
// $('.alert_datas_changed', $('#SSTT_content_' + data.changed[i])).show();
// }
// }
// }
// }
// if ($.trim(data.message) !== '') {
// if ($('#MESSAGE').length === 0)
// $('body').append('<div id="#MESSAGE"></div>');
// $('#MESSAGE')
// .empty()
// .append('<div style="margin:30px 10px;"><h4><b>' + data.message + '</b></h4></div><div style="margin:20px 0px 10px;"><label class="checkbox"><input type="checkbox" class="dialog_remove" />' + language.hideMessage + '</label></div>')
// .attr('title', 'Global Message')
// .dialog({
// autoOpen: false,
// closeOnEscape: true,
// resizable: false,
// draggable: false,
// modal: true,
// close: function () {
// if ($('.dialog_remove:checked', $(this)).length > 0) {
// // setTemporaryPref
// $.ajax({
// type: 'POST',
// url: '/user/preferences/temporary/',
// data: {
// prop: 'message',
// value: 0
// },
// success: function (data) {
// return;
// }
// });
// }
// }
// })
// .dialog('open');
// }
// }
// return true;
// }
export default {
initialize,
manageSession
initialize
};

View File

@@ -1,15 +1,11 @@
import $ from 'jquery';
const humane = require('humane-js');
require('imports-loader?define=>false&exports=>false!./../components/utils/jquery-plugins/colorAnimation');
import * as AppCommons from './../phraseanet-common';
import publication from '../components/publication';
import workzone from '../components/ui/workzone';
import notify from '../components/notify/index';
import Locale from '../components/locale';
import notifyLayout from '../components/notify/notifyLayout';
import LocaleService from '../components/locale';
import ui from '../components/ui';
import ConfigService from './../components/core/configService';
import LocaleService from '../components/locale';
import i18next from 'i18next';
import defaultConfig from './config';
import Emitter from '../components/core/emitter';
import user from '../components/user';
@@ -18,6 +14,10 @@ import search from '../components/search';
import utils from './../components/utils/utils';
import dialog from './../phraseanet-common/components/dialog';
import merge from 'lodash.merge';
const humane = require('humane-js');
require('imports-loader?define=>false&exports=>false!./../components/utils/jquery-plugins/colorAnimation');
class Bootstrap {
app;
@@ -69,27 +69,28 @@ class Bootstrap {
const userSession = user(this.appServices);
let appProdNotification = {
url: this.configService.get('notify.url'),
moduleId: this.configService.get('notify.moduleId'),
userId: this.configService.get('notify.userId')
};
// let appProdNotification = {
// url: this.configService.get('notify.url'),
// moduleId: this.configService.get('notify.moduleId'),
// userId: this.configService.get('notify.userId')
// };
notifyLayout(this.appServices).initialize();
/**
* Initialize notifier
* @type {{bindEvents, createNotifier, isValid, poll}}
* Poll just in menu_bar
*/
const notifier = notify(this.appServices);
notifier.initialize();
// create a new notification poll:
appProdNotification = notifier.createNotifier(appProdNotification);
if (notifier.isValid(appProdNotification)) {
notifier.poll(appProdNotification);
} else {
throw new Error('implementation error: failed to configure new notifier');
}
// const notifier = notify(this.appServices);
// notifier.initialize();
//
// // create a new notification poll:
// appProdNotification = notifier.createNotifier(appProdNotification);
//
// if (notifier.isValid(appProdNotification)) {
// notifier.poll(appProdNotification);
// } else {
// throw new Error('implementation error: failed to configure new notifier');
// }
// @TODO remove global variables
// register some global variables,

View File

@@ -11,10 +11,10 @@ namespace Alchemy\Phrasea\Controller\Root;
use Alchemy\Phrasea\Application\Helper\EntityManagerAware;
use Alchemy\Phrasea\Controller\Controller;
use Alchemy\Phrasea\Model\Entities\SessionModule;
use Alchemy\Phrasea\Model\Repositories\BasketRepository;
use Alchemy\Phrasea\Model\Repositories\SessionRepository;
use Alchemy\Phrasea\Utilities\Stopwatch;
use eventsmanager_broker;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
@@ -42,10 +42,14 @@ class SessionController extends Controller
'status' => 'unknown',
'message' => '',
'notifications' => false,
'changed' => []
'notifications_html' => false,
'unread_basket_ids' => []
];
$authenticator = $this->getAuthenticator();
// every request is catched by SessionManagerSubscriber, which handles disconnect
// so this code is probably useless
/*
if ($authenticator->isAuthenticated()) {
$usr_id = $authenticator->getUser()->getId();
if ($usr_id != $request->request->get('usr')) { // I logged with another user
@@ -58,29 +62,39 @@ class SessionController extends Controller
return $this->app->json($ret);
}
*/
try {
$this->getApplicationBox()->get_connection();
} catch (\Exception $e) {
}
catch (\Exception $e) {
return $this->app->json($ret);
}
// module id is only used to track apps, its done in SessioManagerSubscriber (parsing url)
/*
if (1 > $moduleId = (int) $request->request->get('module')) {
$ret['message'] = 'Missing or Invalid `module` parameter';
return $this->app->json($ret);
}
*/
$ret['status'] = 'ok';
$stopwatch->lap("start");
$notifs = $this->getEventsManager()->get_notifications($stopwatch);
$offset = (int)$request->get('offset', 0);
$limit = (int)$request->get('limit', 10);
$what = (int)$request->get('what', eventsmanager_broker::UNREAD | eventsmanager_broker::READ);
$notifications = $this->getEventsManager()->get_notifications($offset, $limit, $what, $stopwatch);
$stopwatch->lap("get_notifications done");
$ret['notifications'] = $this->render('prod/notifications.html.twig', [
'notifications' => $notifs
$ret['notifications'] = $notifications;
$ret['notifications_html'] = $this->render('prod/notifications.html.twig', [
'notifications' => $notifications['notifications']
]);
$stopwatch->lap("render done");
@@ -90,7 +104,7 @@ class SessionController extends Controller
$stopwatch->lap("baskets::findUnreadActiveByUser done");
foreach ($baskets as $basket) {
$ret['changed'][] = $basket->getId();
$ret['unread_basket_ids'][] = $basket->getId();
}
if (in_array($this->getSession()->get('phraseanet.message'), ['1', null])) {
@@ -121,96 +135,6 @@ class SessionController extends Controller
return $response;
}
/**
* @param Request $request
* @return JsonResponse
* @throws \Exception in case "new \DateTime()" fails ?
*/
public function updateSession(Request $request)
{
if (!$request->isXmlHttpRequest()) {
$this->app->abort(400);
}
$ret = [
'status' => 'unknown',
'message' => '',
'notifications' => false,
'changed' => []
];
$authenticator = $this->getAuthenticator();
if ($authenticator->isAuthenticated()) {
$usr_id = $authenticator->getUser()->getId();
if ($usr_id != $request->request->get('usr')) { // I logged with another user
$ret['status'] = 'disconnected';
return $this->app->json($ret);
}
}
else {
$ret['status'] = 'disconnected';
return $this->app->json($ret);
}
try {
$this->getApplicationBox()->get_connection();
}
catch (\Exception $e) {
return $this->app->json($ret);
}
if (1 > $moduleId = (int) $request->request->get('module')) {
$ret['message'] = 'Missing or Invalid `module` parameter';
return $this->app->json($ret);
}
/** @var \Alchemy\Phrasea\Model\Entities\Session $session */
$session = $this->getSessionRepository()->find($this->getSession()->get('session_id'));
$session->setUpdated(new \DateTime());
$manager = $this->getEntityManager();
if (!$session->hasModuleId($moduleId)) {
$module = new SessionModule();
$module->setModuleId($moduleId);
$module->setSession($session);
$manager->persist($module);
}
else {
$manager->persist($session->getModuleById($moduleId)->setUpdated($now));
}
$manager->persist($session);
$manager->flush();
$ret['status'] = 'ok';
$ret['notifications'] = $this->render('prod/notifications.html.twig', [
'notifications' => $this->getEventsManager()->get_notifications()
]);
$baskets = $this->getBasketRepository()->findUnreadActiveByUser($authenticator->getUser());
foreach ($baskets as $basket) {
$ret['changed'][] = $basket->getId();
}
if (in_array($this->getSession()->get('phraseanet.message'), ['1', null])) {
$conf = $this->getConf();
if ($conf->get(['main', 'maintenance'])) {
$ret['message'] .= $this->app->trans('The application is going down for maintenance, please logout.');
}
if ($conf->get(['registry', 'maintenance', 'enabled'])) {
$ret['message'] .= strip_tags($conf->get(['registry', 'maintenance', 'message']));
}
}
return $this->app->json($ret);
}
/**
* Deletes identified session
*

View File

@@ -21,6 +21,7 @@ class UserNotificationController extends Controller
* @param Request $request
* @return JsonResponse
*/
/* remove in favor of existing /session/ route
public function readNotifications(Request $request)
{
if (!$request->isXmlHttpRequest()) {
@@ -38,6 +39,7 @@ class UserNotificationController extends Controller
return $this->app->json(['success' => false, 'message' => $e->getMessage()]);
}
}
*/
/**
* Get all notifications
@@ -45,6 +47,7 @@ class UserNotificationController extends Controller
* @param Request $request
* @return JsonResponse
*/
/* remove in favor of existing /session/ route
public function listNotifications(Request $request)
{
if (!$request->isXmlHttpRequest()) {
@@ -55,12 +58,15 @@ class UserNotificationController extends Controller
return $this->app->json($this->getEventsManager()->get_notifications_as_array(($page < 0 ? 0 : $page)));
}
*/
/**
* @return \eventsmanager_broker
*/
/* remove in favor of existing /session/ route
private function getEventsManager()
{
return $this->app['events-manager'];
}
*/
}

View File

@@ -13,9 +13,8 @@ namespace Alchemy\Phrasea\ControllerProvider\Admin;
use Alchemy\Phrasea\Application as PhraseaApplication;
use Alchemy\Phrasea\Controller\Admin\DataboxController;
use Alchemy\Phrasea\Core\LazyLocator;
use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait;
use Alchemy\Phrasea\Security\Firewall;
use Alchemy\Phrasea\Core\LazyLocator;
use Silex\Application;
use Silex\ControllerProviderInterface;
use Silex\ServiceProviderInterface;
@@ -89,6 +88,7 @@ class Databox implements ControllerProviderInterface, ServiceProviderInterface
->before([$this, 'requireChangeSbasStructureRight'])
->bind('admin_database_submit_cgus');
// polled by admin/databox to display indexation progress bar
$controllers->get('/{databox_id}/informations/documents/', 'controller.admin.databox:progressBarInfos')
->before([$this, 'requireManageRightOnSbas'])
->bind('admin_database_display_document_information');

View File

@@ -12,9 +12,9 @@
namespace Alchemy\Phrasea\ControllerProvider\Root;
use Alchemy\Phrasea\Application as PhraseaApplication;
use Alchemy\Phrasea\Core\LazyLocator;
use Alchemy\Phrasea\Controller\Root\SessionController;
use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait;
use Alchemy\Phrasea\Core\LazyLocator;
use Silex\Application;
use Silex\ControllerProviderInterface;
use Silex\ServiceProviderInterface;
@@ -41,12 +41,12 @@ class Session implements ControllerProviderInterface, ServiceProviderInterface
{
$controllers = $this->createCollection($app);
$controllers->post('/update/', 'controller.session:updateSession')
->bind('update_session');
/** @uses SessionController::getNotifications() */
$controllers->post('/notifications/', 'controller.session:getNotifications')
->bind('list_notifications');
/** @uses SessionController::deleteSession() */
// used in admin/connected_users to kill a session
$controller = $controllers->post('/delete/{id}', 'controller.session:deleteSession')
->bind('delete_session');

View File

@@ -11,8 +11,6 @@
namespace Alchemy\Phrasea\ControllerProvider\User;
use Alchemy\Phrasea\Application as PhraseaApplication;
use Alchemy\Phrasea\Controller\User\UserNotificationController;
use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait;
use Silex\Application;
use Silex\ControllerProviderInterface;
@@ -24,9 +22,12 @@ class Notifications implements ControllerProviderInterface, ServiceProviderInter
public function register(Application $app)
{
/* remove in favor of existing /session/ route
*
$app['controller.user.notifications'] = $app->share(function (PhraseaApplication $app) {
return (new UserNotificationController($app));
});
*/
}
public function boot(Application $app)
@@ -46,11 +47,16 @@ class Notifications implements ControllerProviderInterface, ServiceProviderInter
$firewall->requireNotGuest();
});
/* remove in favor of existing /session/ route
*
/** @uses UserNotificationController::listNotifications * /
$controllers->get('/', 'controller.user.notifications:listNotifications')
->bind('get_notifications');
/** @uses UserNotificationController::readNotifications() * /
$controllers->post('/read/', 'controller.user.notifications:readNotifications')
->bind('set_notifications_readed');
*/
return $controllers;
}

View File

@@ -13,13 +13,12 @@ namespace Alchemy\Phrasea\Core\Event\Subscriber;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Model\Entities\Session;
use Alchemy\Phrasea\Model\Entities\SessionModule;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class SessionManagerSubscriber implements EventSubscriberInterface
{
@@ -43,56 +42,52 @@ class SessionManagerSubscriber implements EventSubscriberInterface
{
return [
KernelEvents::REQUEST => [
['initSession', Application::EARLY_EVENT],
/** @uses SessionManagerSubscriber::checkSessionActivity */
['checkSessionActivity', Application::LATE_EVENT]
]
];
}
public function initSession(GetResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
$request = $event->getRequest();
if ($this->isFlashUploadRequest($request) && null !== $sessionId = $request->request->get('php_session_id')) {
$request->cookies->set($this->app['session']->getName(), $sessionId);
}
}
/**
* log real human activity on application, to keep session alive
*
* to "auto-disconnect" when idle duration is passed, we use the "poll" requests.
* nb : the route "/sessions/notifications" is not considered as comming from a "module" (prod, admin, ...)
* so it will not update session
*
* @param GetResponseEvent $event
*/
public function checkSessionActivity(GetResponseEvent $event)
{
$request = $event->getRequest();
// ignore routes that comes from api (?) : todo : check if usefull since "api" is not a "module"
// todo : "LOG" ???
if ($request->request->has('oauth_token')
|| $request->query->has('oauth_token')
|| $request->query->has('LOG')
|| null === $moduleId = $this->getModuleId($request->getPathInfo())
) {
return;
}
if ($this->isAdminJsPolledRoute($moduleId, $request)) {
return;
}
if ($moduleId === self::$modulesIds['prod'] && $this->isFlashUploadRequest($request)) {
return;
}
$moduleId = $this->getModuleId($request->getPathInfo());
// if we are already disconnected (ex. from another window), quit immediately
//
if (!($this->app->getAuthenticator()->isAuthenticated())) {
$this->setDisconnectResponse($event);
return;
}
// we must still ignore some "polling" (js) routes
//
if ($this->isJsPollingRoute($moduleId, $request)) {
return;
}
// ANY route can disconnect the user if idle duration is over
//
/** @var Session $session */
$session = $this->app['repo.sessions']->find($this->app['session']->get('session_id'));
@@ -112,6 +107,14 @@ class SessionManagerSubscriber implements EventSubscriberInterface
return;
}
// only routes from "modules" (prod, admin, ...) are considered as "user activity"
//
if(is_null($moduleId)) {
return;
}
// here the route is considered as "user activity" : update session
//
$entityManager = $this->app['orm.em'];
$module = $this->addOrUpdateSessionModule($session, $moduleId, $now);
@@ -121,11 +124,6 @@ class SessionManagerSubscriber implements EventSubscriberInterface
$entityManager->flush();
}
private function isFlashUploadRequest(Request $request)
{
return false !== stripos($request->server->get('HTTP_USER_AGENT'), 'flash') && $request->getRequestUri() === '/prod/upload/';
}
/**
* @param GetResponseEvent $event
*/
@@ -133,9 +131,10 @@ class SessionManagerSubscriber implements EventSubscriberInterface
{
$request = $event->getRequest();
$response = $request->isXmlHttpRequest() ? $this->getXmlHttpResponse() : $this->getRedirectResponse($request);
$event->setResponse($response);
if($this->getModuleName($request->getPathInfo()) !== 'login') { // prevent infinite redirections
$response = $request->isXmlHttpRequest() ? $this->getXmlHttpResponse() : $this->getRedirectResponse($request);
$event->setResponse($response);
}
}
/**
@@ -163,7 +162,7 @@ class SessionManagerSubscriber implements EventSubscriberInterface
* @param string $pathInfo
* @return int|null
*/
private function getModuleId($pathInfo)
private function getModuleName($pathInfo)
{
$parts = array_filter(explode('/', $pathInfo));
@@ -171,7 +170,16 @@ class SessionManagerSubscriber implements EventSubscriberInterface
return null;
}
$moduleName = strtolower($parts[1]);
return strtolower($parts[1]);
}
/**
* @param string $pathInfo
* @return int|null
*/
private function getModuleId($pathInfo)
{
$moduleName = $this->getModuleName($pathInfo);
if (!isset(self::$modulesIds[$moduleName])) {
return null;
@@ -181,23 +189,39 @@ class SessionManagerSubscriber implements EventSubscriberInterface
}
/**
* returns true is the route match a "polling" route (databox progressionbar, task manager, notifications, ...)
* polling routes (sent every n seconds with no user action) must not update the session
*
* the request should contain a "update-session=0" header, but for now we still test hardcoded routes
*
* @param int $moduleId
* @param Request $request
* @return bool
*/
private function isAdminJsPolledRoute($moduleId, Request $request)
private function isJsPollingRoute($moduleId, Request $request)
{
if ($moduleId !== self::$modulesIds['admin']) {
return false;
if($request->headers->get('update-session', '1') === '0') {
return true;
}
$pathInfo = $request->getPathInfo();
// admin/task managers poll tasks
if ($pathInfo === '/admin/task-manager/tasks/' && $request->getContentType() === 'json') {
return true;
}
return preg_match('#^/admin/databox/\d+/informations/documents/#', $pathInfo) === 1;
// admin/databox poll to update the indexation progress bar (header "update-session=0") sent
if(preg_match('#^/admin/databox/\d+/informations/documents/#', $pathInfo)) {
return true;
}
// admin/databox poll to update the indexation progress bar
if(preg_match('#^/.*/notifications/#', $pathInfo)) {
return true;
}
return false;
}
/**

View File

@@ -96,15 +96,29 @@ class eventsmanager_broker
return true;
}
/*
public function get_notifications_as_array($page = 0)
{
$r = $this->get_notifications($page * 10, 10);
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!! FAKE USER FOR TESTING !!!!!!!!!!!!!!!!!!!!!!!!
// $usr_id = $this->app->getAuthenticatedUser()->getId();
$usr_id = 15826;
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
$total = 0;
$sql = 'SELECT count(id) as total, sum(unread) as unread
FROM notifications WHERE usr_id = :usr_id';
$stmt = $this->app->getApplicationBox()->get_connection()->prepare($sql);
$stmt->execute([':usr_id' => $this->app->getAuthenticatedUser()->getId()]);
$stmt->execute([':usr_id' => $usr_id]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
@@ -122,7 +136,7 @@ class eventsmanager_broker
$data = ['notifications' => [], 'next' => ''];
$stmt = $this->app->getApplicationBox()->get_connection()->prepare($sql);
$stmt->execute([':usr_id' => $this->app->getAuthenticatedUser()->getId()]);
$stmt->execute([':usr_id' => $usr_id]);
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
@@ -134,7 +148,9 @@ class eventsmanager_broker
continue;
}
$content = $this->pool_classes[$type]->datas($json, $row['unread']);
/** @var eventsmanager_notifyAbstract $obj * /
$obj = $this->pool_classes[$type];
$content = $obj->datas($json, $row['unread']);
if ( ! isset($this->pool_classes[$type]) || count($content) === 0) {
$sql = 'DELETE FROM notifications WHERE id = :id';
@@ -169,7 +185,8 @@ class eventsmanager_broker
return $data;
}
*/
/*
public function get_unread_notifications_number()
{
$total = 0;
@@ -188,92 +205,152 @@ class eventsmanager_broker
return $total;
}
*/
const READ = 1;
const UNREAD = 2;
public function get_notifications(\Alchemy\Phrasea\Utilities\Stopwatch $stopwatch = null)
public function get_notifications(int $offset=0, int $limit=10, $readFilter = self::READ | self::UNREAD, \Alchemy\Phrasea\Utilities\Stopwatch $stopwatch = null)
{
$unread = 0;
$sql = 'SELECT count(id) as total, sum(unread) as unread
FROM notifications WHERE usr_id = :usr_id';
if($stopwatch) $stopwatch->lap("broker start");
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!! FAKE USER FOR TESTING !!!!!!!!!!!!!!!!!!!!!!!!
// $usr_id = $this->app->getAuthenticatedUser()->getId();
$usr_id = $this->app->getAuthenticatedUser()->getId();
$usr_id = 29882;
// $usr_id = 29882;
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if($stopwatch) $stopwatch->lap("broker start");
// delete old already read notifs (nb: we do this for everybody - not only the current user -)
// todo: for now we use "created_on" since there is no timestamp set when reading.
//
$sql = "DELETE FROM `notifications` WHERE `unread`=0 AND TIMESTAMPDIFF(HOUR, `created_on`, NOW()) > 10";
$this->app->getApplicationBox()->get_connection()->exec($sql);
// get count of unread notifications (to be displayed on navbar)
//
$total = 0;
$unread = 0;
$sql = 'SELECT COUNT(`id`) AS `total`, SUM(`unread`) AS `unread` FROM `notifications` WHERE `usr_id` = :usr_id';
$stmt = $this->app->getApplicationBox()->get_connection()->prepare($sql);
$stmt->execute([':usr_id' => $usr_id]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if( ($row = $stmt->fetch(PDO::FETCH_ASSOC)) ) {
$total = (int)$row['total'];
$unread = (int)$row['unread'];
}
$stmt->closeCursor();
if($stopwatch) $stopwatch->lap("sql 1");
if($stopwatch) $stopwatch->lap("sql count unread");
if ($row) {
$unread = $row['unread'];
$notifications = [];
// fetch notifications
//
$sql = "SELECT * FROM `notifications` WHERE `usr_id` = :usr_id";
switch ($readFilter) {
case self::READ:
$sql .= " AND `unread`=0";
$total -= $unread; // fix total to match the filter
break;
case self::UNREAD:
$sql .= " AND `unread`=1";
$total = $unread; // fix total to match the filter
break;
default:
// any other case : fetch both ; no need to fix total
break;
}
$sql .= " ORDER BY created_on DESC LIMIT " . $offset . ", " . $limit;
if ($unread < 3) {
$sql = 'SELECT * FROM notifications
WHERE usr_id = :usr_id ORDER BY created_on DESC LIMIT 0,4';
} else {
$sql = 'SELECT * FROM notifications
WHERE usr_id = :usr_id AND unread="1" ORDER BY created_on DESC';
}
$ret = [];
$stmt = $this->app->getApplicationBox()->get_connection()->prepare($sql);
$stmt->execute([':usr_id' => $usr_id]);
// $rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
//$stmt->closeCursor();
$stmt->execute([':usr_id' => $usr_id]); // , ':offset' => $offset, ':limit' => $limit]);
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
if($stopwatch) $stopwatch->lap("sql 2");
// foreach ($rs as $row) {
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$bad_ids = [];
// nb : we asked for a "page" of notifs (limit), but since some notifications may be ignored (bad type, bad json, ...)
// the result array may contain less than expected.
foreach ($rs as $row) {
$type = 'eventsmanager_' . $row['type'];
if ( ! isset($this->pool_classes[$type])) {
$bad_ids[] = $row['id'];
continue;
}
$data = @json_decode($row['datas'], true);
if (json_last_error() !== JSON_ERROR_NONE) {
$bad_ids[] = $row['id'];
continue;
}
$datas = $this->pool_classes[$type]->datas($data, $row['unread']);
/** @var eventsmanager_notifyAbstract $obj */
$obj = $this->pool_classes[$type];
$datas = $obj->datas($data, $row['unread']);
// $datas = [
// 'text' => "blabla"
// 'class' => "" | "relaod_baskets"
// ]
// if ( ! isset($this->pool_classes[$type]) || count($datas) === 0) {
// $sql = 'DELETE FROM notifications WHERE id = :id';
// $stmt = $this->app->getApplicationBox()->get_connection()->prepare($sql);
// $stmt->execute([':id' => $row['id']]);
// $stmt->closeCursor();
// continue;
// }
if (count($datas) === 0) {
$bad_ids[] = $row['id'];
continue;
}
$ret[] = array_merge(
$datas
, [
'created_on' => $this->app['date-formatter']->getPrettyString(new DateTime($row['created_on']))
, 'icon' => $this->pool_classes[$type]->icon_url()
, 'id' => $row['id']
, 'unread' => $row['unread']
// add infos to the notification and add to list
//
$created_on = new DateTime($row['created_on']);
$notifications[] = array_merge(
$datas,
[
'created_on_day' => $created_on->format('Ymd'),
'created_on' => $this->app['date-formatter']->getPrettyString($created_on),
'time' => $this->app['date-formatter']->getTime($created_on),
//, 'icon' => '<img src="' . $this->pool_classes[$type]->icon_url() . '" style="vertical-align:middle;width:16px;margin:2px;" />'
'icon' => $this->pool_classes[$type]->icon_url(),
'id' => $row['id'],
'unread' => $row['unread']
]
);
}
$stmt->closeCursor();
if($stopwatch) $stopwatch->lap("broker ret");
if($stopwatch) $stopwatch->lap("fetch");
if(!empty($bad_ids)) {
// delete broken notifs
$sql = 'DELETE FROM `notifications` WHERE `id` IN (' . join(',', $bad_ids) . ')';
$stmt = $this->app->getApplicationBox()->get_connection()->exec($sql);
}
$next_offset = $offset+$limit;
$ret = [
'unread_count' => $unread,
'offset' => $offset,
'limit' => $limit,
// 'prev_offset' => $offset === 0 ? null : max(0, $offset-$limit),
'next_offset' => $next_offset < $total ? $next_offset : null,
'next_page_html' => $next_offset < $total ? '<a href="#" class="notification__print-action" data-offset="' . $next_offset . '">' . $this->app->trans('charger d\'avantages de notifications') . '</a>' : null,
'notifications' => $notifications
];
return $ret;
}
/**
* mark a notification as read
* todo : add a "read_on" datetime so we can delete read notifs after N days. For now we use "created_on"
*
* @param array $notifications
* @param $usr_id
* @return $this|false
*/
public function read(Array $notifications, $usr_id)
{
if (count($notifications) == 0) {

View File

@@ -1,3 +1,4 @@
// import notifyLayout from "../../../../../Phraseanet-production-client/src/components/notify/notifyLayout";
var p4 = p4 || {};
var datepickerLang = [];
@@ -32,7 +33,6 @@ var commonModule = (function ($, p4) {
$(this).removeClass('context-menu-item-hover');
});
$('body').on('click', '.infoDialog', function (event) {
infoDialog($(this));
});
@@ -42,11 +42,13 @@ var commonModule = (function ($, p4) {
function showOverlay(n, appendto, callback, zIndex) {
var div = "OVERLAY";
if (typeof(n) != "undefined")
if (typeof(n) != "undefined") {
div += n;
}
if ($('#' + div).length === 0) {
if (typeof(appendto) == 'undefined')
if (typeof(appendto) == 'undefined') {
appendto = 'body';
}
$(appendto).append('<div id="' + div + '" style="display:none;">&nbsp;</div>');
}
@@ -61,12 +63,13 @@ var commonModule = (function ($, p4) {
left: 0
};
if (parseInt(zIndex) > 0)
if (parseInt(zIndex) > 0) {
css['zIndex'] = parseInt(zIndex);
}
if (typeof(callback) != 'function')
callback = function () {
};
if (typeof(callback) != 'function') {
callback = function () {};
}
$('#' + div).css(css).addClass('overlay').fadeTo(500, 0.7).bind('click', function () {
(callback)();
});
@@ -85,8 +88,9 @@ var commonModule = (function ($, p4) {
});
}
var div = "OVERLAY";
if (typeof(n) != "undefined")
if (typeof(n) != "undefined") {
div += n;
}
$('#' + div).hide().remove();
}
@@ -110,92 +114,118 @@ var commonModule = (function ($, p4) {
}).dialog('open').css({'overflow-x': 'auto', 'overflow-y': 'hidden', 'padding': '0'});
}
// @deprecated
function manageSession(data, showMessages) {
if (typeof(showMessages) == "undefined")
showMessages = false;
function manageSession(data)
{
if (data.status == 'disconnected' || data.status == 'session') {
self.location.replace(self.location.href);
}
if (showMessages) {
var box = $('#notification_box');
box.empty().append(data.notifications);
if (box.is(':visible'))
fix_notification_height();
// add notification in bar
if ($('.notification.unread', box).length > 0) {
var trigger = $('.notification_trigger');
$('.counter', trigger)
.empty()
.append($('.notification.unread', box).length);
$('.counter', trigger).css('visibility', 'visible');
// fill the pseudo-dropdown with pre-formatted list of notifs (10 unread)
//
var box = $('#notification_box');
box.empty().append(data.notifications_html);
}
else
$('.notification_trigger .counter').css('visibility', 'hidden').empty();
if (box.is(':visible')) {
fix_notification_height(); // duplicated, better call notifyLayout.setBoxHeight();
}
if (data.changed.length > 0) {
var current_open = $('.SSTT.ui-state-active');
var current_sstt = current_open.length > 0 ? current_open.attr('id').split('_').pop() : false;
// fill the count of uread (red button)
//
var trigger = $('.notification_trigger');
if(data.notifications.unread_count > 0) {
$('.counter', trigger)
.empty()
.append(data.notifications.unread_count).show();
}
else {
$('.counter', trigger)
.hide()
.empty();
}
var main_open = false;
for (var i = 0; i != data.changed.length; i++) {
var sstt = $('#SSTT_' + data.changed[i]);
if (sstt.size() === 0) {
if (main_open === false) {
$('#baskets .bloc').animate({'top': 30}, function () {
$('#baskets .alert_datas_changed:first').show()
});
main_open = true;
}
// diplay notification about unread baskets
//
if (data.unread_basket_ids.length > 0) {
var current_open = $('.SSTT.ui-state-active');
var current_sstt = current_open.length > 0 ? current_open.attr('id').split('_').pop() : false;
var main_open = false;
for (var i = 0; i != data.unread_basket_ids.length; i++) {
var sstt = $('#SSTT_' + data.unread_basket_ids[i]);
if (sstt.size() === 0) {
if (main_open === false) {
$('#baskets .bloc').animate({'top': 30}, function () {
$('#baskets .alert_datas_changed:first').show()
});
main_open = true;
}
}
else {
if (!sstt.hasClass('active')) {
sstt.addClass('unread');
}
else {
if (!sstt.hasClass('active'))
sstt.addClass('unread');
else {
$('.alert_datas_changed', $('#SSTT_content_' + data.changed[i])).show();
}
$('.alert_datas_changed', $('#SSTT_content_' + data.unread_basket_ids[i])).show();
}
}
}
if ('' !== $.trim(data.message)) {
if ($('#MESSAGE').length === 0)
$('body').append('<div id="#MESSAGE"></div>');
$('#MESSAGE')
.empty()
.append('<div style="margin:30px 10px;"><h4><b>' + data.message + '</b></h4></div><div style="margin:20px 0px 10px;"><label class="checkbox"><input type="checkbox" class="dialog_remove" />' + language.hideMessage + '</label></div>')
.attr('title', 'Global Message')
.dialog({
autoOpen: false,
closeOnEscape: true,
resizable: false,
draggable: false,
modal: true,
close: function () {
if ($('.dialog_remove:checked', $(this)).length > 0) {
// setTemporaryPref
$.ajax({
type: "POST",
url: "/user/preferences/temporary/",
data: {
prop: 'message',
value: 0
},
success: function (data) {
return;
}
});
}
}
})
.dialog('open');
}
}
if ('' !== $.trim(data.message)) {
if ($('#MESSAGE').length === 0) {
$('body').append('<div id="#MESSAGE"></div>');
}
$('#MESSAGE')
.empty()
.append('<div style="margin:30px 10px;"><h4><b>' + data.message + '</b></h4></div><div style="margin:20px 0px 10px;"><label class="checkbox"><input type="checkbox" class="dialog_remove" />' + language.hideMessage + '</label></div>')
.attr('title', 'Global Message')
.dialog({
autoOpen: false,
closeOnEscape: true,
resizable: false,
draggable: false,
modal: true,
close: function () {
if ($('.dialog_remove:checked', $(this)).length > 0) {
// setTemporaryPref
$.ajax({
type: "POST",
url: "/user/preferences/temporary/",
data: {
prop: 'message',
value: 0
},
success: function (data) {
return;
}
});
}
}
})
.dialog('open');
}
return true;
}
/**
* duplicated from production_client::notifyLayout.js
*/
function fix_notification_height() {
const $notificationBoxContainer = $('#notification_box');
const not = $('.notification', $notificationBoxContainer);
const n = not.length;
const not_t = $('.notification_title', $notificationBoxContainer);
const n_t = not_t.length;
var h = not.outerHeight() * n + not_t.outerHeight() * n_t;
h = h > 350 ? 350 : h;
$notificationBoxContainer.stop().animate({height: h});
}
return {
showOverlay: showOverlay,
hideOverlay: hideOverlay,

View File

@@ -201,34 +201,35 @@ function reportDatePicker() {
});
}
// poll only from menu bar
function pollNotifications() {
$.ajax({
type: "POST",
url: "/session/notifications/",
dataType: 'json',
data: {
module: 10,
usr: usrId
},
error: function () {
window.setTimeout("pollNotifications();", 10000);
},
timeout: function () {
window.setTimeout("pollNotifications();", 10000);
},
success: function (data) {
if (data) {
commonModule.manageSession(data);
}
var t = 120000;
if (data.apps && parseInt(data.apps) > 1) {
t = Math.round((Math.sqrt(parseInt(data.apps) - 1) * 1.3 * 120000));
}
window.setTimeout("pollNotifications();", t);
return;
}
});
};
window.setTimeout("pollNotifications();", 10000);
// function pollNotifications() {
// $.ajax({
// type: "POST",
// url: "/session/notifications/",
// dataType: 'json',
// data: {
// module: 10,
// usr: usrId
// },
// error: function () {
// window.setTimeout("pollNotifications();", 10000);
// },
// timeout: function () {
// window.setTimeout("pollNotifications();", 10000);
// },
// success: function (data) {
// if (data) {
// commonModule.manageSession(data);
// }
// var t = 120000;
// if (data.apps && parseInt(data.apps) > 1) {
// t = Math.round((Math.sqrt(parseInt(data.apps) - 1) * 1.3 * 120000));
// }
// window.setTimeout("pollNotifications();", t);
// return;
// }
// });
// };
//
// window.setTimeout("pollNotifications();", 10000);

View File

@@ -242,7 +242,12 @@
type: "GET",
url: "/admin/databox/{{ databox.get_sbas_id() }}/informations/documents/",
dataType: 'json',
data: {},
headers: {
'update-session': '0' // polling should not maintain the session alive
// also : use lowercase as recomended / normalized
},
data: {
},
success: function (data) {
try {
if (data.viewname === '') {

View File

@@ -202,19 +202,13 @@
<div class="PNB right desktopmenu" style="left:auto;overflow:hidden;">
<ol>
{% if app.getAuthenticator().isAuthenticated() and module == "prod" %}
<li class="notification_trigger">
<a href="#" style="font-weight:bold;text-decoration:none;">
<span>
<button class="counter btn btn-danger"
style="visibility:{% if app['events-manager'].get_unread_notifications_number > 0 %}visible{% else %}hidden{% endif %};">
{% if app['events-manager'].get_unread_notifications_number > 0 %}
{{ app['events-manager'].get_unread_notifications_number }}
{% endif %}
</button>
{{ 'Notifications' | trans }}
</span>
</a>
</li>
<li class="notification_trigger">
<a href="#" style="font-weight:bold;text-decoration:none;">
<span>
<button class="counter btn btn-danger"></button>&nbsp;{{ 'Notifications' | trans }}
</span>
</a>
</li>
{% endif %}
<li class="user">
{% if app.getAuthenticator().isAuthenticated() %}
@@ -274,26 +268,19 @@
<div class="mobilemenu" style="float: right;margin-right: 50px;">
{% if app.getAuthenticator().isAuthenticated() and module == "prod" %}
<li class="notification_trigger">
<a href="#" style="font-weight:bold;text-decoration:none;">
<span>
<button class="counter btn btn-danger"
style="visibility:{% if app['events-manager'].get_unread_notifications_number > 0 %}visible{% else %}hidden{% endif %};">
{% if app['events-manager'].get_unread_notifications_number > 0 %}
{{ app['events-manager'].get_unread_notifications_number }}
{% endif %}
</button>
{{ 'Notifications' | trans }}
</span>
</a>
</li>
<li class="notification_trigger">
<a href="#" style="font-weight:bold;text-decoration:none;">
<span>
<button class="counter btn btn-danger"></button>&nbsp;{{ 'Notifications' | trans }}
</span>
</a>
</li>
{% endif %}
</div>
</div>
{% if app.getAuthenticator().isAuthenticated() and module == "prod" %}
<div style="display:none;z-index:30000;" id="notification_box">
{% set notifications = app['events-manager'].get_notifications %}
{% include 'prod/notifications.html.twig' %}
</div>
{% endif %}
@@ -430,31 +417,36 @@
});
});
/**manage session and redirect to login page**/
var usrId = '{{ app.getAuthenticator().user.getId }}' ;
// var usrId = '{{ app.getAuthenticator().user.getId }}' ;
var module = '{{ module }}';
function pollNotifications() {
$.ajax({
type: "POST",
url: "/session/notifications/",
dataType: 'json',
data: {
module: 10,
usr: usrId
'offset': 0,
'limit': 10,
'what': 2, // 2 : only unread
},
error: function () {
window.setTimeout("pollNotifications();", 10000);
error: function (data) {
if(data.getResponseHeader('x-phraseanet-end-session')) {
self.location.replace(self.location.href); // refresh will redirect to login
}
},
timeout: function () {
window.setTimeout("pollNotifications();", 10000);
},
success: function (data) {
if (data) {
// there is no notification bar nor a basket notification if not on prod module
if (module === 'prod' && data) {
commonModule.manageSession(data);
}
window.setTimeout("pollNotifications();", 60000);
return;
}
});
};
}
window.setTimeout("pollNotifications();", 10000);
window.setTimeout("pollNotifications();", 2000);
</script>

View File

@@ -99,7 +99,7 @@
{% if basket.getValidation is null %}
{% set basket_length = basket.getElements().count() %}
{% set counter = ( counter | default(0) ) + 1 %}
{% set counter_length = baskets_collection.length() %}
{% set counter_length = baskets_collection | length %}
<tr class="{% if counter >=4 %}other_basket hidden{% endif %}">
<td colspan="2">

View File

@@ -101,11 +101,11 @@
lang: "{{ app.locale }}",
baseUrl: '{{ app['request'].getUriForPath('/') }}',
basePath: '{{ app.request.basePath|e('js') }}',
notify: {
url: "{{ path('list_notifications') }}",
moduleId: 1,
userId: {{app.getAuthenticatedUser().getId()}}
},
{#notify: {#}
{# url: "{{ path('list_notifications') }}",#}
{# moduleId: 1,#}
{# userId: {{app.getAuthenticatedUser().getId()}}#}
{#},#}
debug: {% if app.debug %}true{% else %}false{% endif %},
initialState: "{{ initialAppState }}",
geonameServerUrl: '{{ app['geonames.server-uri'] }}',

View File

@@ -6,7 +6,7 @@
</div>
{% else %}
<div class="notification_title">
<a href="#" class="notification__print-action" data-page="0">{{ 'toutes les notifications' | trans }}</a>
<a href="#" class="notification__print-action">{{ 'toutes les notifications' | trans }}</a>
</div>
{% endif %}

View File

@@ -150,7 +150,7 @@
alert("{{ 'phraseanet::erreur: Votre session est fermee, veuillez vous re-authentifier' | trans }}");
self.location.replace(self.location.href);
}
window.setTimeout("pollNotifications();", 10000);
window.setTimeout("pollNotifications();", 60000);
return;
}

View File

@@ -21,35 +21,37 @@ define([
eventManager: _.extend({}, Backbone.Events)
};
window.pullNotifications = function (){
$.ajax({
type: "POST",
url: AdminApp.$scope.data("notif-url"),
dataType: 'json',
data: {
module : 3,
usr : AdminApp.$scope.data("usr")
},
error: function(){
window.setTimeout("pullNotifications();", 10000);
},
timeout: function(){
window.setTimeout("pullNotifications();", 10000);
},
success: function(data){
if (data) {
if (data.status == 'disconnected' || data.status == 'session') {
self.location.replace(self.location.href);
}
}
var t = 120000;
if (data.apps && parseInt(data.apps) > 1) {
t = Math.round((Math.sqrt(parseInt(data.apps)-1) * 1.3 * 120000));
}
window.setTimeout("pullNotifications();", t);
}
});
};
// pull notification is called from menu bar
// window.pullNotifications = function (){
// $.ajax({
// type: "POST",
// url: AdminApp.$scope.data("notif-url"),
// dataType: 'json',
// data: {
// module : 3,
// usr : AdminApp.$scope.data("usr")
// },
// error: function(){
// window.setTimeout("pullNotifications();", 10000);
// },
// timeout: function(){
// window.setTimeout("pullNotifications();", 10000);
// },
// success: function(data){
// if (data) {
// if (data.status == 'disconnected' || data.status == 'session') {
// self.location.replace(self.location.href);
// }
// }
// var t = 120000;
// if (data.apps && parseInt(data.apps) > 1) {
// t = Math.round((Math.sqrt(parseInt(data.apps)-1) * 1.3 * 120000));
// }
// window.setTimeout("pullNotifications();", t);
// }
// });
// };
window.enableForms = function (forms) {
forms.bind('submit', function(event){
var method = $(this).attr('method');

View File

@@ -15,7 +15,7 @@ define([
var TaskCollection = Backbone.Collection.extend({
model: TaskModel,
url: function () {
return "/admin/task-manager/tasks";
return "/admin/task-manager/tasks?update_session=0";
}
});