diff --git a/Phraseanet-production-client/src/components/basket/index.js b/Phraseanet-production-client/src/components/basket/index.js
index 8518e68228..cab59617be 100644
--- a/Phraseanet-production-client/src/components/basket/index.js
+++ b/Phraseanet-production-client/src/components/basket/index.js
@@ -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) => {
diff --git a/Phraseanet-production-client/src/components/notify/index.js b/Phraseanet-production-client/src/components/notify/index.js
index 3214b8ad5f..669e96489c 100644
--- a/Phraseanet-production-client/src/components/notify/index.js
+++ b/Phraseanet-production-client/src/components/notify/index.js
@@ -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;
};
diff --git a/Phraseanet-production-client/src/components/notify/notifyLayout.js b/Phraseanet-production-client/src/components/notify/notifyLayout.js
index 3014258a97..ae68c04a1a 100644
--- a/Phraseanet-production-client/src/components/notify/notifyLayout.js
+++ b/Phraseanet-production-client/src/components/notify/notifyLayout.js
@@ -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('
');
+ $('body').append('');
$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('' + data.notifications[i].display + '
');
- 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 = '' +
- '
' +
- loc_dat.icon +
- ' ' +
- '' +
- loc_dat.text + ' ' + loc_dat.time + '
' +
- '
' +
- '
';
- 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('' + notifications[i].created_on + '
');
+ date_cont = $('#' + id, $notificationsContent);
}
+ // write notif
+ let html = '' +
+ '
' +
+ ' ' +
+ ' ' +
+ '' +
+ notification.text + ' ' + notification.time + '
' +
+ '
' +
+ '
';
+ date_cont.append(html);
}
- var next_ln = $.trim(data.next);
-
- if (next_ln !== '') {
- $notificationDialog.append('' + next_ln + '
');
+ 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
};
};
diff --git a/Phraseanet-production-client/src/components/record/index.js b/Phraseanet-production-client/src/components/record/index.js
index 9fc66e02fc..8d1834c6ed 100644
--- a/Phraseanet-production-client/src/components/record/index.js
+++ b/Phraseanet-production-client/src/components/record/index.js
@@ -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');
diff --git a/Phraseanet-production-client/src/components/user/index.js b/Phraseanet-production-client/src/components/user/index.js
index 44424e76f3..1682573293 100644
--- a/Phraseanet-production-client/src/components/user/index.js
+++ b/Phraseanet-production-client/src/components/user/index.js
@@ -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('
');
+ // }
+ // $('#MESSAGE')
+ // .empty()
+ // .append(data.message + ' ' + localeService.t('hideMessage') + '
')
+ // .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('
');
- }
- $('#MESSAGE')
- .empty()
- .append(data.message + ' ' + localeService.t('hideMessage') + '
')
- .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;
diff --git a/Phraseanet-production-client/src/phraseanet-common/components/common.js b/Phraseanet-production-client/src/phraseanet-common/components/common.js
index 786d3179ed..3707ad28c6 100644
--- a/Phraseanet-production-client/src/phraseanet-common/components/common.js
+++ b/Phraseanet-production-client/src/phraseanet-common/components/common.js
@@ -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('
');
- $('#MESSAGE')
- .empty()
- .append('
' + data.message + ' ' + language.hideMessage + '
')
- .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('
');
+// $('#MESSAGE')
+// .empty()
+// .append('
' + data.message + ' ' + language.hideMessage + '
')
+// .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
};
diff --git a/Phraseanet-production-client/src/prod/bootstrap.js b/Phraseanet-production-client/src/prod/bootstrap.js
index f29f6c987f..91913a4a4a 100644
--- a/Phraseanet-production-client/src/prod/bootstrap.js
+++ b/Phraseanet-production-client/src/prod/bootstrap.js
@@ -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,
diff --git a/lib/Alchemy/Phrasea/Controller/Root/SessionController.php b/lib/Alchemy/Phrasea/Controller/Root/SessionController.php
index 7c91b16498..38a9e33535 100644
--- a/lib/Alchemy/Phrasea/Controller/Root/SessionController.php
+++ b/lib/Alchemy/Phrasea/Controller/Root/SessionController.php
@@ -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
*
diff --git a/lib/Alchemy/Phrasea/Controller/User/UserNotificationController.php b/lib/Alchemy/Phrasea/Controller/User/UserNotificationController.php
index 5bce930b69..f7e9581208 100644
--- a/lib/Alchemy/Phrasea/Controller/User/UserNotificationController.php
+++ b/lib/Alchemy/Phrasea/Controller/User/UserNotificationController.php
@@ -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'];
}
+ */
}
diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Admin/Databox.php b/lib/Alchemy/Phrasea/ControllerProvider/Admin/Databox.php
index ff0ad4ecc0..7f809c1e32 100644
--- a/lib/Alchemy/Phrasea/ControllerProvider/Admin/Databox.php
+++ b/lib/Alchemy/Phrasea/ControllerProvider/Admin/Databox.php
@@ -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');
diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Root/Session.php b/lib/Alchemy/Phrasea/ControllerProvider/Root/Session.php
index b549a2bc03..63b7b18c1e 100644
--- a/lib/Alchemy/Phrasea/ControllerProvider/Root/Session.php
+++ b/lib/Alchemy/Phrasea/ControllerProvider/Root/Session.php
@@ -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');
diff --git a/lib/Alchemy/Phrasea/ControllerProvider/User/Notifications.php b/lib/Alchemy/Phrasea/ControllerProvider/User/Notifications.php
index ded279f367..f12f0f3bd6 100644
--- a/lib/Alchemy/Phrasea/ControllerProvider/User/Notifications.php
+++ b/lib/Alchemy/Phrasea/ControllerProvider/User/Notifications.php
@@ -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;
}
diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/SessionManagerSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/SessionManagerSubscriber.php
index 689ae02837..89990330e5 100644
--- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/SessionManagerSubscriber.php
+++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/SessionManagerSubscriber.php
@@ -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;
}
/**
diff --git a/lib/classes/eventsmanager/broker.php b/lib/classes/eventsmanager/broker.php
index 86c63d78f3..d8b7c19819 100644
--- a/lib/classes/eventsmanager/broker.php
+++ b/lib/classes/eventsmanager/broker.php
@@ -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' => ' '
+ '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 ? '' . $this->app->trans('charger d\'avantages de notifications') . ' ' : 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) {
diff --git a/resources/www/common/js/components/common.js b/resources/www/common/js/components/common.js
index 16ba0264d9..479f5fb4a7 100644
--- a/resources/www/common/js/components/common.js
+++ b/resources/www/common/js/components/common.js
@@ -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('
');
}
@@ -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('
');
- $('#MESSAGE')
- .empty()
- .append('
' + data.message + ' ' + language.hideMessage + '
')
- .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('
');
+ }
+ $('#MESSAGE')
+ .empty()
+ .append('
' + data.message + ' ' + language.hideMessage + '
')
+ .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,
diff --git a/resources/www/report/js/report.js b/resources/www/report/js/report.js
index 64b7bc3ea6..af439032f5 100644
--- a/resources/www/report/js/report.js
+++ b/resources/www/report/js/report.js
@@ -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);
diff --git a/templates/web/admin/databox/databox.html.twig b/templates/web/admin/databox/databox.html.twig
index d5e6a62d25..e4d4777090 100644
--- a/templates/web/admin/databox/databox.html.twig
+++ b/templates/web/admin/databox/databox.html.twig
@@ -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 === '') {
diff --git a/templates/web/common/menubar.html.twig b/templates/web/common/menubar.html.twig
index 3ee72dd2cf..3b47bc6265 100644
--- a/templates/web/common/menubar.html.twig
+++ b/templates/web/common/menubar.html.twig
@@ -202,19 +202,13 @@
{% if app.getAuthenticator().isAuthenticated() and module == "prod" %}
- {% set notifications = app['events-manager'].get_notifications %}
{% include 'prod/notifications.html.twig' %}
{% 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);
diff --git a/templates/web/lightbox/index.html.twig b/templates/web/lightbox/index.html.twig
index fd2629e123..3ac4aa8e2b 100644
--- a/templates/web/lightbox/index.html.twig
+++ b/templates/web/lightbox/index.html.twig
@@ -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 %}
diff --git a/templates/web/prod/index.html.twig b/templates/web/prod/index.html.twig
index ad0d519547..15328f978f 100644
--- a/templates/web/prod/index.html.twig
+++ b/templates/web/prod/index.html.twig
@@ -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'] }}',
diff --git a/templates/web/prod/notifications.html.twig b/templates/web/prod/notifications.html.twig
index 313c6230df..c390cda1e5 100644
--- a/templates/web/prod/notifications.html.twig
+++ b/templates/web/prod/notifications.html.twig
@@ -6,7 +6,7 @@
{% else %}
{% endif %}
diff --git a/templates/web/thesaurus/thesaurus.html.twig b/templates/web/thesaurus/thesaurus.html.twig
index 1ce8830e10..8013e063d2 100644
--- a/templates/web/thesaurus/thesaurus.html.twig
+++ b/templates/web/thesaurus/thesaurus.html.twig
@@ -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;
}
diff --git a/www/scripts/apps/admin/main/app.js b/www/scripts/apps/admin/main/app.js
index 50f2c690e6..54fe2ea6c3 100644
--- a/www/scripts/apps/admin/main/app.js
+++ b/www/scripts/apps/admin/main/app.js
@@ -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');
diff --git a/www/scripts/apps/admin/tasks-manager/collections/tasks.js b/www/scripts/apps/admin/tasks-manager/collections/tasks.js
index 9cac331e81..63253dc376 100644
--- a/www/scripts/apps/admin/tasks-manager/collections/tasks.js
+++ b/www/scripts/apps/admin/tasks-manager/collections/tasks.js
@@ -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";
}
});