Initial Notification Rewrite + Markdown posts (experimental).

This commit is contained in:
buddha87 2017-01-04 14:53:25 +01:00
parent 52d19d5c1d
commit 4d5bc8708e
156 changed files with 10417 additions and 782 deletions

View File

@ -51,7 +51,8 @@
"yiisoft/yii2-debug": "~2.0.0",
"yiisoft/yii2-gii": "~2.0.0",
"yiisoft/yii2-faker": "~2.0.0",
"yiisoft/yii2-apidoc": "~2.0.0"
"yiisoft/yii2-apidoc": "~2.0.0",
"googlecode/google-api-php-client": "0.6.*"
},
"config": {
"process-timeout": 1800,

View File

@ -30,7 +30,12 @@ humhub.module('client', function(module, require, $) {
};
Response.prototype.setError = function(errorThrown) {
this.error = errorThrown;
try {
this.error = JSON.parse(this.response);
} catch(e) {/* Nothing todo... */}
this.error = this.error || {};
this.errorThrown = errorThrown;
this.validationError = (this.status === 400);
return this;
};

View File

@ -95,7 +95,10 @@ humhub.module('log', function (module, require, $) {
if(msg instanceof Error && level >= TRACE_WARN) {
details = msg;
msg = this.getMessage(details.message, level, true);
} else if(msg.status && level >= TRACE_WARN) {
} else if(msg.error && msg.error.message) { // client.Response
details = msg;
msg = msg.error.message;
} else if(msg.status && level >= TRACE_WARN) { // client.Response.status
details = msg;
msg = this.getMessage(msg.status, level, true);
} else if(object.isString(msg) || object.isNumber(msg)) {

View File

@ -10,6 +10,8 @@ humhub.module('ui.additions', function(module, require, $) {
var event = require('event');
var object = require('util.object');
var richtext = require('ui.richtext', true);
var _additions = {};
/**
@ -23,7 +25,7 @@ humhub.module('ui.additions', function(module, require, $) {
*/
var register = function(id, selector, handler, options) {
options = options || {};
if(!_additions[id] || options.overwrite) {
_additions[id] = {
'selector': selector,
@ -46,8 +48,8 @@ humhub.module('ui.additions', function(module, require, $) {
* @returns {undefined}
*/
var applyTo = function(element, options) {
options = options || {};
options = options || {};
var $element = (element instanceof $) ? element : $(element);
$.each(_additions, function(id) {
if(options.filter && !options.filter.indexOf(id)) {
@ -106,6 +108,38 @@ humhub.module('ui.additions', function(module, require, $) {
});
});
module.register('markdown', '[data-ui-markdown]', function($match) {
var converter = new Markdown.Converter();
Markdown.Extra.init(converter);
$match.each(function() {
var $this = $(this);
if($this.data('markdownProcessed')) {
return;
}
// Export all richtext features
var features = {};
$this.find('[data-richtext-feature]').each(function() {
var $this = $(this);
features[$this.data('guid')] = $this.clone();
$this.replaceWith($this.data('guid'));
});
var text = richtext.Richtext.plainText($this.clone());
var result = converter.makeHtml(text);
// Rewrite richtext feature
$.each(features, function(guid, $element) {
result = result.replace(guid.trim(), $('<div></div>').html($element).html());
});
$this.html(result).data('markdownProcessed', true);
});
});
$(document).on('click.humhub-ui-tooltip', function() {
$('.tooltip').remove();
});
@ -117,8 +151,8 @@ humhub.module('ui.additions', function(module, require, $) {
// Activate placeholder text for older browsers (specially IE)
/*this.register('placeholder','input, textarea', function($match) {
$match.placeholder();
});*/
$match.placeholder();
});*/
// Replace the standard checkbox and radio buttons
module.register('forms', ':checkbox, :radio', function($match) {
@ -130,10 +164,10 @@ humhub.module('ui.additions', function(module, require, $) {
$match.loader();
});
};
var extend = function(id, handler, options) {
options = options || {};
if(_additions[id]) {
var addition = _additions[id];
if(options.prepend) {
@ -141,21 +175,21 @@ humhub.module('ui.additions', function(module, require, $) {
} else {
addition.handler = object.chain(addition.handler, addition.handler, handler);
}
if(options.selector && options.selector !== addition.selector) {
addition.selector += ','+options.selector;
addition.selector += ',' + options.selector;
}
if(options.applyOnInit) {
module.apply('body', id);
}
} else if(options.selector){
} else if(options.selector) {
options.extend = false; // Make sure we don't get caught in a loop somehow.
module.register(id, options.selector, handler, options);
}
};
//TODO: additions.extend('id', handler); for extending existing additions.
/**
@ -201,7 +235,7 @@ humhub.module('ui.additions', function(module, require, $) {
} else if(!options) {
options = {};
}
node = (node instanceof $) ? node[0] : node;
var observer = new MutationObserver(function(mutations) {

View File

@ -114,7 +114,7 @@ humhub.module('ui.richtext', function(module, require, $) {
sel.addRange(range);
}
};
/**
* Empty spans prevent text deletions in some browsers, so we have to get sure there are no empty spans present.
* @param {type} $node
@ -259,17 +259,17 @@ humhub.module('ui.richtext', function(module, require, $) {
tooltip = tooltip || this.options.disabledText;
this.$.removeAttr('contenteditable').attr({
disabled: 'disabled',
title : tooltip,
title: tooltip,
}).tooltip({
placement : 'bottom'
placement: 'bottom'
});
};
Richtext.prototype.clear = function() {
this.$.html('');
this.checkPlaceholder();
};
Richtext.prototype.focus = function() {
this.$.trigger('focus');
};
@ -294,7 +294,17 @@ humhub.module('ui.richtext', function(module, require, $) {
}
});
var html = $clone.html();
return Richtext.plainText($clone);
};
Richtext.plainText = function(element, options) {
options = options || {};
var $element = element instanceof $ ? element : $(element);
var html = $element.html();
// remove all line breaks
html = html.replace(/(?:\r\n|\r|\n)/g, "");
// replace html space
html = html.replace(/\&nbsp;/g, ' ');
@ -309,18 +319,17 @@ humhub.module('ui.richtext', function(module, require, $) {
html = html.replace(/\<p>\<br\s*\\*>\<\/p>/g, '<br>');
html = html.replace(/\<\/p>/g, '<br>');
// remove all line breaks
html = html.replace(/(?:\r\n|\r|\n)/g, "");
// At.js adds a zwj at the end of each mentioning
html = html.replace(/\u200d/g,'');
html = html.replace(/\u200d/g, '');
// replace all <br> with new line break
$clone.html(html.replace(/\<br\s*\>/g, '\n'));
html = html.replace(/\<br\s*\>/g, '\n');
// return plain text without html tags
var $clone = (options.clone) ? $element.clone() : $element;
$clone.html(html);
return $clone.text().trim();
};
}
Richtext.features = {};

View File

@ -110,8 +110,10 @@
// assign label to checkbox
$this.parent().attr('for', $this.attr('id'));
var $checkbox = $('<div class="regular-checkbox-box"></div>').attr('style', $this.attr('style'));
// add new checkbox element
$this.parent().append('<div class="regular-checkbox-box"></div>');
$this.parent().append($checkbox);
}
}

View File

@ -1,78 +1,323 @@
// Markdown
.markdown-render {
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: bold !important;
}
h1 {
font-size: 28px !important;
}
h2 {
font-size: 24px !important;
}
h3 {
font-size: 18px !important;
}
h4 {
font-size: 16px !important;
}
h5 {
font-size: 14px !important;
}
h6 {
color: #999;
font-size: 14px !important;
}
pre {
padding: 0;
border: none;
border-radius: 3px;
code {
padding: 10px;
border-radius: 3px;
font-size: 12px !important;
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: bold !important;
}
}
a,
a:visited {
background-color: inherit;
text-decoration: none;
color: @info !important;
}
img {
max-width: 100%;
display: table-cell !important;
}
table {
width: 100%;
th {
font-size: 13px;
font-weight: 700;
color: @font3;
h1 {
font-size: 28px !important;
}
h2 {
font-size: 24px !important;
}
h3 {
font-size: 18px !important;
}
h4 {
font-size: 16px !important;
}
h5 {
font-size: 14px !important;
}
h6 {
color: #999;
font-size: 14px !important;
}
pre {
padding: 0;
border: none;
border-radius: 3px;
code {
padding: 10px;
border-radius: 3px;
font-size: 12px !important;
}
}
a,
a:visited {
background-color: inherit;
text-decoration: none;
color: @info !important;
}
img {
max-width: 100%;
display: table-cell !important;
}
thead {
tr {
border-bottom: 1px solid @background3;
}
}
table {
width: 100%;
th {
font-size: 13px;
font-weight: 700;
color: @font3;
}
tbody tr td, thead tr th {
border: 1px solid @background3 !important;
padding: 4px;
thead {
tr {
border-bottom: 1px solid @background3;
}
}
tbody tr td, thead tr th {
border: 1px solid @background3 !important;
padding: 4px;
}
}
}
}
.md-editor.active {
border: 2px solid @info !important;
border: 2px solid @info !important;
}
.md-editor textarea {
padding: 10px !important;
}
padding: 10px !important;
}
[data-ui-markdown] {
word-break: break-all;
h1, h2, h3, h4, h5, h6 {
margin: 0 0 1em 0;
text-align: start;
}
h1 {
font-size: 2.6em !important;
}
h2 {
font-size: 2.15em !important;
}
h3 {
font-size: 1.7em !important;
}
h4 {
font-size: 1.25em !important;
}
h5 {
font-size: 1em !important;
}
h6 {
font-size: .85em !important;
}
p, pre, blockquote {
margin: 0 0 1.1em;
}
blockquote {
border-left-width: 10px;
background-color: rgba(128,128,128,0.05);
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
padding: 15px 20px;
font-size: 1em;
border-left: 5px solid #888888;
}
table {
margin-bottom: 20px;
max-width: 100%;
background-color: transparent;
border-collapse: collapse;
border-spacing: 0px;
}
table caption+thead tr:first-child th,
table caption+thead tr:first-child td,
table colgroup+thead tr:first-child th,
table colgroup+thead tr:first-child td,
table thead:first-child tr:first-child th,
table thead:first-child tr:first-child td {
border-top: 0px;
}
table thead th {
vertical-align: bottom;
}
table th {
font-weight: bold;
text-align: left;
}
table th, table td {
padding: 8px;
line-height: 20px;
vertical-align: top;
border-top: 1px solid #ddd;
}
dt, dd {
margin-top: 5px;
margin-bottom: 5px;
line-height: 1.45;
}
dt {
font-weight: bold;
}
dd {
margin-left: 40px;
}
pre {
text-align: start;
border: 0;
padding: 10px 20px;
border-radius: 5px;
code {
white-space: pre !important;
}
}
blockquote ul:last-child, blockquote ol:last-child {
margin-bottom: 0px;
}
ul, ol {
margin-top: 0;
margin-bottom: 10.5px;
}
.footnote {
vertical-align: top;
position: relative;
top: -0.5em;
font-size: .8em;
}
}
blockquote {
border-left: 2px dotted #888;
padding-left: 5px;
background: #d0f0ff;
}
.wmd-panel
{
//margin-left: 25%;
//margin-right: 25%;
//width: 50%;
min-width: 500px;
}
.wmd-button-bar
{
width: 100%;
background-color: Silver;
}
.wmd-input
{
height: 300px;
width: 100%;
background-color: Gainsboro;
border: 1px solid DarkGray;
}
.wmd-preview
{
//background-color: #c0e0ff;
}
.wmd-button-row
{
position: relative;
margin-left: 5px;
margin-right: 5px;
margin-bottom: 5px;
margin-top: 10px;
padding: 0px;
height: 20px;
}
.wmd-spacer
{
width: 1px;
height: 20px;
margin-left: 14px;
position: absolute;
background-color: Silver;
display: inline-block;
list-style: none;
}
.wmd-button {
width: 20px;
height: 20px;
padding-left: 2px;
padding-right: 3px;
position: absolute;
display: inline-block;
list-style: none;
cursor: pointer;
}
.wmd-button > span {
background-image: url(../img/wmd-buttons.png);
background-repeat: no-repeat;
background-position: 0px 0px;
width: 20px;
height: 20px;
display: inline-block;
}
.wmd-spacer1
{
left: 50px;
}
.wmd-spacer2
{
left: 175px;
}
.wmd-spacer3
{
left: 300px;
}
.wmd-prompt-background
{
background-color: Black;
}
.wmd-prompt-dialog
{
border: 1px solid #999999;
background-color: #F5F5F5;
}
.wmd-prompt-dialog > div {
font-size: 0.8em;
font-family: arial, helvetica, sans-serif;
}
.wmd-prompt-dialog > form > input[type="text"] {
border: 1px solid #999999;
color: black;
}
.wmd-prompt-dialog > form > input[type="button"]{
border: 1px solid #888888;
font-family: trebuchet MS, helvetica, sans-serif;
font-size: 0.8em;
font-weight: bold;
}

View File

@ -5,7 +5,7 @@ return [
'modules' => [
'debug' => [
'class' => 'yii\debug\Module',
//'allowedIPs' => ['*'],
'allowedIPs' => ['*'],
],
],
];

View File

@ -92,6 +92,7 @@ class AppAsset extends AssetBundle
'humhub\assets\NProgressAsset',
'humhub\assets\IE9FixesAsset',
'humhub\assets\IEFixesAsset',
'humhub\assets\PagedownConverterAsset',
];
}

View File

@ -0,0 +1,40 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\assets;
use yii\web\AssetBundle;
/**
* jquery-highlight
*
* @author buddha
*/
class PagedownConverterAsset extends AssetBundle
{
/**
* @inheritdoc
*/
public $basePath = '@webroot';
/**
* @inheritdoc
*/
public $baseUrl = '@web';
/**
* @inheritdoc
*/
public $js = [
'resources/js/pagedown/Markdown.Converter.js',
'resources/js/pagedown/Markdown.Editor.js',
'resources/js/pagedown/Markdown.Extra.js',
];
}

View File

@ -327,5 +327,10 @@ class Module extends \yii\base\Module
{
return [];
}
public function hasNotifications()
{
return !empty($this->getNotifications());
}
}

View File

@ -48,21 +48,27 @@ class SettingsManager extends BaseSettingsManager
}
/**
* Returns ContentContainerSettingsManager for current logged in user
* Returns ContentContainerSettingsManager for the given $user or current logged in user
* @return ContentContainerSettingsManager
*/
public function user()
public function user($user = null)
{
return $this->contentContainer(Yii::$app->user->getIdentity());
if(!$user) {
$user = Yii::$app->user->getIdentity();
}
return $this->contentContainer($user);
}
/**
* Returns ContentContainerSettingsManager for current logged in user
* Returns ContentContainerSettingsManager for the given $space or current controller space
* @return ContentContainerSettingsManager
*/
public function space()
public function space($space = null)
{
if (Yii::$app->controller instanceof \humhub\modules\content\components\ContentContainerController) {
if($space != null) {
return $this->contentContainer($space);
} elseif (Yii::$app->controller instanceof \humhub\modules\content\components\ContentContainerController) {
if (Yii::$app->controller->contentContainer instanceof \humhub\modules\space\models\Space) {
return $this->contentContainer(Yii::$app->controller->contentContainer);
}

View File

@ -8,25 +8,27 @@
namespace humhub\components;
use humhub\modules\notification\models\Notification;
use humhub\modules\content\components\ContentActiveRecord;
use Yii;
use yii\helpers\Html;
use humhub\modules\content\components\ContentContainerActiveRecord;
use humhub\modules\content\components\ContentAddonActiveRecord;
use humhub\libs\Viewable;
use humhub\modules\space\models\Space;
use humhub\modules\content\interfaces\ContentOwner;
use humhub\widgets\RichText;
/**
* Name (SocialEvent/NetworkEvent/SocialActivity/BaseEvent)
*
* This class represents an social activity triggered within the network.
* An activity instance can be linked to an $originator user, which performed the activity.
* This class represents a social Activity triggered within the network.
*
* The activity mainly provides functions for rendering the output for different channels as
* web, mail or plain-text.
* A SocialActivity can be assigned with an originator User, which triggered the activity and a source ActiveRecord.
* The source is used to connect the SocialActivity to a related Content, ContentContainerActiveRecord or any other
* ActiveRecord.
*
* Since SocialActivities need to be rendered in most cases it implements the humhub\components\rendering\Viewable interface and provides
* a default implementation of the getViewParams function.
*
* @since 1.1
* @author buddha
*/
abstract class SocialActivity extends Viewable
abstract class SocialActivity extends \yii\base\Object implements rendering\Viewable
{
/**
@ -43,42 +45,168 @@ abstract class SocialActivity extends Viewable
*/
public $source;
/**
* The content container this activity belongs to.
*
* If the source object is a type of Content/ContentAddon or ContentContainer the container
* will be automatically set.
*
* @var ContentContainerActiveRecord
*/
public $container = null;
/**
* @var string the module id which this activity belongs to (required)
*/
public $moduleId = "";
/**
* The notification record this notification belongs to
*
* @var Notification
* An SocialActivity can be represented in the database as ActiveRecord.
* By defining the $recordClass an ActiveRecord will be created automatically within the
* init function.
*
* @var \yii\db\ActiveRecord The related record for this activitiy
*/
public $record;
/**
* @var string Record class used for instantiation.
*/
public $recordClass;
/**
* @var string view name used for rendering the activity
*/
public $viewName = 'default.php';
public function init()
{
parent::init();
if($this->recordClass) {
$this->record = Yii::createObject($this->recordClass, [
'class' => $this->className()
]);
}
}
/**
* Static initializer should be prefered over new initialization, since it makes use
* of Yii::createObject dependency injection/configuration.
*
* @return \humhub\components\SocialActivity
*/
public static function instance($options = [])
{
return Yii::createObject(static::class, $options);
}
/**
* Builder function for the originator.
*
* @param type $originator
* @return \humhub\components\SocialActivity
*/
public function from($originator)
{
$this->originator = $originator;
return $this;
}
/**
* Builder function for the source.
* @param type $source
* @return \humhub\components\SocialActivity
*/
public function about($source)
{
$this->source = $source;
$this->record->source_pk = $this->source->getPrimaryKey();
$this->record->source_class = $this->source->className();
return $this;
}
/**
* @inheritdoc
*/
public function getViewName()
{
// If no suffix is given, we assume a php file.
if(!strpos($this->viewName, '.')) {
return $this->viewName. '.php';
} else {
return $this->viewName;
}
}
/**
* @inheritdoc
*/
protected function getViewParams($params = [])
public function getViewParams($params = [])
{
$params['originator'] = $this->originator;
$params['source'] = $this->source;
$params['contentContainer'] = $this->container;
$params['record'] = $this->record;
if (!isset($params['url'])) {
$params['url'] = $this->getUrl();
$result = [
'originator' => $this->originator,
'source' => $this->source,
'contentContainer' => $this->getContentContainer(),
'space' => $this->getSpace(),
'record' => $this->record,
'url' => $this->getUrl(),
'viewable' => $this,
'html' => $this->html(),
'text' => $this->text()
];
return \yii\helpers\ArrayHelper::merge($result, $params);
}
/**
* Returns the related content instance in case the source is of type ContentOwner.
*
* @return \humhub\modules\content\models\Content Content ActiveRecord or null if not related to a ContentOwner source
*/
public function getContent()
{
if ($this->hasContent()) {
return $this->source->content;
}
return $params;
return null;
}
/**
* @return Space related space instance in case the activity source is an related contentcontainer of type space, otherwise null
*/
public function getSpace()
{
$container = $this->getContentContainer();
return ($container instanceof Space) ? $container : null;
}
/**
* @return integer related space id in case the activity source is an related contentcontainer of type space, otherwise null
*/
public function getSpaceId()
{
$space = $this->getSpace();
return ($space) ? $space->id : null;
}
/**
* Determines if this activity is related to a content. This is the case if the activitiy source
* is of type ContentOwner.
*
* @return boolean true if this activity is related to a ContentOwner else false
*/
public function hasContent()
{
return $this->source instanceof ContentOwner;
}
/**
* Determines if the activity source is related to an ContentContainer.
* This is the case if the source is either a ContentContainerActiveRecord itself or a ContentOwner.
*
* @return ContentContainerActiveRecord
*/
public function getContentContainer()
{
if ($this->source instanceof ContentContainerActiveRecord) {
return $this->source;
} else if ($this->hasContent()) {
return $this->getContent()->getContainer();
}
return null;
}
/**
@ -91,8 +219,8 @@ abstract class SocialActivity extends Viewable
{
$url = '#';
if ($this->source instanceof ContentActiveRecord || $this->source instanceof ContentAddonActiveRecord) {
$url = $this->source->content->getUrl();
if ($this->hasContent()) {
$url = $this->getContent()->getUrl();
} elseif ($this->source instanceof ContentContainerActiveRecord) {
$url = $this->source->getUrl();
}
@ -105,6 +233,55 @@ abstract class SocialActivity extends Viewable
return $url;
}
/**
* @inheritdoc
*/
public function text()
{
$html = $this->html();
return !empty($html) ? strip_tags($html) : null;
}
/**
* @inheritdoc
*/
public function html()
{
return null;
}
/**
* @inheritdoc
*/
public function json()
{
return \yii\helpers\Json::encode($this->asArray());
}
/**
* Returns an array representation of this notification.
*/
public function asArray()
{
$result = [
'class' => $this->className(),
'text' => $this->text(),
'html' => $this->html()
];
if($this->originator) {
$result['originator_id'] = $this->originator->id;
}
if ($this->source) {
$result['source_class'] = $this->source->className();
$result['source_pk'] = $this->source->getPrimaryKey();
$result['space_id'] = $this->source->getSpaceId();
}
return $result;
}
/**
* Build info text about a content
*
@ -114,11 +291,11 @@ abstract class SocialActivity extends Viewable
* @param Content $content
* @return string
*/
public function getContentInfo(\humhub\modules\content\interfaces\ContentTitlePreview $content)
public function getContentInfo(ContentOwner $content)
{
return \yii\helpers\Html::encode($content->getContentName()) .
return Html::encode($content->getContentName()) .
' "' .
\humhub\widgets\RichText::widget(['text' => $content->getContentDescription(), 'minimal' => true, 'maxLength' => 60]) . '"';
RichText::widget(['text' => $content->getContentDescription(), 'minimal' => true, 'maxLength' => 60]) . '"';
}
}

View File

@ -0,0 +1,59 @@
<?php
namespace humhub\components\rendering;
use Yii;
/**
* A LayoutRenderer subclass can be used to render layout based views by setting the $viewPath and $layout properties.
*
* The $viewPath defines the path where the target view file resides.
* For a viewable with the viewName 'myView.php' the renderer will render the view:
*
* '<viewPath>/myView.php'
*
* where viewPath can also be provided as a Yii alias.
*
* The rendered view will be embeded into the given $layout which should point to the layout file
* and can also be provided as a Yii alias e.g:
*
* '@myModule/views/layouts/myLayout.php'
*
* @author buddha
*/
class LayoutRenderer extends ViewPathRenderer
{
/**
* @var string layout file path
*/
public $layout;
/**
* @inheritdoc
*/
public function render(Viewable $viewable, $params = [])
{
// Render the view itself
$viewParams = $viewable->getViewParams($params);
if(!isset($viewParams['content'])) {
$viewParams['content'] = parent::renderView($viewable, $viewParams);
}
$layout = $this->getLayout($viewable);
// Embed view into layout if provided
if ($layout) {
return Yii::$app->getView()->renderFile($layout, $viewParams, $viewable);
} else {
return $viewParams['content'];
}
}
protected function getLayout(Viewable $viewable)
{
return $this->layout;
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace humhub\components\rendering;
use Yii;
/**
* Description of MailLayoutRenderer
*
* @author buddha
*/
class MailLayoutRenderer extends LayoutRenderer
{
/**
* Layout for text rendering.
* @var type
*/
public $textLayout;
/**
* Used for rendering text mail content, by embedding the rendered view into
* a $textLayout and removing all html elemtns.
*
* @param \humhub\components\rendering\Viewable $viewable
* @return type
*/
public function renderText(Viewable $viewable , $params = [])
{
$textRenderer = new LayoutRenderer([
'layout' => $this->getTextLayout($viewable)
]);
$params['content'] = $viewable->text();
return strip_tags($textRenderer->render($viewable, $params));
}
public function getTextLayout(Viewable $viewable )
{
return $this->textLayout;
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace humhub\components\rendering;
/**
* Renderer interface used by render components to render Viewable instances.
*
* A Renderer implementation is responsible for rendering the viewable either by using it's viewName or
* by converting it's data into a specific format.
*
* @author buddha
*/
interface Renderer
{
/**
* Renders the given $viewable.
*
* The renderer will usually use the $viewable->viewName to determine the target view and
* forward the given $params to $viewable->getViewParams($params). By doing so, the
* $params can be used to overwrite the default view parameter of $viewable.
*
* @param \humhub\components\rendering\Viewable $viewable
* @param type $params
*/
public function render(Viewable $viewable, $params = []);
}

View File

@ -0,0 +1,77 @@
<?php
namespace humhub\components\rendering;
use Yii;
/**
* A ViewPathRender is a simple Renderer implementation for rendering Viewable
* instances by searching for the Viewable viewName within the given $viewPath.
*
* If no $viewPath is given, we'll determine the view path of the viewable as following:
*
* ViewableClassPath/../views
*
* @author buddha
*/
class ViewPathRenderer extends \yii\base\Object implements Renderer
{
/**
* @var string view path
*/
public $viewPath;
/**
* Renders the viewable by searching the viewable's viewName within the given viewPath.
*
* If no viewPath is given this function uses '../views/viewName' as view file path.
*
* @param \humhub\components\rendering\Viewable $viewable
* @return type
*/
public function render(Viewable $viewable, $params = [])
{
return $this->renderView($viewable, $viewable->getViewParams($params));
}
/**
* Helper function for rendering a Viewable with the given viewParams.
*
* @param \humhub\components\rendering\Viewable $viewable
* @param type $viewParams
* @return type
*/
public function renderView(Viewable $viewable, $viewParams)
{
$viewFile = $this->getViewFile($viewable);
return Yii::$app->getView()->renderFile($viewFile, $viewParams, $viewable);
}
/**
* Returnes the viewFile of the given Viewable.
*
* @param \humhub\components\rendering\Viewable $viewable
* @return type
*/
public function getViewFile(Viewable $viewable)
{
return $this->getViewPath($viewable) . '/' . $viewable->getViewName();
}
/**
* Returns the directory containing the view files for this event.
* The default implementation returns the 'views' subdirectory under the directory containing the notification class file.
* @return string the directory containing the view files for this notification.
*/
public function getViewPath(Viewable $viewable)
{
if ($this->viewPath !== null) {
return Yii::getAlias($this->viewPath);
}
$class = new \ReflectionClass($viewable);
return dirname(dirname($class->getFileName())) . DIRECTORY_SEPARATOR . 'views';
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace humhub\components\rendering;
/**
* The Viewable interface is used for Classes that can be rendered by Renderer components.
* A Renderer can make use of the html, json or text represenation when rendering a viewable.
*
* @author buddha
*/
interface Viewable
{
/**
* Returns an array of view parameter, required for rendering.
*
* @param array $params
*/
public function getViewParams($params = []);
/**
* @return string viewname of this viewable
*/
public function getViewName();
/**
* @return string html content representation of this viewable.
*/
public function html();
/**
* @return string json content representation of this viewable.
*/
public function json();
/**
* @return string text content representation of this viewable.
*/
public function text();
}

View File

@ -1,11 +1,4 @@
<?php
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
namespace humhub\components\validators;
use yii\validators\Validator;

View File

@ -1,11 +1,4 @@
<?php
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
namespace humhub\components\validators;
use Yii;

View File

@ -16,6 +16,20 @@ $config = [
'moduleManager' => [
'class' => '\humhub\components\ModuleManager'
],
'notification' => [
'class' => 'humhub\modules\notification\components\NotificationManager',
'targets' => [
[
'class' => 'humhub\modules\notification\components\WebNotificationTarget',
'renderer' => ['class' => 'humhub\modules\notification\components\WebTargetRenderer']
],
[
'class' => 'humhub\modules\notification\components\MailNotificationTarget',
'renderer' => ['class' => 'humhub\modules\notification\components\MailTargetRenderer']
],
//['class' => '\humhub\modules\notification\components\MobileNotificationTarget']
]
],
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [

View File

@ -28,4 +28,5 @@ HumHub Change Log
- Enh: Picker widgets rewrite (UserPicker/SpacePicker/MultiselectDropdown). (buddha)
- Enh: Richtext widget rewrite. (buddha)
- Enh: Removed almost all inline JS blocks. (buddha)
- Enh: StreamAction now uses flexible StreamQuery Model.
- Enh: StreamAction now uses flexible StreamQuery Model. (buddha)
- Enh: Post markdown support. (buddha)

View File

@ -1,158 +0,0 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2016 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\libs;
use Yii;
use yii\base\Component;
use yii\base\ViewContextInterface;
/**
* Viewable provides view rendering support including layout and different
* output formats (e.g. text, web or mail=.
*
* @since 1.1
* @author Luke
*/
abstract class Viewable extends Component implements ViewContextInterface
{
const OUTPUT_WEB = 'web';
const OUTPUT_MAIL = 'mail';
const OUTPUT_MAIL_PLAINTEXT = 'mail_plaintext';
const OUTPUT_TEXT = 'text';
/**
* Name of the view, used for rendering the event
*
* @var string
*/
public $viewName = null;
/**
* View path
*
* @var string
*/
public $viewPath = null;
/**
* Layout file for web version
*
* @var string
*/
protected $layoutWeb;
/**
* Layout file for mail version
*
* @var string
*/
protected $layoutMail;
/**
* Layout file for mail plaintext version
*
* @var string
*/
protected $layoutMailPlaintext;
/**
* Assambles all parameter required for rendering the view.
*
* @return array all view parameter
*/
protected function getViewParams($params = [])
{
$params['originator'] = $this->originator;
$params['source'] = $this->source;
$params['contentContainer'] = $this->container;
$params['record'] = $this->record;
$params['url'] = $this->getUrl();
return $params;
}
/**
* Renders the notification
*
* @return string
*/
public function render($mode = self::OUTPUT_WEB, $params = [])
{
$viewFile = $this->getViewFile($mode);
$viewParams = $this->getViewParams($params);
$result = Yii::$app->getView()->renderFile($viewFile, $viewParams, $this);
if ($mode == self::OUTPUT_TEXT) {
return strip_tags($result);
}
$viewParams['content'] = $result;
return Yii::$app->getView()->renderFile($this->getLayoutFile($mode), $viewParams, $this);
}
/**
* Returns the correct view file
*
* @param string $mode the output mode
* @return string the view file
*/
protected function getViewFile($mode)
{
$viewFile = $this->getViewPath() . '/' . $this->viewName . '.php';
$alternativeViewFile = "";
// Lookup alternative view file based on view mode
if ($mode == self::OUTPUT_MAIL) {
$alternativeViewFile = $this->getViewPath() . '/mail/' . $this->viewName . '.php';
} elseif ($mode === self::OUTPUT_MAIL_PLAINTEXT) {
$alternativeViewFile = $this->getViewPath() . '/mail/plaintext/' . $this->viewName . '.php';
}
if ($alternativeViewFile != "" && file_exists($alternativeViewFile)) {
$viewFile = $alternativeViewFile;
}
return $viewFile;
}
/**
* Returns the layout file
*
* @param string $mode the output mode
* @return string the layout file
*/
protected function getLayoutFile($mode)
{
if ($mode == self::OUTPUT_MAIL_PLAINTEXT) {
return $this->layoutMailPlaintext;
} elseif ($mode == self::OUTPUT_MAIL) {
return $this->layoutMail;
}
return $this->layoutWeb;
}
/**
* Returns the directory containing the view files for this event.
* The default implementation returns the 'views' subdirectory under the directory containing the notification class file.
* @return string the directory containing the view files for this notification.
*/
public function getViewPath()
{
if ($this->viewPath !== null) {
return Yii::getAlias($this->viewPath);
}
$class = new \ReflectionClass($this);
return dirname($class->getFileName()) . DIRECTORY_SEPARATOR . 'views';
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace humhub\modules\activity\components;
use humhub\components\rendering\MailLayoutRenderer;
/**
* Description of ActivityMailRenderer
*
* @author buddha
*/
class ActivityMailRenderer extends MailLayoutRenderer
{
/**
* @var string layout file path
*/
public $layout = '@activity/views/layouts/mail.php';
/**
* @var string text layout file
*/
public $textLayout = "@activity/views/layouts/mail_plaintext.php";
}

View File

@ -11,7 +11,7 @@ namespace humhub\modules\activity\components;
use humhub\modules\activity\models\Activity;
use humhub\modules\content\components\ContentActiveRecord;
use humhub\modules\content\components\ContentAddonActiveRecord;
use humhub\modules\content\components\ContentContainerActiveRecord;
use humhub\modules\content\models\Content;
/**
* BaseActivity is the base class for all activities.
@ -22,9 +22,10 @@ abstract class BaseActivity extends \humhub\components\SocialActivity
{
/**
* Default content visibility of this Activity.
* @var int
*/
public $visibility = 1;
public $visibility = Content::VISIBILITY_PRIVATE;
/**
* @inheritdoc
@ -40,6 +41,11 @@ abstract class BaseActivity extends \humhub\components\SocialActivity
* @inheritdoc
*/
public $layoutMailPlaintext = "@humhub/modules/notification/views/layouts/mail_plaintext.php";
/**
* @inheritdoc
*/
public $recordClass = Activity::class;
/**
* @var boolean
@ -55,12 +61,13 @@ abstract class BaseActivity extends \humhub\components\SocialActivity
throw new \yii\base\InvalidConfigException("Missing viewName!");
}
if ($this->visibility === null) {
$this->visibility = \humhub\modules\content\models\Content::VISIBILITY_PRIVATE;
}
parent::init();
}
public function render($params = array())
{
}
/**
* @inheritdoc
@ -68,9 +75,13 @@ abstract class BaseActivity extends \humhub\components\SocialActivity
public function getViewParams($params = [])
{
$params['clickable'] = $this->clickable;
return parent::getViewParams($params);
}
public function save()
{
return $this->record->save();
}
/**
* Creates an activity model and determines the contentContainer/visibility
@ -87,40 +98,51 @@ abstract class BaseActivity extends \humhub\components\SocialActivity
throw new \yii\base\InvalidConfigException("Invalid source object given!");
}
if ($this->container == null) {
$this->container = $this->getContentContainerFromSource();
if ($this->container == null) {
throw new \yii\base\InvalidConfigException("Could not determine content container for activity!");
}
}
$this->saveModelInstance();
}
protected function getContentContainerFromSource()
/**
* @inheritdoc
*/
public function from($originator)
{
if ($this->hasContentSource()) {
return $this->source->content->container;
} elseif ($this->source instanceof ContentContainerActiveRecord) {
return $this->source;
}
parent::from($originator);
$this->record->content->created_by = $originator->id;
return $this;
}
/**
* @inheritdoc
*/
public function about($source)
{
parent::from($source);
$this->record->content->visibility = $this->getContentVisibility();
return $this;
}
protected function hasContentSource()
/**
* Builder function for setting ContentContainerActiveRecord
* @param \humhub\modules\content\components\ContentContainerActiveRecord $container
*/
public function container($container)
{
return $this->source instanceof ContentActiveRecord || $this->source instanceof ContentAddonActiveRecord;
$this->record->content->container = $container;
return $this;
}
private function saveModelInstance()
{
$model = new Activity();
$model->class = $this->className();
$model->module = $this->moduleId;
$model->object_model = $this->source->className();
$model->object_id = $this->source->getPrimaryKey();
$model->content->container = $this->container;
$model->content->visibility = $this->getContentVisibility();
$this->record->source_class = $this->source->className();
$this->record->source_pk = $this->source->getPrimaryKey();
$this->record->content->visibility = $this->getContentVisibility();
if (!$this->content->container) {
$model->content->container = $this->getContentContainer();
}
$model->content->created_by = $this->getOriginatorId();
if ($model->content->created_by == null) {
@ -134,7 +156,7 @@ abstract class BaseActivity extends \humhub\components\SocialActivity
protected function getContentVisibility()
{
return $this->hasContentSource() ? $this->source->content->visibility : $this->visibility;
return $this->hasContent() ? $this->getContent()->visibility : $this->visibility;
}
protected function getOriginatorId()

View File

@ -0,0 +1,30 @@
<?php
use yii\db\Migration;
class m161228_131023_rename_source_fields extends Migration
{
public function up()
{
$this->renameColumn('activity', 'object_model', 'source_class');
$this->renameColumn('activity', 'object_id', 'source_pk');
}
public function down()
{
echo "m161228_131023_rename_source_fields cannot be reverted.\n";
return false;
}
/*
// Use safeUp/safeDown to run migration code within a transaction
public function safeUp()
{
}
public function safeDown()
{
}
*/
}

View File

@ -36,6 +36,8 @@ class Activity extends ContentActiveRecord
return [
[
'class' => \humhub\components\behaviors\PolymorphicRelation::className(),
'classAttribute' => 'source_class',
'pkAttribute' => 'source_pk',
'mustBeInstanceOf' => [
\yii\db\ActiveRecord::className(),
]
@ -57,9 +59,9 @@ class Activity extends ContentActiveRecord
public function rules()
{
return [
[['object_id'], 'integer'],
[['source_pk'], 'integer'],
[['class'], 'string', 'max' => 100],
[['module', 'object_model'], 'string', 'max' => 100]
[['module', 'source_class'], 'string', 'max' => 100]
];
}

View File

@ -80,7 +80,7 @@ class Module extends \humhub\components\Module
{
if(Yii::$app->user->isAdmin()) {
return [
'humhub\modules\user\notifications\NewVersionAvailable'
'humhub\modules\admin\notifications\NewVersionAvailable'
];
}
return [];

View File

@ -12,6 +12,7 @@ use Yii;
use humhub\libs\ThemeHelper;
use humhub\models\UrlOembed;
use humhub\modules\admin\components\Controller;
use humhub\modules\notification\models\forms\NotificationSettings;
/**
* SettingController
@ -138,7 +139,20 @@ class SettingController extends Controller
$this->view->saved();
}
return $this->render('mailing', array('model' => $form));
return $this->render('mailing', ['model' => $form]);
}
/**
* Notification Mailing Settings
*/
public function actionNotification()
{
$form = new NotificationSettings();
if ($form->load(Yii::$app->request->post()) && $form->validate() && $form->save()) {
$this->view->saved();
}
return $this->render('notification', ['model' => $form]);
}
/**

View File

@ -1,11 +1,4 @@
<?php
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
namespace humhub\modules\admin\models\forms;
use Yii;

View File

@ -0,0 +1,28 @@
<?php
namespace humhub\modules\admin\notifications;
use Yii;
use humhub\modules\notification\components\NotificationCategory;
/**
* Description of AdminNotificationCategory
*
* @author buddha
*/
class AdminNotificationCategory extends NotificationCategory
{
public $id = 'admin';
public $sortOrder = 100;
public function getDescription()
{
return Yii::t('AdminModule.notifications_AdminNotificationCategory', 'Receive Notifications for administrative events like available updates.');
}
public function getTitle()
{
return Yii::t('AdminModule.notifications_AdminNotificationCategory', 'Administrative');
}
}

View File

@ -34,6 +34,14 @@ class NewVersionAvailable extends BaseNotification
{
return \yii\helpers\Url::to(['/admin/information/about']);
}
/**
* @inheritdoc
*/
public function category()
{
return new AdminNotificationCategory;
}
/**
* @inheritdoc
@ -46,7 +54,7 @@ class NewVersionAvailable extends BaseNotification
/**
* @inheritdoc
*/
public function getAsHtml()
public function html()
{
return Yii::t('AdminModule.notification', "There is a new HumHub Version ({version}) available.", ['version' => Html::tag('strong', $this->getLatestHumHubVersion())]);
}

View File

@ -0,0 +1,22 @@
<?php
use yii\widgets\ActiveForm;
/* @var $model \humhub\modules\notification\models\forms\NotificationSettings */
?>
<div class="panel-body">
<h4><?= Yii::t('AdminModule.setting', 'Notification Settings'); ?></h4>
<div class="help-block">
<?= Yii::t('AdminModule.setting',
'Here you can configure the default notification behaviour for your users.'); ?><br />
<?= Yii::t('AdminModule.setting', 'You can enable outgoing notifications for a notification category by choosing the disired notification targets.'); ?>
</div>
<br />
<?php $form = ActiveForm::begin() ?>
<?= humhub\modules\notification\widgets\NotificationSettingsForm::widget([
'model' => $model,
'form' => $form
]) ?>
<?php ActiveForm::end(); ?>
</div>

View File

@ -26,42 +26,50 @@ class SettingsMenu extends \humhub\widgets\BaseMenu
{
$canEditSettings = Yii::$app->user->can(new \humhub\modules\admin\permissions\ManageSettings());
$this->addItem(array(
$this->addItem([
'label' => Yii::t('AdminModule.widgets_AdminMenuWidget', 'General'),
'url' => Url::toRoute('/admin/setting/index'),
'icon' => '<i class="fa fa-cogs"></i>',
'sortOrder' => 100,
'isActive' => (Yii::$app->controller->module && Yii::$app->controller->module->id == 'admin' && Yii::$app->controller->id == 'setting' && Yii::$app->controller->action->id == 'basic'),
'isVisible' => $canEditSettings
));
]);
$this->addItem(array(
$this->addItem([
'label' => Yii::t('AdminModule.widgets_AdminMenuWidget', 'Appearance'),
'url' => Url::toRoute('/admin/setting/design'),
'icon' => '<i class="fa fa-magic"></i>',
'sortOrder' => 200,
'isActive' => (Yii::$app->controller->module && Yii::$app->controller->module->id == 'admin' && Yii::$app->controller->id == 'setting' && Yii::$app->controller->action->id == 'design'),
'isVisible' => $canEditSettings
));
]);
$this->addItem(array(
$this->addItem([
'label' => Yii::t('AdminModule.widgets_AdminMenuWidget', 'E-Mails'),
'url' => Url::toRoute('/admin/setting/mailing'),
'icon' => '<i class="fa fa-envelope"></i>',
'sortOrder' => 300,
'isActive' => (Yii::$app->controller->module && Yii::$app->controller->module->id == 'admin' && Yii::$app->controller->id == 'setting' && (Yii::$app->controller->action->id == 'mailing' || Yii::$app->controller->action->id == 'mailing-server')),
'isVisible' => $canEditSettings
));
]);
$this->addItem([
'label' => Yii::t('AdminModule.widgets_AdminMenuWidget', 'Notifications'),
'url' => Url::toRoute('/admin/setting/notification'),
'icon' => '<i class="fa fa-envelope"></i>',
'sortOrder' => 400,
'isActive' => (Yii::$app->controller->module && Yii::$app->controller->module->id == 'admin' && Yii::$app->controller->id == 'setting' && (Yii::$app->controller->action->id == 'notification')),
'isVisible' => $canEditSettings
]);
$this->addItem(array(
$this->addItem([
'label' => Yii::t('AdminModule.widgets_AdminMenuWidget', 'Advanced'),
'url' => Url::toRoute('/admin/setting/advanced'),
'icon' => '<i class="fa fa-lock"></i>',
'sortOrder' => 1000,
'isVisible' => $canEditSettings
));
]);
parent::init();
}
}

View File

@ -38,14 +38,6 @@ class NewComment extends \humhub\modules\notification\components\BaseNotificatio
return parent::send($user);
}
/**
* @inheritdoc
*/
public static function getTitle()
{
return Yii::t('CommentModule.notifications_NewComment', 'New Comment');
}
/**
* @inheritdoc
*/

View File

@ -1,9 +1,2 @@
<?php
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
return [];

View File

@ -33,14 +33,14 @@ class ContentCreated extends BaseActivity
/**
* @inheritdoc
*/
public function render($mode = self::OUTPUT_WEB, $params = array())
public function render($params = array())
{
if ($this->source === null) {
Yii::error('Could not render ContentCreated Activity without given source - ' . $this->record->id);
return;
}
return parent::render($mode, $params);
return parent::render($params);
}
}

View File

@ -12,6 +12,7 @@ use Yii;
use yii\base\Exception;
use humhub\components\ActiveRecord;
use humhub\modules\content\models\Content;
use humhub\modules\content\interfaces\ContentOwner;
/**
* ContentActiveRecord is the base ActiveRecord [[\yii\db\ActiveRecord]] for Content.
@ -41,7 +42,7 @@ use humhub\modules\content\models\Content;
*
* @author Luke
*/
class ContentActiveRecord extends ActiveRecord implements \humhub\modules\content\interfaces\ContentTitlePreview
class ContentActiveRecord extends ActiveRecord implements ContentOwner
{
/**
@ -188,7 +189,7 @@ class ContentActiveRecord extends ActiveRecord implements \humhub\modules\conten
/**
* Related Content model
*
* @return \yii\db\ActiveQuery
* @return Content
*/
public function getContent()
{

View File

@ -11,6 +11,7 @@ namespace humhub\modules\content\components;
use Yii;
use yii\base\Exception;
use humhub\components\ActiveRecord;
use humhub\modules\content\interfaces\ContentOwner;
/**
* HActiveRecordContentAddon is the base active record for content addons.
@ -28,7 +29,7 @@ use humhub\components\ActiveRecord;
* @package humhub.components
* @since 0.5
*/
class ContentAddonActiveRecord extends ActiveRecord implements \humhub\modules\content\interfaces\ContentTitlePreview
class ContentAddonActiveRecord extends ActiveRecord implements ContentOwner
{
/**

View File

@ -8,6 +8,7 @@
namespace humhub\modules\content\components;
use Yii;
use humhub\libs\BaseSettingsManager;
/**
@ -28,6 +29,22 @@ class ContentContainerSettingsManager extends BaseSettingsManager
* @var ContentContainerActiveRecord the content container this settings manager belongs to
*/
public $contentContainer;
/**
* Returns the setting value of this container for the given setting $name.
* If there is not container specific setting, this function will search for a global setting or
* return default or null if there is also no global setting.
*
* @param type $name
* @param type $default
* @return boolean
* @since 1.2
*/
public function getInherit($name, $default = null) {
$result = $this->get($name);
return ($result != null) ? $result
: Yii::$app->getModule($this->moduleId)->settings->get($name, $default);
}
/**
* @inheritdoc

View File

@ -0,0 +1,15 @@
<?php
namespace humhub\modules\content\interfaces;
/**
*
* @author luke
*/
interface ContentOwner
{
public function getContent();
public function getContentName();
public function getContentDescription();
}

View File

@ -1,21 +0,0 @@
<?php
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
namespace humhub\modules\content\interfaces;
/**
*
* @author luke
*/
interface ContentTitlePreview
{
public function getContentName();
public function getContentDescription();
}

View File

@ -26,7 +26,7 @@ class ContentCreated extends \humhub\modules\notification\components\BaseNotific
/**
* @inheritdoc
*/
public function getAsHtml()
public function html()
{
return Yii::t('ContentModule.notifications_views_ContentCreated', '{displayName} created {contentTitle}.', array(
'displayName' => Html::tag('strong', Html::encode($this->originator->displayName)),

View File

@ -16,7 +16,8 @@ class ContentContainerFixture extends ActiveFixture
public $dataFile = '@modules/content/tests/codeception/fixtures/data/contentcontainer.php';
public $depends = [
'humhub\modules\content\tests\codeception\fixtures\ContentContainerPermissionFixture'
'humhub\modules\content\tests\codeception\fixtures\ContentContainerPermissionFixture',
'humhub\modules\content\tests\codeception\fixtures\ContentContainerSettingFixture'
];
}

View File

@ -0,0 +1,19 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\content\tests\codeception\fixtures;
use yii\test\ActiveFixture;
class ContentContainerSettingFixture extends ActiveFixture
{
public $modelClass = 'humhub\modules\content\models\ContentContainerSetting';
public $dataFile = '@modules/content/tests/codeception/fixtures/data/contentcontainer_setting.php';
}

View File

@ -0,0 +1,20 @@
<?php
/**
* HumHub
* Copyright © 2014 The HumHub Project
*
* The texts of the GNU Affero General Public License with an additional
* permission and of our proprietary license can be found at and
* in the LICENSE file you have received along with this program.
*
* According to our dual licensing model, this program can be used either
* under the terms of the GNU Affero General Public License, version 3,
* or under a proprietary license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*/
return [];

View File

@ -0,0 +1,52 @@
<?php
namespace humhub\modules\friendship\notifications;
use Yii;
use humhub\modules\notification\components\NotificationCategory;
use humhub\modules\notification\components\NotificationTarget;
/**
* Description of SpaceJoinNotificationCategory
*
* @author buddha
*/
class FriendshipNotificationCategory extends NotificationCategory
{
/**
* Category Id
* @var string
*/
public $id = 'friendship';
/**
* @inheritdoc
*/
public function getTitle()
{
return Yii::t('SpaceModule.notifications_FriendshipNotificationCategory', 'Friendship');
}
/**
* @inheritdoc
*/
public function getDescription()
{
return Yii::t('SpaceModule.notifications_FriendshipNotificationCategory', 'Receive Notifications for Friendship Request and Approval events');
}
/**
* @inheritdoc
*/
public function getDefaultSetting(NotificationTarget $target)
{
if ($target->id === \humhub\modules\notification\components\MailNotificationTarget::getId()) {
return true;
} else if ($target->id === \humhub\modules\notification\components\WebNotificationTarget::getId()) {
return true;
} else if ($target->id === \humhub\modules\notification\components\MobileNotificationTarget::getId()) {
return true;
}
return $target->defaultSetting;
}
}

View File

@ -37,19 +37,19 @@ class Request extends BaseNotification
{
return $this->originator->getUrl();
}
/**
* @inheritdoc
*/
public static function getTitle()
public function category()
{
return Yii::t('FriendshipModule.notifications_Request', 'Friendship Request');
return new FriendshipNotificationCategory;
}
/**
* @inheritdoc
*/
public function getAsHtml()
public function html()
{
return Yii::t('FriendshipModule.notification', '{displayName} sent you a friend request.', array(
'displayName' => Html::tag('strong', Html::encode($this->originator->displayName)),

View File

@ -30,6 +30,14 @@ class RequestApproved extends BaseNotification
*/
public $markAsSeenOnClick = true;
/**
* @inheritdoc
*/
public function category()
{
return new FriendshipNotificationCategory;
}
/**
* @inheritdoc
*/
@ -41,19 +49,11 @@ class RequestApproved extends BaseNotification
/**
* @inheritdoc
*/
public static function getTitle()
public function html()
{
return Yii::t('FriendshipModule.notifications_RequestApproved', 'Friendship Approved');
}
/**
* @inheritdoc
*/
public function getAsHtml()
{
return Yii::t('FriendshipModule.notification', '{displayName} accepted your friend request.', array(
return Yii::t('FriendshipModule.notification', '{displayName} accepted your friend request.', [
'displayName' => Html::tag('strong', Html::encode($this->originator->displayName)),
));
]);
}
}

View File

@ -30,6 +30,11 @@ class RequestDeclined extends BaseNotification
*/
public $markAsSeenOnClick = true;
public function category()
{
return new FriendshipNotificationCategory;
}
/**
* @inheritdoc
*/

View File

@ -25,14 +25,6 @@ class NewLike extends BaseNotification
*/
public $moduleId = 'like';
/**
* @inheritdoc
*/
public static function getTitle()
{
return Yii::t('LikeModule.notifiations_NewLike', 'New Like');
}
/**
* @inheritdoc
*/
@ -45,20 +37,21 @@ class NewLike extends BaseNotification
/**
* @inheritdoc
*/
public function getAsHtml()
public function html()
{
$contentInfo = $this->getContentInfo($this->getLikedRecord());
if ($this->groupCount > 1) {
return Yii::t('LikeModule.notification', "{displayNames} likes {contentTitle}.", array(
return Yii::t('LikeModule.notification', "{displayNames} likes {contentTitle}.", [
'displayNames' => $this->getGroupUserDisplayNames(),
'contentTitle' => $contentInfo
));
]);
}
return Yii::t('LikeModule.notification', "{displayName} likes {contentTitle}.", array(
return Yii::t('LikeModule.notification', "{displayName} likes {contentTitle}.", [
'displayName' => Html::tag('strong', Html::encode($this->originator->displayName)),
'contentTitle' => $contentInfo
));
]);
}
/**

View File

@ -1,9 +1,3 @@
<?php
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
return [];

View File

@ -13,11 +13,16 @@ use yii\helpers\Url;
use yii\bootstrap\Html;
use humhub\modules\notification\models\Notification;
use humhub\modules\user\models\User;
use humhub\modules\content\components\ContentActiveRecord;
use humhub\modules\content\components\ContentAddonActiveRecord;
/**
* BaseNotification
* A BaseNotification class describes the behaviour and the type of a Notification.
* A BaseNotification is created and can be sent to one or multiple users over different targets.
*
* The BaseNotification can should be created like this:
*
* MyNotification::instance()->from($originator)->about($source)->sendBulk($userList);
*
* This will send Notifications to different NotificationTargets by using a queue.
*
* @author luke
*/
@ -25,34 +30,10 @@ abstract class BaseNotification extends \humhub\components\SocialActivity
{
/**
* Space this notification belongs to. (Optional)
* If source is a Content, ContentAddon or ContentContainer this will be
* automatically set.
*
* @var \humhub\modules\space\models\Space
* Can be used to delay the NotificationJob execution.
* @var type
*/
public $space;
/**
* Layout file for web version
*
* @var string
*/
protected $layoutWeb = "@notification/views/layouts/web.php";
/**
* Layout file for mail version
*
* @var string
*/
protected $layoutMail = "@notification/views/layouts/mail.php";
/**
* Layout file for mail plaintext version
*
* @var string
*/
protected $layoutMailPlaintext = "@notification/views/layouts/mail_plaintext.php";
public $delay = 0;
/**
* @var boolean automatically mark notification as seen after click on it
@ -69,18 +50,72 @@ abstract class BaseNotification extends \humhub\components\SocialActivity
*/
protected $_groupKey = null;
/**
* @var \humhub\modules\notification\components\NotificationCategory cached category instance
*/
protected $_category = null;
/**
* @inheritdoc
*/
public $recordClass = Notification::class;
/**
* Returns the notification category instance. If no category class is set (default) the default notification settings
* can't be overwritten.
*
* The category instance is cached, once created.
*
* If the Notification configuration should be configurable subclasses have to overwrite this method.
*
* @return \humhub\modules\notification\components\NotificationCategory
*/
public function getCategory()
{
if (!$this->_category) {
$this->_category = $this->category();
}
return $this->_category;
}
/**
* Returns a new NotificationCategory instance.
*
* This function should be overwritten by subclasses to append this BaseNotification
* to the returned category. If no category instance is returned, the BaseNotification behavriour (targets) will not be
* configurable.
*
* @return \humhub\modules\notification\components\NotificationCategory
*/
protected function category()
{
return null;
}
/**
* @return string title text for this notification
*/
public function getTitle()
{
$category = $this->getCategory();
if ($category) {
return Yii::t('NotificationModule.base', 'There are new updates available at {baseUrl} - ({category})', ['baseUrl' => Url::base(true), 'category' => $category]);
} else {
return Yii::t('NotificationModule.base', 'There are new updates available at {baseUrl}', ['baseUrl' => Url::base(true)]);
}
}
/**
* @inheritdoc
*/
public function getViewParams($params = [])
{
$params['url'] = Url::to(['/notification/entry', 'id' => $this->record->id], true);
$params['space'] = $this->space;
$params['isNew'] = ($this->record->seen != 1);
$params['asHtml'] = $this->getAsHtml();
$params['asText'] = $this->getAsText();
$result = [
'url' => Url::to(['/notification/entry', 'id' => $this->record->id], true),
'isNew' => !$this->record->seen,
];
return parent::getViewParams($params);
return \yii\helpers\ArrayHelper::merge(parent::getViewParams($result), $params);
}
/**
@ -100,57 +135,98 @@ abstract class BaseNotification extends \humhub\components\SocialActivity
}
/**
* Sends this notification to a User
* Sends this notification to all notification targets of this User
*
* @param User $user
*/
public function send(User $user)
{
if ($this->moduleId == "") {
throw new \yii\base\InvalidConfigException("No moduleId given!");
if (empty($this->moduleId)) {
throw new \yii\base\InvalidConfigException('No moduleId given for "' . $this->className() . '"');
}
// Skip - do not set notification to the originator
if ($this->originator !== null && $user->id == $this->originator->id) {
if ($this->originator && $this->originator->id == $user->id) {
return;
}
$notification = new Notification;
$notification->user_id = $user->id;
$notification->class = $this->className();
$notification->module = $this->moduleId;
$notification->seen = 0;
// Load group key
if ($this->_groupKey === null) {
$this->_groupKey = $this->getGroupKey();
//$this->queueJob($user);
$this->saveRecord($user);
foreach (Yii::$app->notification->getTargets($user) as $target) {
$target->send($this, $user);
}
}
if ($this->_groupKey !== '') {
$notification->group_key = $this->getGroupKey();
}
/**
* Queues the notification job. The Job is responsible for creating and sending
* the Notifications out to its NotificationTargets.
*
* @param User $user
*/
protected function queueJob(User $user)
{
Yii::$app->notificationQueue->push(new NotificationJob([
'notification' => $this,
'user_id' => $user->id
]));
}
if ($this->source !== null) {
public function save(User $user)
{
// We reuse our record instance to save multiple records.
$this->record->id = null;
$this->record->isNewRecord = true;
$this->record->user_id = $user->id;
return $this->record->save();
}
/**
* Creates the an Notification instance of the current BaseNotification type for the
* given $user.
*
* @param User $user
*/
public function saveRecord(User $user)
{
$notification = new Notification([
'user_id' => $user->id,
'class' => static::class,
'module' => $this->moduleId,
'group_key' => $this->getGroupKey(),
]);
if ($this->source) {
$notification->source_pk = $this->source->getPrimaryKey();
$notification->source_class = $this->source->className();
// Automatically set spaceId if source is Content/Addon/Container
if ($this->source instanceof ContentActiveRecord || $this->source instanceof ContentAddonActiveRecord) {
if ($this->source->content->container instanceof \humhub\modules\space\models\Space) {
$notification->space_id = $this->source->content->container->id;
}
} elseif ($this->source instanceof \humhub\modules\space\models\Space) {
$notification->space_id = $this->source->id;
}
$notification->space_id = $this->getSpaceId();
}
if ($this->originator !== null) {
if ($this->originator) {
$notification->originator_user_id = $this->originator->id;
}
$notification->save();
$this->record = $notification;
}
/**
* @inheritdoc
*/
public function about($source)
{
parent::about($source);
$this->record->space_id = $this->getSpaceId();
return $this;
}
/**
* @inheritdoc
*/
public function from($originator)
{
$this->originator = $originator;
$this->record->originator_user_id = $originator->id;
return $this;
}
/**
@ -160,7 +236,7 @@ abstract class BaseNotification extends \humhub\components\SocialActivity
{
$condition = [];
$condition['class'] = $this->className();
$condition['class'] = static::class;
if ($user !== null) {
$condition['user_id'] = $user->id;
@ -201,71 +277,38 @@ abstract class BaseNotification extends \humhub\components\SocialActivity
$similarNotifications = Notification::find()
->where(['source_class' => $this->record->source_class, 'source_pk' => $this->record->source_pk, 'user_id' => $this->record->user_id])
->andWhere(['!=', 'seen', '1']);
foreach ($similarNotifications->all() as $n) {
$n->getClass()->markAsSeen();
foreach ($similarNotifications->all() as $notification) {
$notification->getClass()->markAsSeen();
}
}
/**
* Returns key to group notifications.
* If empty notification grouping is disabled.
* Returns a key for grouping notifications.
* If null is returned (default) the notification grouping for this BaseNotification type disabled.
*
* The returned key could for example be a combination of classname related content id.
*
* @return string the group key
*/
public function getGroupKey()
{
return "";
}
/**
* Should be overwritten by subclasses. This method provides a user friendly
* title for the different notification types.
*
* @return string e.g. New Like
*/
public static function getTitle()
{
return null;
}
/**
* Returns text version of this notification
* Renders the Notificaiton for the given NotificationTarget.
* Subclasses are able to use custom renderer for different targets by overwriting this function.
*
* @return string
* @param NotificationTarger $target
* @return string render result
*/
public function getAsText()
public function render(NotificationTarget $target = null)
{
$html = $this->getAsHtml();
if ($html === null) {
return null;
if (!$target) {
$target = Yii::$app->notification->getTarget(WebNotificationTarget::class);
}
return strip_tags($html);
}
/**
* Returns Html text of this notification,
*
* @return type
*/
public function getAsHtml()
{
return null;
}
/**
* @inheritdoc
*/
public function render($mode = self::OUTPUT_WEB, $params = array())
{
// Set default notification view - when not specified
if ($this->viewName === null) {
$this->viewName = 'default';
$this->viewPath = '@notification/views/notification';
}
return parent::render($mode, $params);
return $target->getRenderer()->render($this);
}
/**
@ -322,4 +365,42 @@ abstract class BaseNotification extends \humhub\components\SocialActivity
return $users;
}
/**
* @inheritdoc
*/
public function asArray()
{
$result = parent::asArray();
$result['title'] = $this->getTitle();
return $result;
}
/**
* Should be overwritten by subclasses for a html representation of the notification.
* @return type
*/
public function html()
{
// Only for backward compatibility.
return $this->getAsHtml();
}
/**
* Use text() instead
* @deprecated since version 1.2
*/
public function getAsText()
{
return $this->text();
}
/**
* Use html() instead
* @deprecated since version 1.2
*/
public function getAsHtml()
{
return null;
}
}

View File

@ -0,0 +1,76 @@
<?php
namespace humhub\modules\notification\components;
use Yii;
use humhub\modules\user\models\User;
/**
*
* @author buddha
*/
class MailNotificationTarget extends NotificationTarget
{
/**
* @inheritdoc
*/
public $id = 'email';
/**
* Enable this target by default.
* @var type
*/
public $defaultSetting = true;
public $view = [
'html' => '@humhub/modules/content/views/mails/Update',
'text' => '@humhub/modules/content/views/mails/plaintext/Update'
];
/**
* @inheritdoc
*/
public function getTitle()
{
return Yii::t('NotificationModule.components_WebNotificationTarget', 'E-Mail');
}
/**
* @inheritdoc
*/
public function handle(BaseNotification $notification, User $user)
{
try {
// TODO: find cleaner solution...
Yii::$app->view->params['showUnsubscribe'] = true;
$viewParams = [
'notifications' => $notification->render($this),
'notifications_plaintext' => $this->getText($notification)
];
return Yii::$app->mailer->compose($this->view, $viewParams)
->setFrom([Yii::$app->settings->get('mailer.systemEmailAddress') => Yii::$app->settings->get('mailer.systemEmailName')])
->setTo($user->email)
->setSubject($notification->getTitle())->send();
} catch (\Exception $ex) {
Yii::error('Could not send mail to: ' . $user->email . ' - Error: ' . $ex->getMessage());
if(YII_DEBUG) {
throw $ex;
}
}
}
public function getText(BaseNotification $notification)
{
$textRenderer = $this->getRenderer();
if (!method_exists($textRenderer, 'renderText')) {
$textRenderer = Yii::createObject(MailTargetRenderer::class);
}
return $textRenderer->renderText($notification);
}
}

View File

@ -0,0 +1,125 @@
<?php
namespace humhub\modules\notification\components;
use Yii;
use humhub\components\rendering\Viewable;
/**
* The MailTargetRenderer is used to render Notifications for the MailNotificationTarget.
*
* A BaseNotification can overwrite the default view and layout by setting a specific $viewName and
* defining the following files:
*
* Overwrite default html view for this notification:
* @module/views/notification/mail/viewname.php
*
* Overwrite default mail layout for this notification:
* @module/views/layouts/notification/mail/viewname.php
*
* Overwrite default mail text layout for this notification:
* @module/views/layouts/notification/mail/plaintext/viewname.php
*
* @author buddha
*/
class MailTargetRenderer extends \humhub\components\rendering\MailLayoutRenderer
{
/**
* @var string default notification mail view path
*/
public $defaultViewPath = '@notification/views/notification/mail';
/**
* @var string default notification mail view
*/
public $defaultView = '@notification/views/notification/mail/default.php';
/**
* @var string layout file path
*/
public $defaultLayout = '@notification/views/layouts/mail.php';
/**
* @var string default notification mail text view path
*/
public $defaultTextViewPath = '@notification/views/notification/mail/plaintext';
/**
* @var string text layout file
*/
public $defaultTextLayout = "@notification/views/layouts/mail_plaintext.php";
/**
* Returns the view file for the given Viewable Notification.
*
* This function will search for the view file defined in the Viewable within the module/views/notification/mail directory of
* the viewable module.
*
* If the module view does not exist we search for the viewName within the default notification viewPath.
*
* If this view also does not exist we return the base notification view file.
*
* @param \humhub\modules\notification\components\Viewable $viewable
* @return string view file of this notification
*/
public function getViewFile(Viewable $viewable)
{
$viewFile = $this->getViewPath($viewable) . '/notification/mail/' . $viewable->getViewName();
if (!file_exists($viewFile)) {
$viewFile = Yii::getAlias($this->defaultViewPath) . DIRECTORY_SEPARATOR . $viewable->getViewName();
}
if (!file_exists($viewFile)) {
$viewFile = Yii::getAlias($this->defaultView);
}
return $viewFile;
}
/**
* Returns the layout for the given Notification Viewable.
*
* This function will search for a layout file under module/views/layouts/mail with the view name defined
* by $viwable.
*
* If this file does not exists the default notification mail layout will be returned.
*
* @param \humhub\modules\notification\components\Viewable $viewable
* @return type
*/
public function getLayout(Viewable $viewable)
{
$layout = $this->getViewPath($viewable) . '/layouts/notification/mail/' . $viewable->getViewName();
if (!file_exists($layout)) {
$layout = Yii::getAlias($this->defaultLayout);
}
return $layout;
}
/**
* Returns the text layout for the given Notification Viewable.
*
* This function will search for a view file under module/views/layouts/mail/plaintext with the view name defined
* by $viwable.
*
* If this file does not exists the default notification text mail layout is returned.
*
* @param \humhub\modules\notification\components\Viewable $viewable
* @return type
*/
public function getTextLayout(Viewable $viewable)
{
$layout = $this->getViewPath($viewable) . '/layouts/notification/mail/plaintext/' . $viewable->getViewName();
if (!file_exists($layout)) {
$layout = Yii::getAlias($this->defaultTextLayout);
}
return $layout;
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace humhub\modules\notification\components;
use Yii;
use humhub\modules\user\models\User;
/**
*
* @author buddha
*/
class MobileNotificationTarget extends NotificationTarget
{
/**
* @inheritdoc
*/
public $id = 'mobile';
/**
* Used to forward a BaseNotification object to a NotificationTarget.
* The NotificationTarget should handle the notification by pushing a Job to
* a Queue or directly handling the notification.
*
* @param BaseNotification $notification
*/
public function handle(BaseNotification $notification, User $user) {
}
public function getTitle()
{
}
}

View File

@ -0,0 +1,102 @@
<?php
namespace humhub\modules\notification\components;
use humhub\modules\user\models\User;
use humhub\modules\notification\components\NotificationTarget;
/**
* NotificationCategories are used to group different notifications in views and
* configure the notifications in the notification settings.
*
*/
abstract class NotificationCategory extends \yii\base\Object
{
/**
* Category Id
* @var type
*/
public $id;
/**
* Used to sort items in the frontend.
* @var type
*/
public $sortOrder = PHP_INT_MAX;
public function init()
{
parent::init();
if(!$this->id) {
throw new \yii\base\InvalidConfigException('NotificationCategories have to define an id property, which is not the case for "'.self::class.'"');
}
}
/**
* Returns a human readable title of this category
*/
public abstract function getTitle();
/**
* Returns a group description
*/
public abstract function getDescription();
/**
* Returns the default enabled settings for the given $target.
* In case the $target is unknown, subclasses can either return $target->defaultSetting
* or another default value.
*
* @param NotificationTarget $target
* @return boolean
*/
public function getDefaultSetting(NotificationTarget $target)
{
if ($target->id === \humhub\modules\notification\components\MailNotificationTarget::getId()) {
return true;
} else if ($target->id === \humhub\modules\notification\components\WebNotificationTarget::getId()) {
return true;
} else if ($target->id === \humhub\modules\notification\components\MobileNotificationTarget::getId()) {
return false;
}
return $target->defaultSetting;
}
/**
* Returns an array of target ids, which are not editable.
*
* @param NotificationTarget $target
*/
public function getFixedSettings()
{
return [];
}
/**
* Checks if the given NotificationTarget is fixed for this category.
*
* @param type $target
* @return type
*/
public function isFixedSettings(NotificationTarget $target)
{
return in_array($target->id, $this->getFixedSettings());
}
/**
* Determines if this category is visible for the given $user.
* This can be used if a category is only visible for users with certian permissions.
*
* Note if no user is given this function should return true in most cases, otherwise this
* category won't be visible in the global notification settings.
*
* @param User $user
* @return boolean
*/
public function isVisible(User $user = null)
{
return true;
}
}

View File

@ -0,0 +1,133 @@
<?php
namespace humhub\modules\notification\components;
use Yii;
/**
* Description of NotificationManager
*
* @author buddha
*/
class NotificationManager
{
/**
*
* @var array Target configuration.
*/
public $targets = [];
/**
*
* @var BaseNotification[] Cached array of BaseNotification instances.
*/
protected $_notifications;
/**
* @var NotificationTarget[] Cached target instances.
*/
protected $_targets;
/**
* Cached array of NotificationCategories
* @var type
*/
protected $_categories;
/**
* Returns all active targets for the given user.
*
* @param type $user
* @return type
*/
public function getTargets($user = null)
{
if($this->_targets) {
return $this->_targets;
}
foreach($this->targets as $target) {
$instance = Yii::createObject($target);
if($instance->isActive($user)) {
$this->_targets[] = $instance;
}
}
return $this->_targets;
}
public function getTarget($class)
{
foreach($this->getTargets() as $target) {
if($target->className() == $class) {
return $target;
}
}
}
/**
* Returns all available Notifications
*
* @return type
*/
public function getNotifications()
{
if (!$this->_notifications) {
$this->_notifications = $this->searchModuleNotifications();
}
return $this->_notifications;
}
/**
* Returns all available NotificationCategories as array with category id as
* key and a category instance as value.
*/
public function getNotificationCategories($user = null)
{
if($this->_categories) {
return $this->_categories;
}
$result = [];
foreach($this->getNotifications() as $notification) {
$category = $notification->getCategory();
if($category && !array_key_exists($category->id, $result) && $category->isVisible($user)) {
$result[$category->id] = $category;
}
}
$this->_categories = array_values($result);
usort($this->_categories, function($a, $b) {
return $a->sortOrder - $b->sortOrder;
});
return $this->_categories;
}
/**
* Searches for all Notifications exported by modules.
* @return type
*/
protected function searchModuleNotifications()
{
$result = [];
foreach (Yii::$app->moduleManager->getModules(['includeCoreModules' => true]) as $module) {
if ($module instanceof \humhub\components\Module && $module->hasNotifications()) {
$result = array_merge($result, $this->createNotifications($module->getNotifications()));
}
}
return $result;
}
protected function createNotifications($notificationClasses)
{
$result = [];
foreach($notificationClasses as $notificationClass) {
$result[] = Yii::createObject($notificationClass);
}
return $result;
}
}

View File

@ -0,0 +1,242 @@
<?php
namespace humhub\modules\notification\components;
use Yii;
use humhub\modules\user\models\User;
use humhub\components\rendering\Renderer;
/**
* A NotificationTarget is used to handle new Basenotifications. A NotificationTarget
* may send nofication information to external services in specific formats or use
* specific protocols.
*
* @author buddha
*/
abstract class NotificationTarget extends \yii\base\Object
{
/**
* Unique target id has to be defined by subclasses.
* @var string
*/
public $id;
/**
* Holds the title of this insance.
* @var type
*/
public $title;
/**
* Default Renderer for this NotificationTarget
* @var type
*/
public $renderer;
/**
* Defines the acknowledge flag in the notification record.
* If not set, the notification target does not support the acknowledgement of a notification,
* or provides an custom implemention.
*
* @var string
* @see NotificationTarget::acknowledge()
*/
public $acknowledgeFlag;
/**
* Will be used as default enable setting, if there is no user specific setting and no
* global setting and also no default setting for this target for a given NotificationCategory.
* @var boolean
*/
public $defaultSetting = false;
public function init()
{
parent::init();
$this->title = $this->getTitle();
}
/**
* @return string Human readable title for views.
*/
public abstract function getTitle();
/**
* @return \humhub\components\rendering\Renderer default renderer for this target.
*/
public function getRenderer()
{
return \yii\di\Instance::ensure($this->renderer, Renderer::class);
}
/**
* Used to handle a BaseNotification for a given $user.
*
* The NotificationTarget can handle the notification for example by pushing a Job to
* a Queue or directly handling the notification.
*
* @param BaseNotification $notification
*/
public abstract function handle(BaseNotification $notification, User $user);
/**
* Used to acknowledge the seding/processing of the given $notification.
*
* @param BaseNotification $notification notification to be acknowledged
* @param boolean $state true if successful otherwise false
*/
public function acknowledge(BaseNotification $notification, $state = true)
{
if ($this->acknowledgeFlag && $notification->record->hasAttribute($this->acknowledgeFlag)) {
$notification->record->setAttribute($this->acknowledgeFlag, $state);
$notification->record->save();
}
}
/**
* @return boolean Check if the given $notification has already been processed.
*/
public function isAcknowledged(BaseNotification $notification)
{
if ($this->acknowledgeFlag && $notification->record->hasAttribute($this->acknowledgeFlag)) {
return $notification->record->getAttribute($this->acknowledgeFlag);
}
return false;
}
/**
* Static access to the target id.
*
* @return string
*/
public static function getId()
{
$instance = new static();
return $instance->id;
}
/**
* Used to process a $notification for the given $user.
*
* By default the $noification will be marked as acknowledged before processing.
* The processing is triggerd by calling $this->handle.
* If the processing fails the acknowledged mark will be set to false.
*
* @param BaseNotification $notification
*/
public function send(BaseNotification $notification, User $user)
{
if ($this->isAcknowledged($notification)) {
return;
}
try {
$this->acknowledge($notification, true);
if ($this->isEnabled($notification, $user)) {
$this->handle($notification, $user);
} else {
$this->acknowledge($notification, false);
}
} catch (\Exception $e) {
Yii::error($e);
$this->acknowledge($notification, false);
//TODO: increment retry count.
}
}
/**
* Used for handling the given $notification for multiple $users.
*
* @param BaseNotification $notification
* @param type $users
*/
public function sendBulk(BaseNotification $notification, $users)
{
if ($users instanceof \yii\db\ActiveQuery) {
$users = $users->all();
}
foreach ($users as $user) {
$this->send($notification, $user);
}
}
/**
* Returns the setting key for this target of the given $category.
* @param type $category
* @return type
*/
public function getSettingKey($category)
{
return $category->id . '_' . $this->id;
}
/**
* Some NotificationTargets may need to be activated first or require a certain permission in order to be used.
*
* This function checks if this target is active for the given user.
* If no user is given this function will determine if the target is globaly active or deactivated.
*
* If a subclass does not overwrite this function it will be activated for all users by default.
*
* @param User $user
*/
public function isActive(User $user = null)
{
return true;
}
/**
* Checks if the given $notification is enabled for this target.
* If the $notification is not part of a NotificationCategory the $defaultSetting
* of this NotificationTarget is returned.
*
* If this NotificationTarget is not active for the given $user, this function will return false.
*
* @param BaseNotification $notification
* @param User $user
* @see NotificationTarget::isActive()
* @see NotificationTarget::isCategoryEnabled()
* @return boolean
*/
public function isEnabled(BaseNotification $notification, User $user = null)
{
if (!$this->isActive($user)) {
return false;
}
$category = $notification->getCategory();
return ($category) ? $this->isCategoryEnabled($category, $user) : $this->defaultSetting;
}
/**
* Returns the enabled setting of this target for the given $category.
*
* @param NotificationCategory $category
* @param User $user
* @return boolean
*/
public function isCategoryEnabled(NotificationCategory $category, User $user = null)
{
if(!$category->isVisible($user)) {
return false;
}
if($category->isFixedSettings($this)) {
return $category->getDefaultSetting($this);
}
$settingKey = $this->getSettingKey($category);
if ($user) {
$enabled = Yii::$app->getModule('notification')->settings->user($user)->getInherit($settingKey, $category->getDefaultSetting($this));
} else {
$enabled = Yii::$app->getModule('notification')->settings->get($settingKey, $category->getDefaultSetting($this));
}
return ($enabled === null) ? $this->defaultSetting : boolval($enabled);
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace humhub\modules\notification\components;
use Yii;
use humhub\modules\user\models\User;
/**
*
* @author buddha
*/
class WebNotificationTarget extends NotificationTarget
{
/**
* @inheritdoc
*/
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.
*/
public function handle(BaseNotification $notification, User $user) {
if(!$notification->record) {
throw new \yii\base\Exception('Notification record not found for BaseNotification "'.$notification->className().'"');
}
}
public function getTitle()
{
return Yii::t('NotificationModule.components_WebNotificationTarget', 'Web');
}
}

View File

@ -0,0 +1,95 @@
<?php
namespace humhub\modules\notification\components;
use Yii;
use humhub\components\rendering\Viewable;
/**
* The WebTargetRenderer is used to render Notifications for the WebNotificationTarget.
*
* A BaseNotification can overwrite the default view and layout by setting a specific $viewName and
* defining the following files:
*
* Overwrite default view for this notification:
* @module/views/notification/viewname.php
*
* Overwrite default layout for this notification:
* @module/views/layouts/notification/viewname.php
*
* @author buddha
*/
class WebTargetRenderer extends \humhub\components\rendering\LayoutRenderer
{
/**
* @var string default view path
*/
public $defaultViewPath = '@notification/views/notification';
/*
* @var string default view
*/
public $defaultView = '@notification/views/notification/default.php';
/**
* @var string default layout
*/
public $defaultLayout = '@notification/views/layouts/web.php';
/**
* @var string defines the view subfolder containing layouts and views.
*/
public $viewSubFolder = 'notification';
/**
* Returns the view file for the given Viewable Notification.
*
* This function will search for the view file defined in the Viewable within the module/views/mail directory of
* the viewable module.
*
* If the module view does not exist we search for the viewName within the default notification viewPath.
*
* If this view also does not exist we return the base notification view file.
*
* @param \humhub\modules\notification\components\Viewable $viewable
* @return string view file of this notification
*/
public function getViewFile(Viewable $viewable)
{
$viewFile = $this->getViewPath($viewable) . '/'.$this->viewSubFolder.'/' . $viewable->getViewName();
if (!file_exists($viewFile)) {
$viewFile = Yii::getAlias($this->defaultViewPath) . DIRECTORY_SEPARATOR . $viewable->getViewName();
}
if (!file_exists($viewFile)) {
$viewFile = Yii::getAlias($this->defaultView);
}
return $viewFile;
}
/**
* Returns the layout for the given Notification Viewable.
*
* This function will search for a layout file under module/views/layouts/mail with the view name defined
* by $viwable.
*
* If this file does not exists the default notification mail layout will be returned.
*
* @param \humhub\modules\notification\components\Viewable $viewable
* @return type
*/
public function getLayout(Viewable $viewable)
{
$layout = $this->getViewPath($viewable) . '/layouts/'.$this->viewSubFolder.'/' . $viewable->getViewName();
if (!file_exists($layout)) {
$layout = Yii::getAlias($this->defaultLayout);
}
return $layout;
}
}

View File

@ -27,6 +27,14 @@ class Notification extends \humhub\components\ActiveRecord
*/
public $group_count;
public function init()
{
parent::init();
if ($this->seen == null) {
$this->seen = 0;
}
}
/**
* @inheritdoc
*/
@ -46,13 +54,23 @@ class Notification extends \humhub\components\ActiveRecord
[['class', 'source_class'], 'string', 'max' => 100]
];
}
/**
* Use getBaseModel instead.
* @deprecated since version 1.2
* @param type $params
*/
public function getClass($params = [])
{
return $this->getBaseModel($params);
}
/**
* Returns the business model of this notification
*
* @return \humhub\modules\notification\components\BaseNotification
*/
public function getClass($params = [])
public function getBaseModel($params = [])
{
if (class_exists($this->class)) {
$params['source'] = $this->getSourceObject();
@ -155,7 +173,7 @@ class Notification extends \humhub\components\ActiveRecord
public static function findGrouped()
{
$query = self::find();
$query->addSelect(['notification.*',
$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'),

View File

@ -0,0 +1,53 @@
<?php
namespace humhub\modules\notification\models\forms;
use Yii;
/**
* Description of NotificationSettings
*
* @author buddha
*/
class NotificationSettings extends \yii\base\Model
{
public $settings = [];
protected $_targets;
public function rules()
{
return [
['settings', 'safe']
];
}
public function targets($user = null)
{
if(!$this->_targets) {
$this->_targets = Yii::$app->notification->getTargets($user);
}
return $this->_targets;
}
public function categories($user = null)
{
return Yii::$app->notification->getNotificationCategories($user);
}
public function getSettingFormname($category, $target)
{
return $this->formName()."[settings][".$target->getSettingKey($category)."]";
}
public function save($user = null)
{
$module = Yii::$app->getModule('notification');
$settingManager = ($user) ? $module->settings->user($user) : $module->settings;
foreach ($this->settings as $settingKey => $value) {
$settingManager->set($settingKey, $value);
}
}
}

View File

@ -1,5 +1,5 @@
<?php
namespace user;
namespace notification;
/**
* Inherited Methods

View File

@ -1,5 +1,5 @@
<?php
namespace user;
namespace notification;
/**
* Inherited Methods

View File

@ -1,5 +1,5 @@
<?php
namespace user;
namespace notification;
/**
* Inherited Methods

View File

@ -1,4 +1,4 @@
<?php //[STAMP] 890a71cd7f21af7a261c0d3eceef09fb
<?php //[STAMP] c20a7faea2c1c711fd2f9ce4e9fd6e65
namespace notification\_generated;
// This class was automatically generated by build task

View File

@ -1,4 +1,4 @@
<?php //[STAMP] 74c0c3a2840b2412882616390669c863
<?php //[STAMP] 25c2b4e8b8fd2158213a3465a3ef3b60
namespace notification\_generated;
// This class was automatically generated by build task

View File

@ -1,4 +1,4 @@
<?php //[STAMP] e93907d5924b39a3cd4134cc6a6ea3a3
<?php //[STAMP] 0468b7e2480518455b63a22d3aa6f7c2
namespace notification\_generated;
// This class was automatically generated by build task

View File

@ -1,9 +1,2 @@
<?php
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
return [];

View File

@ -0,0 +1,154 @@
<?php
namespace humhub\modules\notification\tests\codeception\unit\category;
use Yii;
use tests\codeception\_support\HumHubDbTestCase;
use Codeception\Specify;
use humhub\modules\notification\components\MailNotificationTarget;
use humhub\modules\notification\components\WebNotificationTarget;
use humhub\modules\notification\tests\codeception\unit\category\notifications\TestNotification;
use humhub\modules\notification\tests\codeception\unit\category\notifications\SpecialNotification;
use humhub\modules\notification\models\forms\NotificationSettings;
class NotificationCategoryTest extends HumHubDbTestCase
{
use Specify;
public function testGlobalCategorySetting()
{
$notification = new TestNotification();
$category = $notification->getCategory();
$mailTarget = Yii::$app->notification->getTarget(MailNotificationTarget::class);
$webTarget = Yii::$app->notification->getTarget(WebNotificationTarget::class);
$this->assertFalse($mailTarget->isEnabled($notification));
$this->assertTrue($webTarget->isEnabled($notification));
$settingForm = new NotificationSettings([
'settings' => [
$mailTarget->getSettingKey($category) => true,
$webTarget->getSettingKey($category) => false,
]
]);
$settingForm->save();
$this->assertTrue($mailTarget->isEnabled($notification));
$this->assertFalse($webTarget->isEnabled($notification));
}
public function testFixedCategorySetting()
{
$notification = new SpecialNotification();
$category = $notification->getCategory();
$mailTarget = Yii::$app->notification->getTarget(MailNotificationTarget::class);
$webTarget = Yii::$app->notification->getTarget(WebNotificationTarget::class);
$this->assertFalse($mailTarget->isEnabled($notification));
$this->assertFalse($webTarget->isEnabled($notification));
// Set true for both
$settingForm = new NotificationSettings([
'settings' => [
$mailTarget->getSettingKey($category) => true,
$webTarget->getSettingKey($category) => true,
]
]);
$settingForm->save();
// Check that setting does not effect fixed target setting.
$this->assertTrue($webTarget->isEnabled($notification));
$this->assertFalse($mailTarget->isEnabled($notification));
}
public function testInvisibleCategorySetting()
{
// SpecialCategory is invisible for this user.
$this->becomeUser('User1');
$user = Yii::$app->user->getIdentity();
$notification = new SpecialNotification();
$category = $notification->getCategory();
$mailTarget = Yii::$app->notification->getTarget(MailNotificationTarget::class);
$webTarget = Yii::$app->notification->getTarget(WebNotificationTarget::class);
$this->assertFalse($mailTarget->isEnabled($notification));
$this->assertFalse($webTarget->isEnabled($notification));
// Set global settings to true for both targets
$settingForm = new NotificationSettings([
'settings' => [
$mailTarget->getSettingKey($category) => true,
$webTarget->getSettingKey($category) => true,
]
]);
$settingForm->save();
// Check this does not affect the decision for this user
$this->assertFalse($webTarget->isEnabled($notification, $user));
$this->assertFalse($mailTarget->isEnabled($notification, $user));
// Save again for the user
$settingForm = new NotificationSettings([
'settings' => [
$mailTarget->getSettingKey($category) => true,
$webTarget->getSettingKey($category) => true,
]
]);
$settingForm->save($user);
// Check this does not affect the decision for this user
$this->assertFalse($webTarget->isEnabled($notification, $user));
$this->assertFalse($mailTarget->isEnabled($notification, $user));
}
public function testUserCategorySetting()
{
$this->becomeUser('User2');
$user = Yii::$app->user->getIdentity();
$notification = new TestNotification();
$category = $notification->getCategory();
$mailTarget = Yii::$app->notification->getTarget(MailNotificationTarget::class);
$webTarget = Yii::$app->notification->getTarget(WebNotificationTarget::class);
// Check default settings.
$this->assertFalse($mailTarget->isEnabled($notification, $user));
$this->assertTrue($webTarget->isEnabled($notification, $user));
// Change global default settings, deny both targets.
$settingForm = new NotificationSettings([
'settings' => [
$mailTarget->getSettingKey($category) => false,
$webTarget->getSettingKey($category) => false,
]
]);
$settingForm->save();
// Check if global defaults effected user check
$this->assertFalse($mailTarget->isEnabled($notification, $user));
$this->assertFalse($webTarget->isEnabled($notification, $user));
// Change user settings.
$userSettings = new NotificationSettings([
'settings' => [
$mailTarget->getSettingKey($category) => true,
$webTarget->getSettingKey($category) => true,
]
]);
$userSettings->save($user);
// Check that global settings are unaffected
$this->assertFalse($mailTarget->isEnabled($notification));
$this->assertFalse($webTarget->isEnabled($notification));
// Check if user settings
$this->assertTrue($mailTarget->isEnabled($notification, $user));
$this->assertTrue($webTarget->isEnabled($notification, $user));
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace humhub\modules\notification\tests\codeception\unit\category\notifications;
/**
* Description of TestedDefaultViewNotification
*
* @author buddha
*/
class SpecialNotification extends \humhub\modules\notification\components\BaseNotification
{
/**
* @inheritdoc
*/
public function category()
{
return new SpecialNotificationCategory();
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace humhub\modules\notification\tests\codeception\unit\category\notifications;
use humhub\modules\notification\components\NotificationTarget;
/**
* Description of TestedDefaultViewNotification
*
* @author buddha
*/
class SpecialNotificationCategory extends \humhub\modules\notification\components\NotificationCategory
{
public $id = 'test_special';
public function getDefaultSetting(NotificationTarget $target)
{
if ($target->id === \humhub\modules\notification\components\MailNotificationTarget::getId()) {
return false;
} else if ($target->id === \humhub\modules\notification\components\WebNotificationTarget::getId()) {
return false;
}
return $target->defaultSetting;
}
public function getFixedSettings()
{
return [\humhub\modules\notification\components\MailNotificationTarget::getId()];
}
public function isVisible(\humhub\modules\user\models\User $user = null)
{
return !$user || $user->id != 2;
}
public function getDescription()
{
return 'My Special Test Notification Category';
}
public function getTitle()
{
return 'Test Special Category';
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace humhub\modules\notification\tests\codeception\unit\category\notifications;
/**
* Description of TestedDefaultViewNotification
*
* @author buddha
*/
class TestNotification extends \humhub\modules\notification\components\BaseNotification
{
/**
* @inheritdoc
*/
public function category()
{
return new TestNotificationCategory();
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace humhub\modules\notification\tests\codeception\unit\category\notifications;
use humhub\modules\notification\components\NotificationTarget;
/**
* Description of TestedDefaultViewNotification
*
* @author buddha
*/
class TestNotificationCategory extends \humhub\modules\notification\components\NotificationCategory
{
public $id = 'test';
public function getDefaultSetting(NotificationTarget $target)
{
if ($target->id === \humhub\modules\notification\components\MailNotificationTarget::getId()) {
return false;
} else if ($target->id === \humhub\modules\notification\components\WebNotificationTarget::getId()) {
return true;
}
return $target->defaultSetting;
}
public function getDescription()
{
return 'My Test Notification Category';
}
public function getTitle()
{
return 'Test Category';
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace humhub\modules\notification\tests\codeception\unit\rendering;
use Yii;
use tests\codeception\_support\HumHubDbTestCase;
use Codeception\Specify;
use humhub\modules\notification\components\MailNotificationTarget;
class MailTargetRenderTest extends HumHubDbTestCase
{
use Specify;
public function testDefaultView()
{
$notification = notifications\TestedMailViewNotification::instance();
$target = Yii::$app->notification->getTarget(MailNotificationTarget::class);
$renderer = $target->getRenderer();
$this->assertContains('<h1>TestedMailViewNotificationHTML</h1>', $renderer->render($notification));
$this->assertContains('TestedMailViewNotificationText', $renderer->renderText($notification));
}
public function testOverwriteViewFile()
{
$notification = notifications\TestedMailViewNotification::instance();
$notification->viewName = 'special';
$target = Yii::$app->notification->getTarget(MailNotificationTarget::class);
$renderer = $target->getRenderer();
$this->assertContains('<div>Special:<h1>TestedMailViewNotificationHTML</h1></div>', $renderer->render($notification));
$this->assertContains('TestedMailViewNotificationText', $renderer->renderText($notification));
}
public function testOverwriteLayoutFile()
{
$notification = notifications\TestedMailViewNotification::instance();
$notification->viewName = 'specialLayout';
$target = Yii::$app->notification->getTarget(MailNotificationTarget::class);
$renderer = $target->getRenderer();
$this->assertEquals('<div>MyLayout:<h1>TestedMailViewNotificationHTML</h1></div>', trim($renderer->render($notification)));
$this->assertEquals('MyLayout:TestedMailViewNotificationText', trim($renderer->renderText($notification)));
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace humhub\modules\notification\tests\codeception\unit\rendering;
use Yii;
use tests\codeception\_support\HumHubDbTestCase;
use Codeception\Specify;
use humhub\modules\notification\components\WebNotificationTarget;
class WebTargetRenderTest extends HumHubDbTestCase
{
use Specify;
public function testDefaultView()
{
$notification = notifications\TestedMailViewNotification::instance();
$target = Yii::$app->notification->getTarget(WebNotificationTarget::class);
$renderer = $target->getRenderer();
$result = $renderer->render($notification);
$this->assertContains('New', $result);
$this->assertContains('<h1>TestedMailViewNotificationHTML</h1>', $result);
}
public function testOverwriteViewFile()
{
$notification = notifications\TestedMailViewNotification::instance();
$notification->viewName = 'special';
$target = Yii::$app->notification->getTarget(WebNotificationTarget::class);
$renderer = $target->getRenderer();
$result = $renderer->render($notification);
$this->assertContains('New', $result);
$this->assertContains('<div>Special:<h1>TestedMailViewNotificationHTML</h1></div>', $result);
}
public function testOverwriteLayoutFile()
{
$notification = notifications\TestedMailViewNotification::instance();
$notification->viewName = 'specialLayout';
$target = Yii::$app->notification->getTarget(WebNotificationTarget::class);
$renderer = $target->getRenderer();
$result = $renderer->render($notification);
$this->assertNotContains('New', $result);
$this->assertEquals('<div>MyLayout:<h1>TestedMailViewNotificationHTML</h1></div>', trim($result));
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace humhub\modules\notification\tests\codeception\unit\rendering\notifications;
/**
* Description of TestedDefaultViewNotification
*
* @author buddha
*/
class TestedMailViewNotification extends \humhub\modules\notification\components\BaseNotification
{
public function html()
{
return '<h1>TestedMailViewNotificationHTML</h1>';
}
public function text()
{
return 'TestedMailViewNotificationText';
}
}

View File

@ -0,0 +1,2 @@
<div>MyLayout:<?= $content ?></div>

View File

@ -0,0 +1,2 @@
<div>Special:<?= $html ?></div>

View File

@ -0,0 +1,2 @@
<div>Special:<?= $html ?></div>

View File

@ -64,7 +64,7 @@ use yii\helpers\Html;
<tr>
<td style="font-size: 13px; line-height: 22px; font-family:Open Sans,Arial,Tahoma, Helvetica, sans-serif; color:#555555; font-weight:300; text-align:left; ">
<?php echo $content; ?>
<?= $content; ?>
<!-- check if activity object has a space -->
<?php if ($space !== null): ?>

View File

@ -8,7 +8,7 @@
?>
<li class="<?php if ($isNew) : ?>new<?php endif; ?>">
<a href="<?php echo $url; ?>">
<a href="<?= $url; ?>">
<div class="media">
<!-- show user image -->
@ -30,10 +30,10 @@
<!-- show content -->
<div class="media-body">
<?php echo $content; ?>
<?= $content; ?>
<br> <?php echo humhub\widgets\TimeAgo::widget(['timestamp' => $record->created_at]); ?>
<?php if ($isNew) : ?> <span class="label label-danger"><?php echo Yii::t('NotificationModule.views_notificationLayout', 'New'); ?></span><?php endif; ?>
<?php if ($isNew) : ?> <span class="label label-danger"><?= Yii::t('NotificationModule.views_notificationLayout', 'New'); ?></span><?php endif; ?>
</div>
</div>

View File

@ -1 +1 @@
<?= $asHtml; ?>
<?= $html; ?>

View File

@ -0,0 +1 @@
<?= $html; ?>

View File

@ -1 +1 @@
<?= $asText; ?>
<?= $text; ?>

View File

@ -0,0 +1,38 @@
<?php
namespace humhub\modules\notification\widgets;
/**
* Description of NotificationSettingForm
*
* @author buddha
*/
class NotificationSettingsForm extends \yii\base\Widget
{
/**
* @var \yii\widgets\ActiveForm
*/
public $form;
/**
* @var \humhub\modules\notification\models\forms\NotificationSettings
*/
public $model;
/**
* Used for user notification settings. If null use global settings.
* @var type
*/
public $user;
/**
* @inheritdoc
*/
public function run()
{
return $this->render('notificationSettingsForm', [
'form' => $this->form,
'model' => $this->model,
'user' => $this->user
]);
}
}

View File

@ -0,0 +1,43 @@
<?php
/* @var $model \humhub\modules\notification\models\forms\NotificationSettings */
/* @var $form yii\widgets\ActiveForm */
use yii\bootstrap\Html
?>
<div class="table-responsive">
<table class="table table-middle table-hover">
<thead>
<tr>
<th><?= Yii::t('NotificationModule.widgets_views_notificationSettingsForm', 'Category') ?></th>
<th><?= Yii::t('NotificationModule.widgets_views_notificationSettingsForm', 'Description') ?></th>
<?php foreach ($model->targets($user) as $target): ?>
<th class="text-center">
<?= $target->getTitle(); ?>
</th>
<?php endforeach; ?>
</tr>
</thead>
<tbody>
<?php foreach ($model->categories($user) as $category): ?>
<tr>
<td>
<?= $category->getTitle() ?>
</td>
<td>
<?= $category->getDescription() ?>
</td>
<?php foreach ($model->targets($user) as $target): ?>
<td class="text-center">
<label style="margin:0px;">
<?= Html::checkbox($model->getSettingFormname($category, $target), $target->isCategoryEnabled($category), ['style' => 'margin:0px;']) ?>
</label>
</td>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
</tbody>
</table>
</div>

View File

@ -53,6 +53,7 @@ humhub.module('post', function(module, require, $) {
};
Post.prototype.toggleCollapse = function() {
debugger;
if(this.$.data('state') === 'collapsed') {
this.expand();
} else {

View File

@ -1,3 +1,5 @@
<span data-ui-widget="post.Post" data-state="collapsed" data-ui-init id="post-content-<?= $post->id; ?>" style="overflow: hidden; margin-bottom: 5px;">
<?= humhub\widgets\RichText::widget(['text' => $post->message, 'record' => $post]) ?>
</span>
<div data-ui-widget="post.Post" data-state="collapsed" data-ui-init id="post-content-<?= $post->id; ?>" style="overflow: hidden; margin-bottom: 5px;">
<div data-ui-markdown>
<?= humhub\widgets\RichText::widget(['text' => $post->message, 'record' => $post]) ?>
</div>
</div>

View File

@ -46,7 +46,7 @@ class SpaceModelMembership extends Behavior
return false;
}
/**
* Checks if a given Userid is allowed to leave this space.
* A User is allowed to leave, if the can_cancel_membership flag in the space_membership table is 1. If it is 2, the decision is delegated to the space.
@ -56,17 +56,17 @@ class SpaceModelMembership extends Behavior
*/
public function canLeave($userId = "")
{
// Take current userid if none is given
if ($userId == "")
$userId = Yii::$app->user->id;
$membership = $this->getMembership($userId);
if ($membership != null && !empty($membership->can_cancel_membership)) {
return $membership->can_cancel_membership === 1 || ($membership->can_cancel_membership === 2 && !empty($this->owner->members_can_leave));
}
}
return false;
}
@ -182,8 +182,9 @@ class SpaceModelMembership extends Behavior
*/
public function getMembership($userId = "")
{
if ($userId == "")
if ($userId == "") {
$userId = Yii::$app->user->id;
}
return Membership::findOne(['user_id' => $userId, 'space_id' => $this->owner->id]);
}
@ -209,7 +210,7 @@ class SpaceModelMembership extends Behavior
$userInvite = Invite::findOne(['email' => $email]);
// No invite yet
if ($userInvite == null) {
if ($userInvite == null) {
// Invite EXTERNAL user
$userInvite = new Invite();
$userInvite->email = $email;
@ -224,12 +225,12 @@ class SpaceModelMembership extends Behavior
$userInvite->user_originator_id = $originatorUserId;
$userInvite->space_invite_id = $this->owner->id;
}
if($userInvite->validate() && $userInvite->save()) {
if ($userInvite->validate() && $userInvite->save()) {
$userInvite->sendInviteMail();
return true;
}
}
return false;
}
@ -279,51 +280,58 @@ class SpaceModelMembership extends Behavior
* If user is applicant approve it.
*
* @param type $userId
* @param type $originatorUserId
* @param type $originatorId
*/
public function inviteMember($userId, $originatorUserId)
public function inviteMember($userId, $originatorId)
{
$membership = $this->getMembership($userId);
if ($membership != null) {
// User is already member
if ($membership->status == Membership::STATUS_MEMBER) {
return;
}
// User requested already membership, just approve him
if ($membership->status == Membership::STATUS_APPLICANT) {
$this->addMember(Yii::$app->user->id);
return;
}
// Already invite, reinvite him
if ($membership->status == Membership::STATUS_INVITED) {
// Remove existing notification
$notification = new \humhub\modules\space\notifications\Invite;
$notification->source = $this->owner;
$notification->delete(User::findOne(['id' => $userId]));
switch ($membership->status) {
case Membership::STATUS_APPLICANT:
// If user is an applicant of this space add user and return.
$this->addMember(Yii::$app->user->id);
case Membership::STATUS_MEMBER:
// If user is already a member just ignore the invitation.
return;
case Membership::STATUS_INVITED:
// If user is already invited, remove old invite notification and retrigger
$oldNotification = new \humhub\modules\space\notifications\Invite(['source' => $this->owner]);
$oldNotification->delete(User::findOne(['id' => $userId]));
break;
}
} else {
$membership = new Membership;
$membership = new Membership([
'space_id' => $this->owner->id,
'user_id' => $userId,
'status' => Membership::STATUS_INVITED,
'group_id' => Space::USERGROUP_MEMBER
]);
}
// Update or set originator
$membership->originator_user_id = $originatorId;
$membership->space_id = $this->owner->id;
$membership->user_id = $userId;
$membership->originator_user_id = $originatorUserId;
$membership->status = Membership::STATUS_INVITED;
$membership->group_id = Space::USERGROUP_MEMBER;
if (!$membership->save()) {
if ($membership->save()) {
$this->sendInviteNotification($userId, $originatorId);
} else {
throw new \yii\base\Exception("Could not save membership!" . print_r($membership->getErrors(), 1));
}
}
/**
* Sends an Invite Notification to the given user.
*
* @param type $userId
* @param type $originatorId
*/
protected function sendInviteNotification($userId, $originatorId)
{
$notification = new \humhub\modules\space\notifications\Invite([
'source' => $this->owner,
'originator' => User::findOne(['id' => $originatorId])
]);
$notification = new \humhub\modules\space\notifications\Invite;
$notification->source = $this->owner;
$notification->originator = User::findOne(['id' => $originatorUserId]);
$notification->send(User::findOne(['id' => $userId]));
}
@ -412,8 +420,9 @@ class SpaceModelMembership extends Behavior
*/
public function removeMember($userId = "")
{
if ($userId == "")
if ($userId == "") {
$userId = Yii::$app->user->id;
}
$user = User::findOne(['id' => $userId]);

View File

@ -157,7 +157,7 @@ class MembershipController extends \humhub\modules\content\components\ContentCon
// Check Permissions to Invite
if (!$space->canInvite()) {
throw new HttpException(403, 'Access denied - You cannot invite members!');
throw new HttpException(403, Yii::t('SpaceModule.controllers_MembershipController', 'Access denied - You cannot invite members!'));
}
$model = new \humhub\modules\space\models\forms\InviteForm();

View File

@ -9,10 +9,7 @@
namespace humhub\modules\space\models;
use Yii;
use humhub\modules\comment\models\Comment;
use humhub\modules\user\models\User;
use humhub\modules\content\models\WallEntry;
use humhub\modules\activity\models\Activity;
/**

View File

@ -29,11 +29,19 @@ class ApprovalRequest extends BaseNotification
* @inheritdoc
*/
public $markAsSeenOnClick = false;
/**
* @inheritdoc
*/
public function category()
{
return new SpaceMemberNotificationCategory;
}
/**
* @inheritdoc
*/
public function getAsHtml()
public function html()
{
return Yii::t('SpaceModule.notification', '{displayName} requests membership for the space {spaceName}', array(
'{displayName}' => Html::tag('strong', Html::encode($this->originator->displayName)),

View File

@ -25,10 +25,18 @@ class ApprovalRequestAccepted extends BaseNotification
*/
public $moduleId = "space";
/**
* @inheritdoc
*/
public function category()
{
return new SpaceMemberNotificationCategory;
}
/**
* @inheritdoc
*/
public function getAsHtml()
public function html()
{
return Yii::t('SpaceModule.notification', '{displayName} approved your membership for the space {spaceName}', array(
'{displayName}' => Html::tag('strong', Html::encode($this->originator->displayName)),

View File

@ -24,11 +24,19 @@ class ApprovalRequestDeclined extends BaseNotification
* @inheritdoc
*/
public $moduleId = "space";
/**
* @inheritdoc
*/
public function category()
{
return new SpaceMemberNotificationCategory;
}
/**
* @inheritdoc
*/
public function getAsHtml()
public function html()
{
return Yii::t('SpaceModule.notification', '{displayName} declined your membership request for the space {spaceName}', array(
'{displayName}' => Html::tag('strong', Html::encode($this->originator->displayName)),

View File

@ -29,11 +29,19 @@ class Invite extends BaseNotification
* @inheritdoc
*/
public $markAsSeenOnClick = false;
/**
* @inheritdoc
*/
public function category()
{
return new SpaceMemberNotificationCategory;
}
/**
* @inheritdoc
*/
public function getAsHtml()
public function html()
{
return Yii::t('SpaceModule.notification', '{displayName} invited you to the space {spaceName}', array(
'{displayName}' => Html::tag('strong', Html::encode($this->originator->displayName)),

View File

@ -26,11 +26,19 @@ class InviteAccepted extends BaseNotification
* @inheritdoc
*/
public $moduleId = "space";
/**
* @inheritdoc
*/
public function category()
{
return new SpaceMemberNotificationCategory;
}
/**
* @inheritdoc
*/
public function getAsHtml()
public function html()
{
return Yii::t('SpaceModule.notification', '{displayName} accepted your invite for the space {spaceName}', array(
'{displayName}' => Html::tag('strong', Html::encode($this->originator->displayName)),

Some files were not shown because too many files have changed in this diff Show More