mirror of
https://github.com/humhub/humhub.git
synced 2025-01-17 14:18:27 +01:00
Live Polling + Notification Frontend Rewrite.
This commit is contained in:
parent
56905f80cf
commit
4da79ebe4f
@ -401,8 +401,17 @@ humhub.module('action', function(module, require, $) {
|
||||
};
|
||||
|
||||
/**
|
||||
* data-action-click-url vs data-action-url-click
|
||||
* data-action-click-block vs data-action-block-click
|
||||
* Returns the value of data-action-click-<name> over data-action-<name>
|
||||
* e.g.:
|
||||
*
|
||||
* If the $trigger sets a data-action-click-url and data-action-url and we call
|
||||
*
|
||||
* $actioNBinding.data($trigger, 'url');
|
||||
*
|
||||
* We'll receive the data-action-click-url.
|
||||
*
|
||||
* If no data-action-click-url is set it will return the fallback data-action-url setting.
|
||||
*
|
||||
* @param {type} $trigger
|
||||
* @param {type} name
|
||||
* @param {type} def
|
||||
@ -546,9 +555,11 @@ humhub.module('action', function(module, require, $) {
|
||||
|
||||
$parent.off(actionEvent).on(actionEvent, selector, function(evt) {
|
||||
evt.preventDefault();
|
||||
// Get sure we don't call the handler twice if the event was already handled by the handler attached to trigger.
|
||||
|
||||
// Get sure we don't call the handler twice if the event was already handled by the directly attached handler.
|
||||
// We have to rebind the handler only if we detect an unbound handler!
|
||||
if($(this).data('action-' + actionBinding.event) || (evt.originalEvent && evt.originalEvent.actionHandled)) {
|
||||
module.log.debug('Action Handler already executed ' + actionEvent, actionBinding);
|
||||
module.log.debug('Action Handler already executed by direct handler' + actionEvent, actionBinding);
|
||||
module.log.debug('Handler event triggered by', $(this));
|
||||
return;
|
||||
}
|
||||
|
@ -18,7 +18,10 @@ humhub.module('client', function (module, require, $) {
|
||||
this.textStatus = textStatus;
|
||||
this.dataType = dataType;
|
||||
|
||||
if (!dataType || dataType === 'json') {
|
||||
var responseType = xhr.getResponseHeader('content-type');
|
||||
|
||||
// If we expect json and received json we merge the json result with our response object.
|
||||
if (!dataType || dataType === 'json' && responseType.indexOf('json') > -1) {
|
||||
$.extend(this, this.response);
|
||||
} else if (dataType) {
|
||||
this[dataType] = this.response;
|
||||
|
@ -3,7 +3,7 @@ humhub.module('client.pjax', function (module, require, $) {
|
||||
|
||||
var init = function () {
|
||||
if (module.config.active) {
|
||||
$(document).pjax('a:not([data-pjax-prevent],[target],[data-target],[data-toggle])', "#layout-content", module.config.options);
|
||||
//$(document).pjax('a:not([data-pjax-prevent],[target],[data-target],[data-toggle])', "#layout-content", module.config.options);
|
||||
pjaxRedirectFix();
|
||||
module.installLoader();
|
||||
}
|
||||
|
@ -1,158 +0,0 @@
|
||||
humhub.module('live', function(module, require, $) {
|
||||
var object = require('util').object;
|
||||
var space = require('space', true);
|
||||
var user = require('user', true);
|
||||
|
||||
var instances = [];
|
||||
|
||||
var _delay = 11;
|
||||
|
||||
var _idleFactor = 0;
|
||||
|
||||
var _defaultOptions = {
|
||||
id: 'unknown',
|
||||
min: 20,
|
||||
max: 45,
|
||||
idle: true,
|
||||
idleFactor: 1,
|
||||
active: true
|
||||
};
|
||||
|
||||
var LiveUpdate = function(topic, options) {
|
||||
this.topic = (arguments.length === 1) ? topic.topic : topic;
|
||||
this.options = (arguments.length === 1) ? topic.options : options;
|
||||
this.options = $.extend(_defaultOptions, this.options);
|
||||
|
||||
this.id = this.options.id;
|
||||
};
|
||||
|
||||
LiveUpdate.prototype.on = function() {
|
||||
this.options.active = true;
|
||||
};
|
||||
|
||||
LiveUpdate.prototype.off = function() {
|
||||
this.options.active = false;
|
||||
};
|
||||
|
||||
LiveUpdate.prototype.isExpired = function() {
|
||||
if(!this.options.active) {
|
||||
return false;
|
||||
} else if(!this.lastUpdate) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var minDelay = this.options.min + (this.options.min * (_idleFactor * this.options.idleFactor));
|
||||
|
||||
if(minDelay > this.options.max) {
|
||||
minDelay = this.options.max;
|
||||
}
|
||||
|
||||
return (this.lastUpdate + minDelay) >= Date.now();
|
||||
};
|
||||
|
||||
LiveUpdate.prototype.handleResult = function(result) {
|
||||
|
||||
}
|
||||
|
||||
LiveUpdate.prototype.validate = function() {
|
||||
return !((this.topic.user && user.isGuest()) || (this.topic.space && !space.guid()));
|
||||
};
|
||||
|
||||
var register = function(topic, options) {
|
||||
var instance = new LiveUpdate(topic, options);
|
||||
instances.push(instance);
|
||||
return instance;
|
||||
};
|
||||
|
||||
var init = function() {
|
||||
setTimeout(_run, _delay);
|
||||
};
|
||||
|
||||
var _run = function() {
|
||||
var topics = [];
|
||||
var lastUpdate = Date.now();
|
||||
instances.forEach(function(update) {
|
||||
if(update.isExpired() && update.validate()) {
|
||||
var topic = _getServerTopic(update);
|
||||
module.log.debug('Topic update:' + update.id, topic);
|
||||
topics.push(topic);
|
||||
update.lastUpdate = lastUpdate;
|
||||
}
|
||||
});
|
||||
|
||||
_send().then(function(result) {
|
||||
instances.forEach(function(liveUpdate) {
|
||||
liveUpdate.handleResult(result);
|
||||
});
|
||||
}).catch(function(err) {
|
||||
// Silent error log
|
||||
module.log.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
var _getServerTopic = function(update) {
|
||||
var result = {};
|
||||
|
||||
if(update.topic.user === true) {
|
||||
result.uguid = user.guid();
|
||||
} else if(update.topic.uguid) {
|
||||
result.uguid = update.topic.uguid;
|
||||
}
|
||||
|
||||
if(update.topic.space) {
|
||||
result.sguid = space.guid();
|
||||
} else if(update.topic.sguid) {
|
||||
result.sguid = update.topic.sguid;
|
||||
}
|
||||
|
||||
if(update.topic.suffix) {
|
||||
result.suffix = update.topic.suffix;
|
||||
}
|
||||
|
||||
if(update.topic.module) {
|
||||
result.module = update.topic.module;
|
||||
}
|
||||
|
||||
if(update.lastResult) {
|
||||
result.last = update.lastResult;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
var update = new LiveUpdate({
|
||||
topic: {
|
||||
'user': true,
|
||||
'space': true,
|
||||
'module': 'mail',
|
||||
'suffix': 'whatever'
|
||||
},
|
||||
options: {
|
||||
min: 10,
|
||||
max: 30,
|
||||
idle: true,
|
||||
idleFactor: 1
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
LiveUpdate
|
||||
Settings
|
||||
LastUpdate -> Date this specific update was requested the last time.
|
||||
(Register To) --> function or static object
|
||||
- User
|
||||
- Space
|
||||
- Module
|
||||
(Live Settings)
|
||||
- Min Duration
|
||||
- Idle Factor
|
||||
(Callback)
|
||||
- Callback
|
||||
|
||||
-On/Off
|
||||
*/
|
||||
module.export({
|
||||
init: init,
|
||||
register: register
|
||||
});
|
||||
});
|
@ -12,17 +12,20 @@ humhub.module('ui.status', function (module, require, $) {
|
||||
var string = util.string;
|
||||
var client = require('client');
|
||||
|
||||
module.initOnPjaxLoad = true;
|
||||
|
||||
module.template = {
|
||||
info : '<i class="fa fa-info-circle info"></i><span>{msg}</span>',
|
||||
success : '<i class="fa fa-check-circle success"></i><span>{msg}</span>',
|
||||
warn : '<i class="fa fa-exclamation-triangle warning"></i><span>{msg}</span>',
|
||||
error : '<i class="fa fa-exclamation-circle error"></i><span>{msg}</span>',
|
||||
info: '<i class="fa fa-info-circle info"></i><span>{msg}</span>',
|
||||
success: '<i class="fa fa-check-circle success"></i><span>{msg}</span>',
|
||||
warn: '<i class="fa fa-exclamation-triangle warning"></i><span>{msg}</span>',
|
||||
error: '<i class="fa fa-exclamation-circle error"></i><span>{msg}</span>',
|
||||
closeButton: '<a class="status-bar-close pull-right" style="">×</a>',
|
||||
showMoreButton: '<a class="showMore"><i class="fa fa-angle-up"></i></a>',
|
||||
errorBlock: '<div class="status-bar-details" style="display:none;"><pre>{msg}</pre><div>'
|
||||
};
|
||||
|
||||
var state = {};
|
||||
var title;
|
||||
|
||||
var SELECTOR_ROOT = '#status-bar';
|
||||
var SELECTOR_BODY = '.status-bar-body';
|
||||
@ -37,21 +40,21 @@ humhub.module('ui.status', function (module, require, $) {
|
||||
|
||||
StatusBar.prototype.info = function (msg, closeAfter) {
|
||||
closeAfter = closeAfter || AUTOCLOSE_INFO;
|
||||
this._trigger(string.template(module.template.info, {msg : msg}), undefined, closeAfter);
|
||||
this._trigger(string.template(module.template.info, {msg: msg}), undefined, closeAfter);
|
||||
};
|
||||
|
||||
StatusBar.prototype.success = function (msg, closeAfter) {
|
||||
closeAfter = closeAfter || AUTOCLOSE_INFO;
|
||||
this._trigger(string.template(module.template.success, {msg : msg}), undefined, closeAfter);
|
||||
this._trigger(string.template(module.template.success, {msg: msg}), undefined, closeAfter);
|
||||
};
|
||||
|
||||
StatusBar.prototype.warn = function (msg, error, closeAfter) {
|
||||
closeAfter = closeAfter || AUTOCLOSE_WARN;
|
||||
this._trigger(string.template(module.template.warn, {msg : msg}), error, closeAfter);
|
||||
this._trigger(string.template(module.template.warn, {msg: msg}), error, closeAfter);
|
||||
};
|
||||
|
||||
StatusBar.prototype.error = function (msg, error, closeAfter) {
|
||||
this._trigger(string.template(module.template.error, {msg : msg}), error, closeAfter);
|
||||
this._trigger(string.template(module.template.error, {msg: msg}), error, closeAfter);
|
||||
};
|
||||
|
||||
StatusBar.prototype._trigger = function (content, error, closeAfter) {
|
||||
@ -105,7 +108,7 @@ humhub.module('ui.status', function (module, require, $) {
|
||||
|
||||
$showMore.find('i').attr('class', 'fa fa-angle-up');
|
||||
} else {
|
||||
$details = $(string.template(module.template.errorBlock, {msg : getErrorMessage(error)}));
|
||||
$details = $(string.template(module.template.errorBlock, {msg: getErrorMessage(error)}));
|
||||
$content.append($details);
|
||||
$details.slideToggle('fast');
|
||||
$showMore.find('i').attr('class', 'fa fa-angle-down');
|
||||
@ -181,10 +184,12 @@ humhub.module('ui.status', function (module, require, $) {
|
||||
});
|
||||
};
|
||||
|
||||
var init = function () {
|
||||
var init = function ($pjax) {
|
||||
title = document.title;
|
||||
if (!$pjax) {
|
||||
module.statusBar = new StatusBar();
|
||||
|
||||
event.on('humhub:ready', function() {
|
||||
event.on('humhub:ready', function () {
|
||||
module.log.debug('Current ui state', state);
|
||||
}).on('humhub:modules:log:setStatus', function (evt, msg, details, level) {
|
||||
switch (level) {
|
||||
@ -209,19 +214,21 @@ humhub.module('ui.status', function (module, require, $) {
|
||||
module.statusBar[module.initState[0]].apply(module.statusBar, module.initState[1]);
|
||||
module.initState = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.export({
|
||||
init: init,
|
||||
setState: function(moduleId, controlerId, action) {
|
||||
setState: function (moduleId, controlerId, action) {
|
||||
// This function is called by controller itself
|
||||
state = {
|
||||
title: title || document.title,
|
||||
moduleId: moduleId,
|
||||
controllerId: controlerId,
|
||||
action: action
|
||||
};
|
||||
},
|
||||
getState: function() {
|
||||
getState: function () {
|
||||
return $.extend({}, state);
|
||||
},
|
||||
StatusBar: StatusBar,
|
||||
|
@ -64,7 +64,7 @@
|
||||
@background4: #b2b2b2;
|
||||
|
||||
//
|
||||
// Alerts
|
||||
// Bootstrap Alert boxes
|
||||
// --------------------------------------------------
|
||||
|
||||
// Alert box success (used in forms)
|
||||
@ -72,12 +72,12 @@
|
||||
@text-color-success: #84be5e;
|
||||
@border-color-success: #97d271;
|
||||
|
||||
// Alert box success (used in forms)
|
||||
// Alert box warning (used in forms)
|
||||
@background-color-warning: #fffbf7;
|
||||
@text-color-warning: #e9b168;
|
||||
@border-color-warning: #fdd198;
|
||||
|
||||
// Alert box success (used in forms)
|
||||
// Alert box danger (used in forms)
|
||||
@background-color-danger: #fff6f6;
|
||||
@text-color-danger: #ff8989;
|
||||
@border-color-danger: #ff8989;
|
||||
|
@ -84,6 +84,8 @@ class AppAsset extends AssetBundle
|
||||
'humhub\assets\AtJsAsset',
|
||||
'humhub\assets\AnimateCssAsset',
|
||||
'humhub\assets\CoreApiAsset',
|
||||
'humhub\modules\live\assets\LiveAsset',
|
||||
'humhub\modules\notification\assets\NotificationAsset',
|
||||
'humhub\modules\content\assets\ContentAsset',
|
||||
'humhub\modules\user\assets\UserAsset',
|
||||
'humhub\modules\user\assets\UserPickerAsset',
|
||||
|
@ -45,7 +45,6 @@ class CoreApiAsset extends AssetBundle
|
||||
'js/humhub/humhub.core.js',
|
||||
'js/humhub/humhub.util.js',
|
||||
'js/humhub/humhub.log.js',
|
||||
//'js/humhub/humhub.scripts.js',
|
||||
'js/humhub/humhub.ui.additions.js',
|
||||
'js/humhub/humhub.ui.form.elements.js',
|
||||
'js/humhub/humhub.ui.loader.js',
|
||||
|
@ -169,7 +169,7 @@ class Theme extends \yii\base\Theme
|
||||
{
|
||||
$this->loadVariables();
|
||||
|
||||
// @deprecated since 1.2 color settings
|
||||
// @deprecated since version 1.2 color settings
|
||||
Yii::$app->settings->set('colorDefault', $this->variable('default'));
|
||||
Yii::$app->settings->set('colorPrimary', $this->variable('default'));
|
||||
Yii::$app->settings->set('colorInfo', $this->variable('default'));
|
||||
|
@ -70,6 +70,9 @@ Please see the following file for 1.2 changes (at the buttom):
|
||||
- Pjax
|
||||
- modules/tour/widgets/views/tourPanel.php
|
||||
|
||||
- Notification:
|
||||
- modules/notification/widget/views/overview.php
|
||||
|
||||
We've integrated a new Space and UserPicker widget. Please check the following files for
|
||||
|
||||
## Migrate to 1.1
|
||||
|
@ -21,6 +21,15 @@ use humhub\modules\friendship\models\Friendship;
|
||||
*/
|
||||
class Module extends \humhub\components\Module
|
||||
{
|
||||
/**
|
||||
* Defines the minimum polling interval if the default polling client is active.
|
||||
*/
|
||||
public $minPollInterval = 15;
|
||||
|
||||
/**
|
||||
* Defines the maximum polling interval if the default polling client is active.
|
||||
*/
|
||||
public $maxPollInterval = 45;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
@ -73,7 +82,7 @@ class Module extends \humhub\components\Module
|
||||
// Include friends
|
||||
if (Yii::$app->getModule('friendship')->isEnabled) {
|
||||
foreach (Friendship::getFriendsQuery($user)->all() as $user) {
|
||||
$legitimation[Content::VISIBILITY_PRIVATE] = $user->contentContainerRecord->id;
|
||||
$legitimation[Content::VISIBILITY_PRIVATE][] = $user->contentContainerRecord->id;
|
||||
}
|
||||
}
|
||||
|
||||
|
21
protected/humhub/modules/live/assets/LiveAsset.php
Normal file
21
protected/humhub/modules/live/assets/LiveAsset.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
namespace humhub\modules\live\assets;
|
||||
|
||||
use yii\web\AssetBundle;
|
||||
|
||||
class LiveAsset extends AssetBundle
|
||||
{
|
||||
public $sourcePath = '@live/resources';
|
||||
public $css = [];
|
||||
public $js = [
|
||||
'js/humhub.live.js',
|
||||
'js/humhub.live.poll.js'
|
||||
];
|
||||
}
|
21
protected/humhub/modules/live/resources/js/humhub.live.js
Normal file
21
protected/humhub/modules/live/resources/js/humhub.live.js
Normal file
@ -0,0 +1,21 @@
|
||||
humhub.module('live', function(module, require, $) {
|
||||
var liveClient;
|
||||
|
||||
var init = function() {
|
||||
try {
|
||||
var clientType = require(module.config.client.type);
|
||||
if(clientType) {
|
||||
liveClient = new clientType(module.config.client.options);
|
||||
} else {
|
||||
module.log.warn("Invalid live client configuration detected, live client could not be initialized.", module.config);
|
||||
}
|
||||
} catch(e) {
|
||||
module.log.warn("Invalid live client configuration detected, live client could not be initialized.", module.config);
|
||||
module.log.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
module.export({
|
||||
init: init
|
||||
});
|
||||
});
|
@ -0,0 +1,88 @@
|
||||
humhub.module('live.poll', function (module, require, $) {
|
||||
var client = require('client');
|
||||
var event = require('event');
|
||||
|
||||
var DEFAULT_MIN_INTERVAL = 10;
|
||||
var DEFAULT_MAX_INTERVAL = 45;
|
||||
var DEFAULT_INIT_DELAY = 5;
|
||||
|
||||
var PollClient = function (options) {
|
||||
if (!options) {
|
||||
module.log.error('Could not initialize PollClient. No options given!');
|
||||
return;
|
||||
}
|
||||
this.options = options;
|
||||
this.options.minInterval = options.minInterval || DEFAULT_MIN_INTERVAL;
|
||||
this.options.maxInterval = options.maxInterval || DEFAULT_MAX_INTERVAL;
|
||||
this.options.initDelay = options.initDelay || DEFAULT_INIT_DELAY;
|
||||
this.init();
|
||||
};
|
||||
|
||||
PollClient.prototype.init = function () {
|
||||
if (!this.options.url) {
|
||||
module.log.error('Could not initialize PollClient. No url option given!');
|
||||
return;
|
||||
}
|
||||
|
||||
this.delay = this.options.minInterval;
|
||||
this.lastCall = Date.now();
|
||||
this.call = $.proxy(this.update, this);
|
||||
this.handle = $.proxy(this.handleUpdate, this);
|
||||
setTimeout(this.call, DEFAULT_INIT_DELAY);
|
||||
};
|
||||
|
||||
PollClient.prototype.update = function () {
|
||||
setTimeout(this.call, this.getDelay());
|
||||
client.get(this.getCallOptions())
|
||||
.then(this.handle)
|
||||
.catch(_handleUpdateError);
|
||||
};
|
||||
|
||||
PollClient.prototype.handleUpdate = function (response) {
|
||||
this.lastTs = response.queryTime;
|
||||
this.lastCall = Date.now();
|
||||
var events = _groupEvents(response.events);
|
||||
$.each(events, function (type, events) {
|
||||
try {
|
||||
event.trigger(type, [events]);
|
||||
} catch (e) {
|
||||
module.log.error(e);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
var _groupEvents = function (events) {
|
||||
var result = {};
|
||||
$.each(events, function (id, liveEvent) {
|
||||
if (!result[liveEvent.type]) {
|
||||
result[liveEvent.type] = [liveEvent];
|
||||
} else {
|
||||
result[liveEvent.type].push(liveEvent);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
var _handleUpdateError = function (e) {
|
||||
module.log.error(e);
|
||||
};
|
||||
|
||||
PollClient.prototype.getCallOptions = function () {
|
||||
return {
|
||||
url: this.options.url,
|
||||
data: {
|
||||
last: this.lastTs
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
PollClient.prototype.getDelay = function () {
|
||||
return this.delay * 1000;
|
||||
};
|
||||
|
||||
module.export({
|
||||
PollClient: PollClient
|
||||
});
|
||||
});
|
@ -14,9 +14,4 @@ namespace humhub\modules\notification;
|
||||
class Module extends \humhub\components\Module
|
||||
{
|
||||
|
||||
/**
|
||||
* @var int the seconds the browser checks for new notifications
|
||||
*/
|
||||
public $pollClientUpdateInterval = 20;
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
namespace humhub\modules\notification\assets;
|
||||
|
||||
use yii\web\AssetBundle;
|
||||
|
||||
class NotificationAsset extends AssetBundle
|
||||
{
|
||||
|
||||
public $sourcePath = '@notification/resources';
|
||||
public $css = [];
|
||||
public $js = [
|
||||
'js/humhub.notification.js'
|
||||
];
|
||||
}
|
@ -211,12 +211,14 @@ class NotificationManager
|
||||
/**
|
||||
* Sets the notification space settings for this user (or global if no user is given).
|
||||
*
|
||||
* Those are the spaces for which the user want to receive ContentCreated Notifications.
|
||||
*
|
||||
* @param string[] $spaceGuids array of space guids
|
||||
* @param User $user
|
||||
* @param string[] $spaces array of space guids
|
||||
*/
|
||||
public function setSpaces(User $user = null, $spaceGuids)
|
||||
public function setSpaces($spaceGuids, User $user = null)
|
||||
{
|
||||
if (!$user) {
|
||||
if (!$user) { // Note: global notification space settings are currently not active!
|
||||
return Yii::$app->getModule('notification')->settings->setSerialized('sendNotificationSpaces', $spaceGuids);
|
||||
}
|
||||
|
||||
@ -247,6 +249,36 @@ class NotificationManager
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the enable_html5_desktop_notifications setting for the given user or global if no user is given.
|
||||
*
|
||||
* @param type $value
|
||||
* @param User $user
|
||||
*/
|
||||
public function setDesktopNoficationSettings($value = 0, User $user = null)
|
||||
{
|
||||
$module = Yii::$app->getModule('notification');
|
||||
$settingManager = ($user) ? $module->settings->user($user) : $module->settings;
|
||||
$settingManager->set('enable_html5_desktop_notifications', $value);
|
||||
}
|
||||
|
||||
public function getDesktopNoficationSettings(User $user = null)
|
||||
{
|
||||
if($user) {
|
||||
return Yii::$app->getModule('notification')->settings->user($user)->getInherit('enable_html5_desktop_notifications');
|
||||
} else {
|
||||
return Yii::$app->getModule('notification')->settings->get('enable_html5_desktop_notifications');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the send_notifications settings for the given space and user.
|
||||
*
|
||||
* @param User $user user instance for which this settings will aplly
|
||||
* @param Space $space which notifications will be followed / unfollowed
|
||||
* @param type $follow the setting value (true by default)
|
||||
* @return type
|
||||
*/
|
||||
public function setSpaceSetting(User $user = null, Space $space, $follow = true)
|
||||
{
|
||||
$membership = $space->getMembership($user->id);
|
||||
|
@ -18,8 +18,7 @@ class WebNotificationTarget extends NotificationTarget
|
||||
public $id = 'web';
|
||||
|
||||
/**
|
||||
* Since the WebNotificationTarget only requires the Notification ActiveRecord to be persisted,
|
||||
* this handler only check the presence of the related Notification record.
|
||||
* Handles Webnotifications by setting the send_web_notifications flag and sending an live event.
|
||||
*/
|
||||
public function handle(BaseNotification $notification, User $user)
|
||||
{
|
||||
@ -32,7 +31,8 @@ class WebNotificationTarget extends NotificationTarget
|
||||
|
||||
Yii::$app->live->send(new \humhub\modules\notification\live\NewNotification([
|
||||
'notificationId' => $notification->record->id,
|
||||
'contentContainerId' => $user->contentcontainer_id
|
||||
'contentContainerId' => $user->contentcontainer_id,
|
||||
'text' => $notification->text()
|
||||
]));
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,6 @@ namespace humhub\modules\notification\controllers;
|
||||
use Yii;
|
||||
use humhub\components\Controller;
|
||||
use humhub\modules\notification\models\Notification;
|
||||
use humhub\modules\notification\components\BaseNotification;
|
||||
|
||||
/**
|
||||
* ListController
|
||||
@ -40,27 +39,19 @@ class ListController extends Controller
|
||||
{
|
||||
Yii::$app->response->format = 'json';
|
||||
|
||||
$query = Notification::findGrouped();
|
||||
|
||||
$maxId = (int) Yii::$app->request->get('from', 0);
|
||||
if ($maxId != 0) {
|
||||
$query->andWhere(['<', 'id', $maxId]);
|
||||
}
|
||||
$query->andWhere(['user_id' => Yii::$app->user->id]);
|
||||
$query->limit(6);
|
||||
$notifications = Notification::loadMore(Yii::$app->request->get('from', 0));
|
||||
$lastEntryId = 0;
|
||||
|
||||
$output = "";
|
||||
|
||||
$notifications = $query->all();
|
||||
$lastEntryId = 0;
|
||||
foreach ($notifications as $notification) {
|
||||
$output .= $notification->getBaseModel()->render();
|
||||
$lastEntryId = $notification->id;
|
||||
}
|
||||
|
||||
return [
|
||||
'output' => $output,
|
||||
'newNotifications' => Notification::findUnseen()->count(),
|
||||
'lastEntryId' => $lastEntryId,
|
||||
'output' => $output,
|
||||
'counter' => count($notifications)
|
||||
];
|
||||
}
|
||||
@ -70,6 +61,8 @@ class ListController extends Controller
|
||||
*/
|
||||
public function actionMarkAsSeen()
|
||||
{
|
||||
$this->forcePostRequest();
|
||||
|
||||
Yii::$app->response->format = 'json';
|
||||
$count = Notification::updateAll(['seen' => 1], ['user_id' => Yii::$app->user->id]);
|
||||
|
||||
@ -81,6 +74,8 @@ class ListController extends Controller
|
||||
|
||||
/**
|
||||
* Returns new notifications
|
||||
*
|
||||
* @deprecated since version 1.2
|
||||
*/
|
||||
public function actionGetUpdateJson()
|
||||
{
|
||||
@ -98,20 +93,14 @@ class ListController extends Controller
|
||||
*/
|
||||
public static function getUpdates()
|
||||
{
|
||||
$user = Yii::$app->user->getIdentity();
|
||||
$update['newNotifications'] = Notification::findUnseen()->count();
|
||||
|
||||
$query = Notification::findGrouped()
|
||||
->andWhere(['seen' => 0])
|
||||
->orWhere(['IS', 'seen', new \yii\db\Expression('NULL')])
|
||||
->andWhere(['user_id' => $user->id]);
|
||||
$unnotified = Notification::findUnnotifiedInFrontend()->all();
|
||||
|
||||
$update['newNotifications'] = $query->count();
|
||||
$query->andWhere(['desktop_notified' => 0]);
|
||||
|
||||
$update['notifications'] = array();
|
||||
foreach ($query->all() as $notification) {
|
||||
if (Yii::$app->getModule('notification')->settings->user()->get("enable_html5_desktop_notifications", Yii::$app->getModule('notification')->settings->get('enable_html5_desktop_notifications'))) {
|
||||
$update['notifications'][] = $notification->getClass()->render(BaseNotification::OUTPUT_TEXT);
|
||||
$update['notifications'] = [];
|
||||
foreach ($unnotified as $notification) {
|
||||
if(Yii::$app->getModule('notification')->settings->user()->getInherit('enable_html5_desktop_notifications', true)) {
|
||||
$update['notifications'][] = $notification->getBaseModel()->text();
|
||||
}
|
||||
$notification->desktop_notified = 1;
|
||||
$notification->update();
|
||||
|
@ -46,7 +46,6 @@ class OverviewController extends Controller
|
||||
$filterForm->load(Yii::$app->request->get());
|
||||
|
||||
$query = Notification::findGrouped();
|
||||
$query->andWhere(['user_id' => Yii::$app->user->id]);
|
||||
|
||||
if ($filterForm->hasFilter()) {
|
||||
$query->andFilterWhere(['not in', 'class', $filterForm->getExcludeClassFilter()]);
|
||||
|
@ -24,6 +24,11 @@ class NewNotification extends LiveEvent
|
||||
*/
|
||||
public $notificationId;
|
||||
|
||||
/**
|
||||
* @var string text representation used for frotnend desktop notifications
|
||||
*/
|
||||
public $text;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
@ -53,7 +53,7 @@ class Notification extends \humhub\components\ActiveRecord
|
||||
}
|
||||
|
||||
// Disable web notification by default, they will be enabld within the web target if allowed by the user.
|
||||
if($this->send_web_notifications === null) {
|
||||
if ($this->send_web_notifications === null) {
|
||||
$this->send_web_notifications = 0;
|
||||
}
|
||||
}
|
||||
@ -189,23 +189,74 @@ class Notification extends \humhub\components\ActiveRecord
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds notifications grouped when available
|
||||
* Loads a certain amount ($limit) of grouped notifications from a given id set by $from.
|
||||
*
|
||||
* @param integer $from notificatoin id which was the last loaded entry.
|
||||
* @param limit $limit limit count of results.
|
||||
* @since 1.2
|
||||
*/
|
||||
public static function loadMore($from = 0, $limit = 6)
|
||||
{
|
||||
$query = Notification::findGrouped();
|
||||
|
||||
if ($from != 0) {
|
||||
$query->andWhere(['<', 'id', $from]);
|
||||
}
|
||||
|
||||
$query->limit($limit);
|
||||
|
||||
return $query->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds grouped notifications if $sendWebNotifications is set to 1 we filter only notifications
|
||||
* with send_web_notifications setting to 1.
|
||||
*
|
||||
* @return \yii\db\ActiveQuery
|
||||
*/
|
||||
public static function findGrouped()
|
||||
public static function findGrouped(User $user = null, $sendWebNotifications = 1)
|
||||
{
|
||||
$user = ($user) ? $user : Yii::$app->user->getIdentity();
|
||||
|
||||
$query = self::find();
|
||||
$query->addSelect(['notification.*',
|
||||
new \yii\db\Expression('count(distinct(originator_user_id)) as group_count'),
|
||||
new \yii\db\Expression('max(created_at) as group_created_at'),
|
||||
new \yii\db\Expression('min(seen) as group_seen'),
|
||||
]);
|
||||
$query->andWhere(['send_web_notifications' => 1]);
|
||||
|
||||
$query->andWhere(['user_id' => $user->id]);
|
||||
|
||||
$query->andWhere(['send_web_notifications' => $sendWebNotifications]);
|
||||
$query->addGroupBy(['COALESCE(group_key, id)', 'class']);
|
||||
$query->orderBy(['group_seen' => SORT_ASC, 'group_created_at' => SORT_DESC]);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all grouped unseen notifications for the given user or the current loggedIn user
|
||||
* if no User instance is provided.
|
||||
*
|
||||
* @param \humhub\modules\notification\models\User $user
|
||||
* @since 1.2
|
||||
*/
|
||||
public static function findUnseen(User $user = null)
|
||||
{
|
||||
return Notification::findGrouped($user)
|
||||
->andWhere(['seen' => 0])
|
||||
->orWhere(['IS', 'seen', new \yii\db\Expression('NULL')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all grouped unseen notifications which were not already sent to the frontend.
|
||||
*
|
||||
* @param \humhub\modules\notification\models\User $user
|
||||
* @since 1.2
|
||||
*/
|
||||
public static function findUnnotifiedInFrontend(User $user = null)
|
||||
{
|
||||
return self::findUnseen($user)->andWhere(['desktop_notified' => 0]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,10 +27,15 @@ class NotificationSettings extends \yii\base\Model
|
||||
public $spaceGuids = [];
|
||||
|
||||
/**
|
||||
* @var type User instance for which the settings should by appleid, if null global settings are used.
|
||||
* @var \humhub\modules\user\models\User instance for which the settings should by appleid, if null global settings are used.
|
||||
*/
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* @var boolean manage if the user/users should receive desktop notifications.
|
||||
*/
|
||||
public $desktopNotifications;
|
||||
|
||||
/**
|
||||
* @var NotificationTarget[]
|
||||
*/
|
||||
@ -41,12 +46,16 @@ class NotificationSettings extends \yii\base\Model
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
if($this->user) {
|
||||
if ($this->user) {
|
||||
// Note the user object has to be provided in the model constructor.
|
||||
$spaces = Yii::$app->notification->getSpaces($this->user);
|
||||
$this->spaceGuids = array_map(function ($space) { return $space->guid;}, $spaces);
|
||||
$this->spaceGuids = array_map(function ($space) {
|
||||
return $space->guid;
|
||||
}, $spaces);
|
||||
}
|
||||
|
||||
$this->desktopNotifications = Yii::$app->notification->getDesktopNoficationSettings($this->user);
|
||||
|
||||
$module = Yii::$app->getModule('notification');
|
||||
return ($this->user) ? $module->settings->user($this->user) : $module->settings;
|
||||
}
|
||||
@ -57,6 +66,7 @@ class NotificationSettings extends \yii\base\Model
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
['desktopNotifications', 'integer'],
|
||||
[['settings', 'spaceGuids'], 'safe']
|
||||
];
|
||||
}
|
||||
@ -66,8 +76,14 @@ class NotificationSettings extends \yii\base\Model
|
||||
*/
|
||||
public function attributeLabels()
|
||||
{
|
||||
if ($this->user) {
|
||||
$desktopNotificationLabel = Yii::t('NotificationModule.models_forms_NotificationSettings', 'Receive desktop notifications when you are online.');
|
||||
} else {
|
||||
$desktopNotificationLabel = Yii::t('NotificationModule.models_forms_NotificationSettings', 'Allow desktop notifications by default.');
|
||||
}
|
||||
return [
|
||||
'spaceGuids' => Yii::t('NotificationModule.models_forms_NotificationSettings', 'Receive \'New Content\' Notifications for the following spaces')
|
||||
'spaceGuids' => Yii::t('NotificationModule.models_forms_NotificationSettings', 'Receive \'New Content\' Notifications for the following spaces'),
|
||||
'desktopNotifications' => $desktopNotificationLabel
|
||||
];
|
||||
}
|
||||
|
||||
@ -77,7 +93,7 @@ class NotificationSettings extends \yii\base\Model
|
||||
*/
|
||||
public function isUserSettingLoaded()
|
||||
{
|
||||
if($this->user) {
|
||||
if ($this->user) {
|
||||
return $this->getSettings()->get('notification.like_email') !== null;
|
||||
}
|
||||
return false;
|
||||
@ -132,7 +148,8 @@ class NotificationSettings extends \yii\base\Model
|
||||
}
|
||||
|
||||
$this->saveSpaceSettings();
|
||||
Yii::$app->notification->setSpaces($this->user, $this->spaceGuids);
|
||||
Yii::$app->notification->setDesktopNoficationSettings($this->desktopNotifications, $this->user);
|
||||
Yii::$app->notification->setSpaces($this->spaceGuids, $this->user);
|
||||
|
||||
$settings = $this->getSettings();
|
||||
|
||||
@ -175,14 +192,14 @@ class NotificationSettings extends \yii\base\Model
|
||||
*
|
||||
* @return type
|
||||
*/
|
||||
public function saveSpaceSettings()
|
||||
private function saveSpaceSettings()
|
||||
{
|
||||
// There is no global space setting right now.
|
||||
if(!$this->user) {
|
||||
if (!$this->user) {
|
||||
return;
|
||||
}
|
||||
|
||||
Yii::$app->notification->setSpaces($this->user, $this->spaceGuids);
|
||||
Yii::$app->notification->setSpaces($this->spaceGuids, $this->user);
|
||||
}
|
||||
|
||||
public function getSettings()
|
||||
@ -204,17 +221,18 @@ class NotificationSettings extends \yii\base\Model
|
||||
|
||||
public function resetUserSettings()
|
||||
{
|
||||
if(!$this->user) {
|
||||
if (!$this->user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$settings = $this->getSettings();
|
||||
foreach($this->targets() as $target) {
|
||||
foreach($this->categories() as $category) {
|
||||
foreach ($this->targets() as $target) {
|
||||
foreach ($this->categories() as $category) {
|
||||
$settings->delete($target->getSettingKey($category));
|
||||
}
|
||||
}
|
||||
Yii::$app->notification->setSpaces($this->user, []);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,208 @@
|
||||
/**
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
humhub.module('notification', function (module, require, $) {
|
||||
var util = require('util');
|
||||
var object = util.object;
|
||||
var string = util.string;
|
||||
var Widget = require('ui.widget').Widget;
|
||||
var event = require('event');
|
||||
var client = require('client');
|
||||
var status = require('ui.status');
|
||||
|
||||
module.initOnPjaxLoad = true;
|
||||
|
||||
var NotificationDropDown = function (node, options) {
|
||||
Widget.call(this, node, options);
|
||||
};
|
||||
|
||||
object.inherits(NotificationDropDown, Widget);
|
||||
|
||||
NotificationDropDown.prototype.init = function (update) {
|
||||
this.isOpen = false;
|
||||
this.lastEntryLoaded = false;
|
||||
this.lastEntryId = 0;
|
||||
this.originalTitle = document.title;
|
||||
this.initDropdown();
|
||||
this.handleResult(update);
|
||||
this.sendDesktopNotifications(update);
|
||||
|
||||
var that = this;
|
||||
event.on('humhub.modules.notification.live.NewNotification', function (evt, events) {
|
||||
var count = (that.$.data('notification-count')) ? parseInt(that.$.data('notification-count')) + events.length : events.length;
|
||||
that.updateCount(count);
|
||||
that.sendDesktopNotifications(events);
|
||||
});
|
||||
};
|
||||
|
||||
NotificationDropDown.prototype.initDropdown = function () {
|
||||
this.$entryList = this.$.find('ul.media-list');
|
||||
this.$dropdown = this.$.find('#dropdown-notifications');
|
||||
|
||||
var that = this;
|
||||
this.$entryList.scroll(function () {
|
||||
var containerHeight = that.$entryList.height();
|
||||
var scrollHeight = that.$entryList.prop("scrollHeight");
|
||||
var currentScrollPosition = that.$entryList.scrollTop();
|
||||
|
||||
// load more activites if current scroll position is near scroll height
|
||||
if (currentScrollPosition >= (scrollHeight - containerHeight - 1)) {
|
||||
if (!that.lastEntryLoaded) {
|
||||
that.loadEntries();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
NotificationDropDown.prototype.toggle = function (evt) {
|
||||
// Always reset the loading settings so we reload the whole dropdown.
|
||||
this.lastEntryLoaded = false;
|
||||
this.lastEntryId = 0;
|
||||
|
||||
// Since the handler will be called before the bootstrap trigger it's an open event if the dropdown is not visible yet
|
||||
this.isOpen = !this.$dropdown.is(':visible');
|
||||
if (this.isOpen) {
|
||||
this.$entryList.empty().hide();
|
||||
this.loadEntries();
|
||||
}
|
||||
};
|
||||
|
||||
NotificationDropDown.prototype.loadEntries = function () {
|
||||
if (this.loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
var that = this;
|
||||
this.loader();
|
||||
client.get(module.config.loadEntriesUrl, {data: {from: this.lastEntryId}})
|
||||
.then($.proxy(this.handleResult, this))
|
||||
.catch(_errorHandler)
|
||||
.finally(function () {
|
||||
that.loader(false);
|
||||
that.loading = false;
|
||||
});
|
||||
};
|
||||
|
||||
NotificationDropDown.prototype.handleResult = function (response) {
|
||||
if (!response.counter) {
|
||||
this.$entryList.append(string.template(module.templates.placeholder, {'text': module.text('placeholder')}));
|
||||
} else {
|
||||
this.lastEntryId = response.lastEntryId;
|
||||
this.$entryList.append(response.output);
|
||||
$('span.time').timeago();
|
||||
}
|
||||
|
||||
|
||||
this.updateCount(parseInt(response.newNotifications));
|
||||
this.lastEntryLoaded = (response.counter < 6);
|
||||
this.$entryList.fadeIn('fast');
|
||||
};
|
||||
|
||||
NotificationDropDown.prototype.updateCount = function ($count) {
|
||||
$('#badge-notifications').hide();
|
||||
if (!$count) {
|
||||
$('#mark-seen-link').hide();
|
||||
$('#icon-notifications .fa').removeClass("animated swing");
|
||||
} else if (this.$.data('notification-count') !== $count) {
|
||||
updateTitle($count);
|
||||
$('#badge-notifications').html($count);
|
||||
$('#mark-seen-link').show();
|
||||
$('#badge-notifications').fadeIn('fast');
|
||||
|
||||
// Clone icon to retrigger animation
|
||||
var $icon = $('#icon-notifications .fa');
|
||||
var $clone = $icon.clone();
|
||||
$clone.addClass("animated swing");
|
||||
$icon.replaceWith($clone);
|
||||
}
|
||||
|
||||
this.$.data('notification-count', $count);
|
||||
};
|
||||
|
||||
NotificationDropDown.prototype.sendDesktopNotifications = function (response) {
|
||||
if (!response) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.text) { // Single Notification
|
||||
module.sendDesktopNotifiaction(response.text);
|
||||
} else if (response.notifications) { // Multiple Notifications
|
||||
var $notifications = response.notifications;
|
||||
for (var i = 0; i < $notifications.length; i++) {
|
||||
module.sendDesktopNotifiaction($notifications[i]);
|
||||
}
|
||||
} else if (object.isArray(response)) { // Live events
|
||||
$.each(response, function (i, liveEvent) {
|
||||
if (liveEvent.data && liveEvent.data.text) {
|
||||
module.sendDesktopNotifiaction(liveEvent.data.text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var sendDesktopNotifiaction = function (body, icon) {
|
||||
icon = icon || module.config.icon;
|
||||
if (body && body.length) {
|
||||
notify.createNotification("Notification", {body: body, icon: icon});
|
||||
}
|
||||
};
|
||||
|
||||
var _errorHandler = function (e) {
|
||||
module.log.error(e, true);
|
||||
};
|
||||
|
||||
NotificationDropDown.prototype.loader = function (show) {
|
||||
if (show !== false) {
|
||||
this.$.find('#loader_notifications').show();
|
||||
} else {
|
||||
this.$.find('#loader_notifications').hide();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
NotificationDropDown.prototype.markAsSeen = function (evt) {
|
||||
client.post(evt).then(function (response) {
|
||||
$('#badge-notifications').hide();
|
||||
$('#mark-seen-link').hide();
|
||||
updateTitle(false);
|
||||
}).catch(function (e) {
|
||||
module.log.error(e, true);
|
||||
});
|
||||
};
|
||||
|
||||
var updateTitle = function ($count) {
|
||||
if ($count) {
|
||||
document.title = '(' + $count + ') ' + status.getState().title;
|
||||
} else if ($count === false) {
|
||||
document.title = status.getState().title;
|
||||
}
|
||||
};
|
||||
|
||||
module.templates = {
|
||||
placeholder: '<li class="placeholder">{text}</li>'
|
||||
};
|
||||
|
||||
var init = function ($pjax) {
|
||||
updateTitle($('#notification_widget').data('notification-count'));
|
||||
if (!$pjax) {
|
||||
$("#dropdown-notifications ul.media-list").niceScroll({
|
||||
cursorwidth: "7",
|
||||
cursorborder: "",
|
||||
cursorcolor: "#555",
|
||||
cursoropacitymax: "0.2",
|
||||
nativeparentscrolling: false,
|
||||
railpadding: {top: 0, right: 3, left: 0, bottom: 0}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.export({
|
||||
init: init,
|
||||
sendDesktopNotifiaction: sendDesktopNotifiaction,
|
||||
NotificationDropDown: NotificationDropDown
|
||||
});
|
||||
});
|
||||
|
@ -1,2 +1,21 @@
|
||||
<?php
|
||||
return [];
|
||||
return [
|
||||
['id' => 1, 'class' => 'humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification', 'user_id' => 2, 'created_at' => '2014-08-08 05:36:05', 'send_web_notifications' => 1],
|
||||
['id' => 2, 'class' => 'humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification', 'user_id' => 2, 'created_at' => '2014-08-08 05:37:05', 'send_web_notifications' => 1],
|
||||
['id' => 3, 'class' => 'humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification', 'user_id' => 2, 'created_at' => '2014-08-08 05:38:05', 'send_web_notifications' => 1],
|
||||
['id' => 4, 'class' => 'humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification', 'user_id' => 2, 'created_at' => '2014-08-08 05:39:05', 'send_web_notifications' => 1],
|
||||
['id' => 5, 'class' => 'humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification', 'user_id' => 2, 'created_at' => '2014-08-08 05:40:05', 'send_web_notifications' => 1],
|
||||
['id' => 6, 'class' => 'humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification', 'user_id' => 2, 'created_at' => '2014-08-08 05:41:05', 'send_web_notifications' => 1],
|
||||
['id' => 7, 'class' => 'humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification', 'user_id' => 2, 'created_at' => '2014-08-08 05:42:05', 'send_web_notifications' => 1],
|
||||
['id' => 8, 'class' => 'humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification', 'user_id' => 2, 'created_at' => '2014-08-08 05:43:05', 'send_web_notifications' => 1],
|
||||
['id' => 9, 'class' => 'humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification', 'user_id' => 2, 'created_at' => '2014-08-08 05:44:05', 'send_web_notifications' => 1],
|
||||
['id' => 10, 'class' => 'humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification', 'user_id' => 2, 'created_at' => '2014-08-08 05:45:05', 'send_web_notifications' => 1],
|
||||
['id' => 11, 'class' => 'humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification', 'user_id' => 2, 'created_at' => '2014-08-08 05:46:05', 'send_web_notifications' => 1],
|
||||
['id' => 12, 'class' => 'humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification', 'user_id' => 2, 'created_at' => '2014-08-08 05:47:05', 'send_web_notifications' => 1],
|
||||
['id' => 13, 'class' => 'humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification', 'user_id' => 2, 'created_at' => '2014-08-08 05:48:05', 'send_web_notifications' => 1],
|
||||
['id' => 14, 'class' => 'humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification', 'user_id' => 2, 'created_at' => '2014-08-08 05:49:05', 'send_web_notifications' => 1],
|
||||
['id' => 15, 'class' => 'humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification', 'user_id' => 2, 'created_at' => '2014-08-08 05:50:05', 'send_web_notifications' => 1],
|
||||
['id' => 16, 'class' => 'humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification', 'user_id' => 2, 'created_at' => '2014-08-08 05:51:05', 'send_web_notifications' => 1],
|
||||
['id' => 17, 'class' => 'humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification', 'user_id' => 2, 'created_at' => '2014-08-08 05:52:05', 'send_web_notifications' => 1],
|
||||
['id' => 18, 'class' => 'humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification', 'user_id' => 2, 'created_at' => '2014-08-08 05:53:05', 'send_web_notifications' => 1],
|
||||
];
|
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace humhub\modules\notification\tests\codeception\unit\rendering;
|
||||
|
||||
use Yii;
|
||||
use tests\codeception\_support\HumHubDbTestCase;
|
||||
use humhub\modules\notification\models\Notification;
|
||||
use Codeception\Specify;
|
||||
|
||||
class LoadNotificationTest extends HumHubDbTestCase
|
||||
{
|
||||
|
||||
use Specify;
|
||||
|
||||
public function testDefaultView()
|
||||
{
|
||||
$this->becomeUser('User1');
|
||||
|
||||
$notifications = Notification::loadMore();
|
||||
$this->assertEquals(6, count($notifications));
|
||||
|
||||
$ids = array_map(create_function('$o', 'return $o->id;'), $notifications);
|
||||
$this->assertEquals(18, max($ids));
|
||||
$this->assertEquals(13, min($ids));
|
||||
|
||||
$notifications = Notification::loadMore(13);
|
||||
$this->assertEquals(6, count($notifications));
|
||||
|
||||
$ids = array_map(create_function('$o', 'return $o->id;'), $notifications);
|
||||
$this->assertEquals(12, max($ids));
|
||||
$this->assertEquals(7, min($ids));
|
||||
|
||||
$notifications = Notification::loadMore(7);
|
||||
$this->assertEquals(6, count($notifications));
|
||||
|
||||
$ids = array_map(create_function('$o', 'return $o->id;'), $notifications);
|
||||
$this->assertEquals(6, max($ids));
|
||||
$this->assertEquals(1, min($ids));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace humhub\modules\notification\tests\codeception\unit\category\notifications;
|
||||
|
||||
/**
|
||||
* Description of TestNotification
|
||||
*
|
||||
* @author buddha
|
||||
*/
|
||||
class TestNotification extends \humhub\modules\notification\components\BaseNotification
|
||||
{
|
||||
|
||||
}
|
@ -6,7 +6,7 @@ use yii\widgets\ActiveForm;
|
||||
<div class="panel-body">
|
||||
<h4><?= Yii::t('AdminModule.setting', 'Notification Settings'); ?></h4>
|
||||
<div class="help-block">
|
||||
<?= Yii::t('NotificationModule.base', 'Notifications are sent instantly to users to inform them about new activities in your network.'); ?><br />
|
||||
<?= Yii::t('NotificationModule.base', 'Notifications are sent directly to your users to inform them about new activities in your network.'); ?><br />
|
||||
<?= Yii::t('NotificationModule.base', 'On this page you can define the default behavior for your users. These settings can be overwritten by users in their account settings page.'); ?>
|
||||
<br />
|
||||
</div>
|
||||
|
@ -7,7 +7,7 @@
|
||||
/** @var string $content */
|
||||
|
||||
?>
|
||||
<li class="<?php if ($isNew) : ?>new<?php endif; ?>">
|
||||
<li class="<?php if ($isNew) : ?>new<?php endif; ?>" data-notification-id="<?= $record->id ?>">
|
||||
<a href="<?= $url; ?>">
|
||||
<div class="media">
|
||||
|
||||
|
@ -28,10 +28,9 @@ class Overview extends \yii\base\Widget
|
||||
return;
|
||||
}
|
||||
|
||||
return $this->render('overview', array(
|
||||
return $this->render('overview', [
|
||||
'update' => \humhub\modules\notification\controllers\ListController::getUpdates(),
|
||||
'updateInterval' => Yii::$app->getModule('notification')->pollClientUpdateInterval
|
||||
));
|
||||
'unseenCount' => \humhub\modules\notification\models\Notification::findUnseen()->count()]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,9 @@
|
||||
use yii\bootstrap\Html
|
||||
?>
|
||||
|
||||
<br />
|
||||
<?= $form->field($model, 'desktopNotifications')->checkbox(); ?>
|
||||
|
||||
<?php if($showSpaces) : ?>
|
||||
<?= humhub\modules\space\widgets\SpacePickerField::widget([
|
||||
'form' => $form,
|
||||
|
@ -1,207 +1,43 @@
|
||||
<?php
|
||||
|
||||
use yii\helpers\Url;
|
||||
|
||||
$this->registerJsConfig('notification', [
|
||||
'icon' => $this->theme->getBaseUrl().'/ico/notification-o.png',
|
||||
'loadEntriesUrl' => Url::to(['/notification/list']),
|
||||
'text' => [
|
||||
'placeholder' => Yii::t('NotificationModule.widgets_views_list', 'There are no notifications yet.')
|
||||
]
|
||||
]);
|
||||
|
||||
?>
|
||||
<div class="btn-group">
|
||||
<a href="#" id="icon-notifications" data-toggle="dropdown">
|
||||
<div id="notification_widget" data-ui-widget="notification.NotificationDropDown" data-ui-init='<?= \yii\helpers\Json::encode($update); ?>' class="btn-group">
|
||||
<a href="#" id="icon-notifications" data-action-click='toggle' data-toggle="dropdown" >
|
||||
<i class="fa fa-bell"></i>
|
||||
</a>
|
||||
<span id="badge-notifications" style="display:none;" class="label label-danger label-notifications">1</span>
|
||||
|
||||
<span id="badge-notifications" style="display:none;" class="label label-danger label-notifications"></span>
|
||||
|
||||
<!-- container for ajax response -->
|
||||
<ul id="dropdown-notifications" class="dropdown-menu">
|
||||
<li class="dropdown-header">
|
||||
<div class="arrow"></div><?php echo Yii::t('NotificationModule.widgets_views_list', 'Notifications'); ?>
|
||||
<div class="dropdown-header-link"><a id="mark-seen-link"
|
||||
href="javascript:markNotificationsAsSeen();"><?php echo Yii::t('NotificationModule.widgets_views_list', 'Mark all as seen'); ?></a>
|
||||
<div class="arrow"></div><?= Yii::t('NotificationModule.widgets_views_list', 'Notifications'); ?>
|
||||
<div class="dropdown-header-link">
|
||||
<a id="mark-seen-link" data-action-click='markAsSeen' data-action-url="<?= Url::to(['/notification/list/mark-as-seen']); ?>">
|
||||
<?= Yii::t('NotificationModule.widgets_views_list', 'Mark all as seen'); ?>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<ul class="media-list"></ul>
|
||||
<li id="loader_notifications">
|
||||
<?php echo \humhub\widgets\LoaderWidget::widget(); ?>
|
||||
<?= \humhub\widgets\LoaderWidget::widget(); ?>
|
||||
</li>
|
||||
<li>
|
||||
<div class="dropdown-footer">
|
||||
<a class="btn btn-default col-md-12" href="<?= Url::to(['/notification/overview']); ?>">
|
||||
<?php echo Yii::t('NotificationModule.widgets_views_list', 'Show all notifications'); ?>
|
||||
<?= Yii::t('NotificationModule.widgets_views_list', 'Show all notifications'); ?>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
// set niceScroll to notification list
|
||||
$("#dropdown-notifications ul.media-list").niceScroll({
|
||||
cursorwidth: "7",
|
||||
cursorborder: "",
|
||||
cursorcolor: "#555",
|
||||
cursoropacitymax: "0.2",
|
||||
railpadding: {top: 0, right: 3, left: 0, bottom: 0}
|
||||
});
|
||||
|
||||
|
||||
function markNotificationsAsSeen() {
|
||||
// call ajax request to mark all notifications as seen
|
||||
jQuery.ajax({
|
||||
'type': 'GET',
|
||||
'url': '<?php echo Url::to(['/notification/list/mark-as-seen', 'ajax' => 1]); ?>',
|
||||
'cache': false,
|
||||
'data': jQuery(this).parents("form").serialize(),
|
||||
'success': function (html) {
|
||||
// hide notification badge at the top menu
|
||||
$('#badge-notifications').css('display', 'none');
|
||||
$('#mark-seen-link').css('display', 'none');
|
||||
|
||||
// remove notification count from page title
|
||||
var pageTitle = $('title').text().replace(/\(.+?\)/g, '');
|
||||
$('title').text(pageTitle);
|
||||
|
||||
}});
|
||||
}
|
||||
|
||||
var originalTitle;
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
originalTitle = document.title;
|
||||
|
||||
// set the ID for the last loaded activity entry to 1
|
||||
var notificationLastLoadedEntryId = 0;
|
||||
|
||||
// save if the last entries are already loaded
|
||||
var notificationLastEntryReached = false;
|
||||
|
||||
// safe action url
|
||||
var _notificationUrl = '<?php echo Url::to(['/notification/list/index', 'from' => 'lastEntryId', 'ajax' => 1]); ?>';
|
||||
|
||||
// Open the notification menu
|
||||
$('#icon-notifications').click(function () {
|
||||
// reset variables by dropdown reopening
|
||||
notificationLastLoadedEntryId = 0;
|
||||
notificationLastEntryReached = false;
|
||||
|
||||
// remove all notification entries from dropdown
|
||||
$('#dropdown-notifications ul.media-list').find('li').remove();
|
||||
|
||||
// checking if ajax loading is necessary or the last entries are already loaded
|
||||
if (notificationLastEntryReached == false) {
|
||||
// load notifications
|
||||
loadNotificationEntries();
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
$('#dropdown-notifications ul.media-list').scroll(function () {
|
||||
|
||||
// save height of the overflow container
|
||||
var _containerHeight = $("#dropdown-notifications ul.media-list").height();
|
||||
|
||||
// save scroll height
|
||||
var _scrollHeight = $("#dropdown-notifications ul.media-list").prop("scrollHeight");
|
||||
|
||||
// save current scrollbar position
|
||||
var _currentScrollPosition = $('#dropdown-notifications ul.media-list').scrollTop();
|
||||
|
||||
// load more activites if current scroll position is near scroll height
|
||||
if (_currentScrollPosition >= (_scrollHeight - _containerHeight - 1)) {
|
||||
|
||||
// checking if ajax loading is necessary or the last entries are already loaded
|
||||
if (notificationLastEntryReached == false) {
|
||||
|
||||
// load more notifications
|
||||
loadNotificationEntries();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var notification_placeholder = "<?php echo Yii::t('NotificationModule.widgets_views_list', 'There are no notifications yet.') ?>"
|
||||
|
||||
|
||||
function loadNotificationEntries() {
|
||||
|
||||
// replace placeholder name with the id from the last loaded entry
|
||||
var _modifiedNotificationUrl = _notificationUrl.replace('lastEntryId', notificationLastLoadedEntryId)
|
||||
|
||||
// show loader
|
||||
$("#loader_notifications .loader").show();
|
||||
|
||||
// send ajax request
|
||||
jQuery.getJSON(_modifiedNotificationUrl, function (json) {
|
||||
|
||||
// hide loader
|
||||
$("#loader_notifications .loader").hide();
|
||||
|
||||
if (json.counter == 0) {
|
||||
$("#dropdown-notifications ul.media-list").append('<li class="placeholder">' + notification_placeholder + '</li>');
|
||||
notificationLastEntryReached = true;
|
||||
} else {
|
||||
|
||||
// save id from the last entry for the next loading
|
||||
notificationLastLoadedEntryId = json.lastEntryId;
|
||||
|
||||
if (json.counter < 6) {
|
||||
// prevent the next ajax calls, if there are no more entries
|
||||
notificationLastEntryReached = true;
|
||||
}
|
||||
|
||||
// add new entries
|
||||
$("#dropdown-notifications ul.media-list").append(json.output);
|
||||
|
||||
// format time
|
||||
$('span.time').timeago();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Regulary fetch new notifications
|
||||
*/
|
||||
reloadNotificationInterval = <?= $updateInterval * 1000; ?>;
|
||||
setInterval(function () {
|
||||
jQuery.getJSON("<?php echo Url::to(['/notification/list/get-update-json']); ?>", function (json) {
|
||||
handleJsonUpdate(json);
|
||||
});
|
||||
}, reloadNotificationInterval);
|
||||
|
||||
handleJsonUpdate(<?php echo \yii\helpers\Json::encode($update); ?>);
|
||||
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Handles JSON Update
|
||||
*
|
||||
* @param String json
|
||||
*/
|
||||
function handleJsonUpdate(json) {
|
||||
$newNotifications = parseInt(json.newNotifications);
|
||||
|
||||
// show or hide the badge for new notifications
|
||||
if ($newNotifications == 0) {
|
||||
document.title = originalTitle;
|
||||
$('#badge-notifications').css('display', 'none');
|
||||
$('#mark-seen-link').css('display', 'none');
|
||||
$('#icon-notifications .fa').removeClass("animated swing");
|
||||
} else {
|
||||
document.title = '(' + $newNotifications + ') ' + originalTitle;
|
||||
$('#badge-notifications').empty();
|
||||
$('#badge-notifications').append($newNotifications);
|
||||
$('#mark-seen-link').css('display', 'inline');
|
||||
$('#badge-notifications').fadeIn('fast');
|
||||
$('#icon-notifications .fa').addClass("animated swing");
|
||||
|
||||
var $notifications = json.notifications;
|
||||
for (var i = 0; i < $notifications.length; i++) {
|
||||
notify.createNotification("Notification", {body: $("<span />", {html: $notifications[i]}).text(), icon: "ico/alert.ico"})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</script>
|
@ -81,7 +81,7 @@ class HeaderControlsMenu extends \humhub\widgets\BaseMenu
|
||||
'label' => Yii::t('SpaceModule.widgets_SpaceAdminMenuWidget', 'Receive Notifications for new content'),
|
||||
'group' => 'admin',
|
||||
'url' => $this->space->createUrl('/space/membership/receive-notifications'),
|
||||
'icon' => '<i class="fa fa-star"></i>',
|
||||
'icon' => '<i class="fa fa-bell"></i>',
|
||||
'sortOrder' => 300,
|
||||
'isActive' => (Yii::$app->controller->id == "module"),
|
||||
'htmlOptions' => ['data-method' => 'POST']
|
||||
@ -91,7 +91,7 @@ class HeaderControlsMenu extends \humhub\widgets\BaseMenu
|
||||
'label' => Yii::t('SpaceModule.widgets_SpaceAdminMenuWidget', 'Don\'t receive notifications for new content'),
|
||||
'group' => 'admin',
|
||||
'url' => $this->space->createUrl('/space/membership/revoke-notifications'),
|
||||
'icon' => '<i class="fa fa-star-o"></i>',
|
||||
'icon' => '<i class="fa fa-bell-o"></i>',
|
||||
'sortOrder' => 300,
|
||||
'isActive' => (Yii::$app->controller->id == "module"),
|
||||
'htmlOptions' => ['data-method' => 'POST']
|
||||
|
@ -27,7 +27,7 @@ use \humhub\modules\space\models\Space;
|
||||
* </pre>
|
||||
*
|
||||
* @since 0.5
|
||||
* @deprecated since 1.2 use SpacePickerField instead
|
||||
* @deprecated since version 1.2 use SpacePickerField instead
|
||||
* @author Luke
|
||||
*/
|
||||
class Picker extends Widget
|
||||
|
@ -55,7 +55,7 @@ class AccountMenu extends BaseMenu
|
||||
|
||||
$this->addItem(array(
|
||||
'label' => Yii::t('UserModule.account', 'Notifications'),
|
||||
'icon' => '<i class="fa fa-star"></i>',
|
||||
'icon' => '<i class="fa fa-bell"></i>',
|
||||
'group' => 'account',
|
||||
'url' => Url::toRoute('/notification/user'),
|
||||
'sortOrder' => 106,
|
||||
|
@ -33,6 +33,18 @@ class CoreJsConfig extends Widget
|
||||
$this->getView()->registerJsConfig(
|
||||
[
|
||||
'user' => $userConfig,
|
||||
'live' => [
|
||||
'client' => [
|
||||
'type' => 'humhub.modules.live.poll.PollClient',
|
||||
'options' => [
|
||||
'url' => Url::to(['/live/poll']),
|
||||
'minInterval' => Yii::$app->getModule('live')->minPollInterval, // Minimal polling request delay variation
|
||||
'maxInterval' => Yii::$app->getModule('live')->maxPollInterval, // Maximal polling request delay in seconds
|
||||
'idleFactor' => 1 //
|
||||
]
|
||||
]
|
||||
|
||||
],
|
||||
'file' => [
|
||||
'upload' => [
|
||||
'url' => Url::to(['/file/file/upload']),
|
||||
|
BIN
themes/HumHub/ico/notification-o.png
Normal file
BIN
themes/HumHub/ico/notification-o.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 766 B |
BIN
themes/HumHub/ico/notification.png
Normal file
BIN
themes/HumHub/ico/notification.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 604 B |
2815
themes/HumHub/less/theme.css
Normal file
2815
themes/HumHub/less/theme.css
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user