Live Polling + Notification Frontend Rewrite.

This commit is contained in:
buddh4 2017-01-25 16:16:35 +01:00
parent 56905f80cf
commit 4da79ebe4f
39 changed files with 3542 additions and 480 deletions

View File

@ -336,7 +336,7 @@ humhub.module('action', function(module, require, $) {
this.directHandler.apply($trigger, _getArgs(event));
return;
}
// Check for a component action handler
if(Component.handleAction(event)) {
return;
@ -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;
}

View File

@ -17,8 +17,11 @@ humhub.module('client', function (module, require, $) {
//Textstatus = "timeout", "error", "abort", "parsererror", "application"
this.textStatus = textStatus;
this.dataType = dataType;
var responseType = xhr.getResponseHeader('content-type');
if (!dataType || dataType === 'json') {
// 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;

View File

@ -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();
}

View File

@ -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
});
});

View File

@ -11,18 +11,21 @@ humhub.module('ui.status', function (module, require, $) {
var object = util.object;
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,47 +184,51 @@ humhub.module('ui.status', function (module, require, $) {
});
};
var init = function () {
module.statusBar = new StatusBar();
event.on('humhub:ready', function() {
module.log.debug('Current ui state', state);
}).on('humhub:modules:log:setStatus', function (evt, msg, details, level) {
switch (level) {
case log.TRACE_ERROR:
case log.TRACE_FATAL:
module.statusBar.error(msg, details);
break;
case log.TRACE_WARN:
module.statusBar.warn(msg, details);
break;
case log.TRACE_SUCCESS:
module.statusBar.success(msg);
break;
default:
module.statusBar.info(msg);
break;
}
});
var init = function ($pjax) {
title = document.title;
if (!$pjax) {
module.statusBar = new StatusBar();
// The initState can be used to append status messages before the module is initialized
if (module.initState) {
module.statusBar[module.initState[0]].apply(module.statusBar, module.initState[1]);
module.initState = null;
event.on('humhub:ready', function () {
module.log.debug('Current ui state', state);
}).on('humhub:modules:log:setStatus', function (evt, msg, details, level) {
switch (level) {
case log.TRACE_ERROR:
case log.TRACE_FATAL:
module.statusBar.error(msg, details);
break;
case log.TRACE_WARN:
module.statusBar.warn(msg, details);
break;
case log.TRACE_SUCCESS:
module.statusBar.success(msg);
break;
default:
module.statusBar.info(msg);
break;
}
});
// The initState can be used to append status messages before the module is initialized
if (module.initState) {
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,

View File

@ -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;

View File

@ -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',

View File

@ -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',

View File

@ -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'));

View File

@ -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

View File

@ -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
@ -53,7 +62,7 @@ class Module extends \humhub\components\Module
public function getLegitimateContentContainerIds(User $user, $cached = true)
{
$legitimation = Yii::$app->cache->get(self::$legitimateCachePrefix . $user->id);
if ($legitimation === false) {
$legitimation = [
Content::VISIBILITY_PUBLIC => [],
@ -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;
}
}

View 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'
];
}

View 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
});
});

View File

@ -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
});
});

View File

@ -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;
}

View 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\notification\assets;
use yii\web\AssetBundle;
class NotificationAsset extends AssetBundle
{
public $sourcePath = '@notification/resources';
public $css = [];
public $js = [
'js/humhub.notification.js'
];
}

View File

@ -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);
}
@ -246,7 +248,37 @@ class NotificationManager
['not in', 'object_id', $spaceIds]
]);
}
/**
* 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);

View File

@ -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()
]));
}

View File

@ -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);
$output = "";
$notifications = $query->all();
$notifications = Notification::loadMore(Yii::$app->request->get('from', 0));
$lastEntryId = 0;
$output = "";
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]);
$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);
$unnotified = Notification::findUnnotifiedInFrontend()->all();
$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();

View File

@ -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()]);

View File

@ -23,6 +23,11 @@ class NewNotification extends LiveEvent
* @var int the id of the new notification
*/
public $notificationId;
/**
* @var string text representation used for frotnend desktop notifications
*/
public $text;
/**
* @inheritdoc

View File

@ -27,7 +27,7 @@ class Notification extends \humhub\components\ActiveRecord
* @var int number of found grouped notifications
*/
public $group_count;
/**
* @inheritdoc
*/
@ -51,9 +51,9 @@ class Notification extends \humhub\components\ActiveRecord
if ($this->seen === null) {
$this->seen = 0;
}
// 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;
}
}
@ -77,7 +77,7 @@ class Notification extends \humhub\components\ActiveRecord
[['class', 'source_class'], 'string', 'max' => 100]
];
}
/**
* Use getBaseModel instead.
* @deprecated since version 1.2
@ -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]);
}
}

View File

@ -20,33 +20,42 @@ class NotificationSettings extends \yii\base\Model
* @var array
*/
public $settings = [];
/**
* @var string[] Holds the selected spaces for receiving content created notifications.
*/
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[]
*/
protected $_targets;
/**
* @inerhitdoc
*/
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,27 +66,34 @@ class NotificationSettings extends \yii\base\Model
public function rules()
{
return [
['desktopNotifications', 'integer'],
[['settings', 'spaceGuids'], 'safe']
];
}
/**
* @inerhitdoc
*/
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
];
}
/**
* Checks if this form has already been saved before.
* @return boolean
*/
public function isUserSettingLoaded()
{
if($this->user) {
if ($this->user) {
return $this->getSettings()->get('notification.like_email') !== null;
}
return false;
@ -132,10 +148,11 @@ 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();
// Save all active settings
foreach ($this->settings as $settingKey => $value) {
$settings->set($settingKey, $value);
@ -146,12 +163,12 @@ class NotificationSettings extends \yii\base\Model
if (!$target->isEditable($this->user)) {
continue;
}
foreach ($this->categories() as $category) {
if ($category->isFixedSetting($target)) {
continue;
}
$settingKey = $target->getSettingKey($category);
if (!array_key_exists($settingKey, $this->settings)) {
$settings->set($settingKey, false);
@ -161,7 +178,7 @@ class NotificationSettings extends \yii\base\Model
return true;
}
/**
* Saves the Notificaton Space settings for the given user.
* This is skipped if no user is selected (global settings).
@ -175,16 +192,16 @@ 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()
{
$module = Yii::$app->getModule('notification');
@ -201,20 +218,21 @@ class NotificationSettings extends \yii\base\Model
return Yii::$app->user->id == $this->user->id;
}
}
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;
}
}

View File

@ -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
});
});

View File

@ -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],
];

View File

@ -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));
}
}

View File

@ -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
{
}

View File

@ -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>

View File

@ -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">

View File

@ -28,10 +28,9 @@ class Overview extends \yii\base\Widget
return;
}
return $this->render('overview', array(
'update' => \humhub\modules\notification\controllers\ListController::getUpdates(),
'updateInterval' => Yii::$app->getModule('notification')->pollClientUpdateInterval
));
return $this->render('overview', [
'update' => \humhub\modules\notification\controllers\ListController::getUpdates(),
'unseenCount' => \humhub\modules\notification\models\Notification::findUnseen()->count()]);
}
}

View File

@ -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,

View File

@ -1,207 +1,43 @@
<?php
<?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>
</div>

View File

@ -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']

View File

@ -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

View File

@ -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,

View File

@ -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']),

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 604 B

2815
themes/HumHub/less/theme.css Normal file

File diff suppressed because it is too large Load Diff