mirror of
https://github.com/humhub/humhub.git
synced 2025-01-16 21:58:17 +01:00
Richtext JS Api, Disabled Archived Stream, Stream Console Fix
This commit is contained in:
parent
db66b3582c
commit
0cef6f0330
@ -42,7 +42,7 @@
|
||||
"bower-asset/jquery-color": "^2.1.2",
|
||||
"bower-asset/autosize": "1.*",
|
||||
"bower-asset/nprogress": "*",
|
||||
"bower-asset/At.js": "0.5.0",
|
||||
"bower-asset/At.js": "^1.5.1",
|
||||
"bower-asset/animate.css": "*",
|
||||
"bower-asset/html5shiv": "^3.7"
|
||||
},
|
||||
@ -60,12 +60,6 @@
|
||||
"scripts": {
|
||||
"post-create-project-cmd": [
|
||||
"yii\\composer\\Installer::postCreateProject"
|
||||
],
|
||||
"post-install-cmd": [
|
||||
"PhantomInstaller\\Installer::installPhantomJS"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"PhantomInstaller\\Installer::installPhantomJS"
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
|
@ -350,7 +350,7 @@ humhub.module('action', function(module, require, $) {
|
||||
|
||||
// As last resort we try to call the action by namespace for handlers like humhub.modules.myModule.myAction
|
||||
var handlerAction = event.handler.split('.').pop();
|
||||
var target = require(string.cutsuffix(event.handler, '.' + handlerAction));
|
||||
var target = require(string.cutSuffix(event.handler, '.' + handlerAction));
|
||||
|
||||
if(!_executeAction(target, handlerAction, event)) {
|
||||
module.log.error('actionHandlerNotFound', event.handler, true);
|
||||
@ -576,6 +576,7 @@ humhub.module('action', function(module, require, $) {
|
||||
var trigger = function($trigger, type, options) {
|
||||
options.$trigger = $trigger;
|
||||
|
||||
// For manually triggered action events we do not need a block in most cases.
|
||||
if(!options.block) {
|
||||
options.block = BLOCK_NONE;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ humhub.module('client.pjax', function(module, require, $) {
|
||||
|
||||
var init = function() {
|
||||
if (module.config.active) {
|
||||
$(document).pjax('a:not([data-pjax-prevent],[target="_blank"],[data-target])', "#layout-content", module.config.options);
|
||||
$(document).pjax('a:not([data-pjax-prevent],[target],[data-target],[data-toggle])', "#layout-content", module.config.options);
|
||||
pjaxRedirectFix();
|
||||
module.installLoader();
|
||||
}
|
||||
|
158
js/humhub/humhub.live.js
Normal file
158
js/humhub/humhub.live.js
Normal file
@ -0,0 +1,158 @@
|
||||
humhub.module('live', function(module, require, $) {
|
||||
var object = require('util').object;
|
||||
var space = require('space', true);
|
||||
var user = require('user', true);
|
||||
|
||||
var instances = [];
|
||||
|
||||
var _delay = 11;
|
||||
|
||||
var _idleFactor = 0;
|
||||
|
||||
var _defaultOptions = {
|
||||
id: 'unknown',
|
||||
min: 20,
|
||||
max: 45,
|
||||
idle: true,
|
||||
idleFactor: 1,
|
||||
active: true
|
||||
};
|
||||
|
||||
var LiveUpdate = function(topic, options) {
|
||||
this.topic = (arguments.length === 1) ? topic.topic : topic;
|
||||
this.options = (arguments.length === 1) ? topic.options : options;
|
||||
this.options = $.extend(_defaultOptions, this.options);
|
||||
|
||||
this.id = this.options.id;
|
||||
};
|
||||
|
||||
LiveUpdate.prototype.on = function() {
|
||||
this.options.active = true;
|
||||
};
|
||||
|
||||
LiveUpdate.prototype.off = function() {
|
||||
this.options.active = false;
|
||||
};
|
||||
|
||||
LiveUpdate.prototype.isExpired = function() {
|
||||
if(!this.options.active) {
|
||||
return false;
|
||||
} else if(!this.lastUpdate) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var minDelay = this.options.min + (this.options.min * (_idleFactor * this.options.idleFactor));
|
||||
|
||||
if(minDelay > this.options.max) {
|
||||
minDelay = this.options.max;
|
||||
}
|
||||
|
||||
return (this.lastUpdate + minDelay) >= Date.now();
|
||||
};
|
||||
|
||||
LiveUpdate.prototype.handleResult = function(result) {
|
||||
|
||||
}
|
||||
|
||||
LiveUpdate.prototype.validate = function() {
|
||||
return !((this.topic.user && user.isGuest()) || (this.topic.space && !space.guid()));
|
||||
};
|
||||
|
||||
var register = function(topic, options) {
|
||||
var instance = new LiveUpdate(topic, options);
|
||||
instances.push(instance);
|
||||
return instance;
|
||||
};
|
||||
|
||||
var init = function() {
|
||||
setTimeout(_run, _delay);
|
||||
};
|
||||
|
||||
var _run = function() {
|
||||
var topics = [];
|
||||
var lastUpdate = Date.now();
|
||||
instances.forEach(function(update) {
|
||||
if(update.isExpired() && update.validate()) {
|
||||
var topic = _getServerTopic(update);
|
||||
module.log.debug('Topic update:' + update.id, topic);
|
||||
topics.push(topic);
|
||||
update.lastUpdate = lastUpdate;
|
||||
}
|
||||
});
|
||||
|
||||
_send().then(function(result) {
|
||||
instances.forEach(function(liveUpdate) {
|
||||
liveUpdate.handleResult(result);
|
||||
});
|
||||
}).catch(function(err) {
|
||||
// Silent error log
|
||||
module.log.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
var _getServerTopic = function(update) {
|
||||
var result = {};
|
||||
|
||||
if(update.topic.user === true) {
|
||||
result.uguid = user.guid();
|
||||
} else if(update.topic.uguid) {
|
||||
result.uguid = update.topic.uguid;
|
||||
}
|
||||
|
||||
if(update.topic.space) {
|
||||
result.sguid = space.guid();
|
||||
} else if(update.topic.sguid) {
|
||||
result.sguid = update.topic.sguid;
|
||||
}
|
||||
|
||||
if(update.topic.suffix) {
|
||||
result.suffix = update.topic.suffix;
|
||||
}
|
||||
|
||||
if(update.topic.module) {
|
||||
result.module = update.topic.module;
|
||||
}
|
||||
|
||||
if(update.lastResult) {
|
||||
result.last = update.lastResult;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
var update = new LiveUpdate({
|
||||
topic: {
|
||||
'user': true,
|
||||
'space': true,
|
||||
'module': 'mail',
|
||||
'suffix': 'whatever'
|
||||
},
|
||||
options: {
|
||||
min: 10,
|
||||
max: 30,
|
||||
idle: true,
|
||||
idleFactor: 1
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
LiveUpdate
|
||||
Settings
|
||||
LastUpdate -> Date this specific update was requested the last time.
|
||||
(Register To) --> function or static object
|
||||
- User
|
||||
- Space
|
||||
- Module
|
||||
(Live Settings)
|
||||
- Min Duration
|
||||
- Idle Factor
|
||||
(Callback)
|
||||
- Callback
|
||||
|
||||
-On/Off
|
||||
*/
|
||||
module.export({
|
||||
init: init,
|
||||
register: register
|
||||
});
|
||||
});
|
@ -15,13 +15,13 @@
|
||||
* @param {type} param1
|
||||
* @param {type} param2
|
||||
*/
|
||||
humhub.module('ui.modal', function (module, require, $) {
|
||||
humhub.module('ui.modal', function(module, require, $) {
|
||||
var util = require('util');
|
||||
var object = util.object;
|
||||
var string = util.string;
|
||||
var additions = require('ui.additions');
|
||||
var loader = require('ui.loader');
|
||||
var client = require('client', true);
|
||||
var Widget = require('ui.widget').Widget;
|
||||
|
||||
//Keeps track of all initialized modals
|
||||
var modals = [];
|
||||
@ -36,22 +36,25 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
*
|
||||
* @param {string} id - id of the modal
|
||||
*/
|
||||
var Modal = function (id, options) {
|
||||
options = options || {};
|
||||
var selector = string.startsWith(id, '#') ? id : '#' + id;
|
||||
this.$ = $(selector);
|
||||
var Modal = function(node, options) {
|
||||
Widget.call(this, node, options);
|
||||
};
|
||||
|
||||
if (!this.$.length) {
|
||||
object.inherits(Modal, Widget)
|
||||
|
||||
Modal.component = 'humhub-ui-modal';
|
||||
;
|
||||
|
||||
Modal.prototype.init = function() {
|
||||
if(!this.$.length) {
|
||||
this.createModal(id);
|
||||
this.reset();
|
||||
}
|
||||
|
||||
this.initModal(options);
|
||||
this.initModal(this.options);
|
||||
modals.push(this);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Template for the modal splitted into different parts. Those can be overwritten my changing or overwriting module.template.
|
||||
*/
|
||||
@ -60,19 +63,19 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
header: '<div class="modal-header"><button type="button" class="close" data-modal-close="true" aria-hidden="true">×</button><h4 class="modal-title"></h4></div>',
|
||||
body: '<div class="modal-body"></div>',
|
||||
footer: '<div class="modal-footer"></div>',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new modal dom skeleton.
|
||||
* @param {type} id the modal id
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.createModal = function (id) {
|
||||
Modal.prototype.createModal = function(id) {
|
||||
this.$ = $(this.getTemplate('container')).attr('id', id);
|
||||
$('body').append(this.$);
|
||||
};
|
||||
|
||||
Modal.prototype.getTemplate = function (id) {
|
||||
|
||||
Modal.prototype.getTemplate = function(id) {
|
||||
return Modal.template[id];
|
||||
};
|
||||
|
||||
@ -80,13 +83,13 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* Initializes default modal events and sets initial data.
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.initModal = function (options) {
|
||||
Modal.prototype.initModal = function(options) {
|
||||
var that = this;
|
||||
|
||||
//Set default modal manipulation event handlers
|
||||
this.$.on('click', '[data-modal-close]', function () {
|
||||
this.$.on('click', '[data-modal-close]', function() {
|
||||
that.close();
|
||||
}).on('click', '[data-modal-clear-error]', function () {
|
||||
}).on('click', '[data-modal-clear-error]', function() {
|
||||
that.clearErrorMessage();
|
||||
});
|
||||
|
||||
@ -95,7 +98,7 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
this.$.attr('aria-labelledby', this.getTitleId());
|
||||
};
|
||||
|
||||
Modal.prototype.getTitleId = function () {
|
||||
Modal.prototype.getTitleId = function() {
|
||||
return this.$.attr('id') + '-title';
|
||||
};
|
||||
|
||||
@ -103,11 +106,11 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* Closes the modal with fade animation and sets the loader content
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.close = function (reset) {
|
||||
Modal.prototype.close = function(reset) {
|
||||
var that = this;
|
||||
this.$.fadeOut('fast', function () {
|
||||
this.$.fadeOut('fast', function() {
|
||||
that.$.modal('hide');
|
||||
if (reset) {
|
||||
if(reset) {
|
||||
that.reset();
|
||||
}
|
||||
});
|
||||
@ -117,7 +120,7 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* Sets the loader content and shows the modal
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.loader = function () {
|
||||
Modal.prototype.loader = function() {
|
||||
this.reset();
|
||||
this.show();
|
||||
};
|
||||
@ -126,7 +129,7 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* Sets the default content (a loader animation)
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.reset = function () {
|
||||
Modal.prototype.reset = function() {
|
||||
// Clear old script tags.
|
||||
var $content = this.getContent().empty();
|
||||
this.$.find('script').remove();
|
||||
@ -141,18 +144,18 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* @param {function} callback - callback function is called after html was inserted
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.setContent = function (content, callback) {
|
||||
Modal.prototype.setContent = function(content, callback) {
|
||||
var that = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
// TODO: assure content order header/content/footer
|
||||
try {
|
||||
that.clearErrorMessage();
|
||||
that.getContent().html(content).promise().always(function () {
|
||||
that.getContent().html(content).promise().always(function() {
|
||||
that.applyAdditions();
|
||||
});
|
||||
that.isFilled = true;
|
||||
resolve(that);
|
||||
} catch (err) {
|
||||
} catch(err) {
|
||||
that.setErrorMessage(err.message);
|
||||
// We try to apply additions anyway
|
||||
that.applyAdditions();
|
||||
@ -161,15 +164,32 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
});
|
||||
};
|
||||
|
||||
Modal.prototype.applyAdditions = function () {
|
||||
Modal.prototype.applyAdditions = function() {
|
||||
additions.applyTo(this.getContent());
|
||||
};
|
||||
|
||||
Modal.prototype.load = function (url, cfg, originalEvent) {
|
||||
Modal.prototype.load = function(url, cfg, originalEvent) {
|
||||
var that = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
that.loader();
|
||||
client.html(url, cfg, originalEvent).then(function (response) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if(!that.isVisible()) {
|
||||
that.loader();
|
||||
}
|
||||
client.html(url, cfg, originalEvent).then(function(response) {
|
||||
that.setDialog(response.html);
|
||||
resolve(response);
|
||||
}).catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
Modal.prototype.post = function(url, cfg, originalEvent) {
|
||||
var that = this;
|
||||
var cfg = cfg || {};
|
||||
cfg.dataType = cfg.dataType || 'html';
|
||||
return new Promise(function(resolve, reject) {
|
||||
if(!that.isVisible()) {
|
||||
that.loader();
|
||||
}
|
||||
client.post(url, cfg, originalEvent).then(function(response) {
|
||||
that.setDialog(response.html);
|
||||
resolve(response);
|
||||
}).catch(reject);
|
||||
@ -184,9 +204,9 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* @param {type} message
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.error = function (title, message) {
|
||||
Modal.prototype.error = function(title, message) {
|
||||
|
||||
if (arguments.length === 1 && title) {
|
||||
if(arguments.length === 1 && title) {
|
||||
message = (title.getFirstError) ? title.getFirstError() : title;
|
||||
title = (title.getErrorTitle) ? title.getErrorTitle() : ERROR_DEFAULT_TITLE;
|
||||
}
|
||||
@ -195,7 +215,7 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
message = message || ERROR_DEFAULT_MESSAGE;
|
||||
|
||||
//If there is no content yet we create an error only content
|
||||
if (!this.isFilled) {
|
||||
if(!this.isFilled) {
|
||||
this.clear();
|
||||
this.setHeader(title);
|
||||
this.setBody('');
|
||||
@ -211,10 +231,10 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* Removes existing error messages
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.clearErrorMessage = function () {
|
||||
Modal.prototype.clearErrorMessage = function() {
|
||||
var modalError = this.getErrorMessage();
|
||||
if (modalError.length) {
|
||||
modalError.fadeOut('fast', function () {
|
||||
if(modalError.length) {
|
||||
modalError.fadeOut('fast', function() {
|
||||
modalError.remove();
|
||||
});
|
||||
}
|
||||
@ -225,9 +245,9 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* @param {type} message
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.setErrorMessage = function (message) {
|
||||
Modal.prototype.setErrorMessage = function(message) {
|
||||
var $errorMessage = this.getErrorMessage();
|
||||
if ($errorMessage.length) {
|
||||
if($errorMessage.length) {
|
||||
$errorMessage.css('opacity', 0);
|
||||
$errorMessage.text(message);
|
||||
$errorMessage.animate({'opacity': 1}, 'fast');
|
||||
@ -240,7 +260,7 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* Returns the current errormessagebox
|
||||
* @returns {humhub.ui.modal_L18.Modal.prototype@call;getContent@call;find}
|
||||
*/
|
||||
Modal.prototype.getErrorMessage = function () {
|
||||
Modal.prototype.getErrorMessage = function() {
|
||||
return this.getContent().find('.modal-error');
|
||||
};
|
||||
|
||||
@ -248,9 +268,9 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* Shows the modal
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.show = function () {
|
||||
if (!this.$.is(':visible')) {
|
||||
if (!this.$.data('bs.modal')) {
|
||||
Modal.prototype.show = function() {
|
||||
if(!this.$.is(':visible')) {
|
||||
if(!this.$.data('bs.modal')) {
|
||||
this.$.modal(this.options);
|
||||
} else {
|
||||
this.$.modal('show');
|
||||
@ -262,7 +282,7 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* Clears the modal content
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.clear = function () {
|
||||
Modal.prototype.clear = function() {
|
||||
this.getContent().empty();
|
||||
};
|
||||
|
||||
@ -270,7 +290,7 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* Retrieves the modal content jQuery representation
|
||||
* @returns {humhub.ui.modal_L18.Modal.prototype@pro;$modal@call;find}
|
||||
*/
|
||||
Modal.prototype.getContent = function () {
|
||||
Modal.prototype.getContent = function() {
|
||||
//We use the :first selector since jQuery refused to execute javascript if we set content with inline js
|
||||
return this.$.find('.modal-content:first');
|
||||
};
|
||||
@ -279,7 +299,7 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* Retrieves the modal dialog jQuery representation
|
||||
* @returns {humhub.ui.modal_L18.Modal.prototype@pro;$modal@call;find}
|
||||
*/
|
||||
Modal.prototype.getDialog = function () {
|
||||
Modal.prototype.getDialog = function() {
|
||||
return this.$.find('.modal-dialog');
|
||||
};
|
||||
|
||||
@ -287,7 +307,7 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* Returns the modal footer
|
||||
* @returns {humhub.ui.modal_L18.Modal.prototype@pro;$modal@call;find}
|
||||
*/
|
||||
Modal.prototype.getFooter = function () {
|
||||
Modal.prototype.getFooter = function() {
|
||||
return this.$.find('.modal-footer');
|
||||
};
|
||||
|
||||
@ -295,7 +315,7 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* Searches for forms within the modal
|
||||
* @returns {humhub.ui.modal_L18.Modal.prototype@pro;$modal@call;find}
|
||||
*/
|
||||
Modal.prototype.getForm = function () {
|
||||
Modal.prototype.getForm = function() {
|
||||
return this.$.find('form');
|
||||
};
|
||||
|
||||
@ -304,9 +324,9 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* @param {type} title
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.setHeader = function (title) {
|
||||
Modal.prototype.setHeader = function(title) {
|
||||
var $header = this.getHeader();
|
||||
if (!$header.length) {
|
||||
if(!$header.length) {
|
||||
$header = $(this.getTemplate('header'));
|
||||
this.getContent().prepend($header);
|
||||
}
|
||||
@ -315,9 +335,9 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
$header.find('.modal-title').attr('id', this.getTitleId()).html(title);
|
||||
};
|
||||
|
||||
Modal.prototype.setFooter = function (footer) {
|
||||
Modal.prototype.setFooter = function(footer) {
|
||||
var $footer = this.getFooter();
|
||||
if (!$footer.length) {
|
||||
if(!$footer.length) {
|
||||
$footer = $(this.getTemplate('footer'));
|
||||
this.getContent().append($footer);
|
||||
}
|
||||
@ -325,29 +345,29 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
$footer.html(footer);
|
||||
};
|
||||
|
||||
Modal.prototype.set = function (options) {
|
||||
Modal.prototype.set = function(options) {
|
||||
this.options = options;
|
||||
|
||||
if (this.options.header) {
|
||||
if(this.options.header) {
|
||||
this.setHeader(this.options.header);
|
||||
}
|
||||
|
||||
if (this.options.body) {
|
||||
if(this.options.body) {
|
||||
this.setBody(this.options.body);
|
||||
}
|
||||
|
||||
if (this.options.content) {
|
||||
if(this.options.content) {
|
||||
this.setContent(this.options.content);
|
||||
}
|
||||
|
||||
if (this.options.footer) {
|
||||
if(this.options.footer) {
|
||||
this.setFooter(this.options.footer);
|
||||
}
|
||||
|
||||
|
||||
this.options.backdrop = options.backdrop || true;
|
||||
this.options.keyboard = options.keyboard || true;
|
||||
|
||||
if (this.$.data('bs.modal')) {
|
||||
if(this.$.data('bs.modal')) {
|
||||
this.$.data('bs.modal').options = this.options;
|
||||
}
|
||||
|
||||
@ -358,7 +378,7 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* Retrieves the modal-header element
|
||||
* @returns {humhub.ui.modal_L18.Modal.prototype@pro;$modal@call;find}
|
||||
*/
|
||||
Modal.prototype.getHeader = function () {
|
||||
Modal.prototype.getHeader = function() {
|
||||
return this.$.find('.modal-header');
|
||||
};
|
||||
|
||||
@ -367,16 +387,16 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* @param {type} content
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.setBody = function (content) {
|
||||
Modal.prototype.setBody = function(content) {
|
||||
var $body = this.getBody();
|
||||
if (!$body.length) {
|
||||
if(!$body.length) {
|
||||
this.setContent($(this.getTemplate('body')));
|
||||
$body = this.getBody();
|
||||
}
|
||||
$body.html(content);
|
||||
};
|
||||
|
||||
Modal.prototype.setDialog = function (content) {
|
||||
Modal.prototype.setDialog = function(content) {
|
||||
this.$.empty().append(content);
|
||||
this.$.find('input[type="text"]:visible, textarea:visible, [contenteditable="true"]:visible').first().focus();
|
||||
this.applyAdditions();
|
||||
@ -387,19 +407,19 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
* Retrieves the modal-body element
|
||||
* @returns {humhub.ui.modal_L18.Modal.prototype@pro;$modal@call;find}
|
||||
*/
|
||||
Modal.prototype.getBody = function () {
|
||||
Modal.prototype.getBody = function() {
|
||||
return this.$.find('.modal-body');
|
||||
};
|
||||
|
||||
var ConfirmModal = function (id) {
|
||||
Modal.call(this, id);
|
||||
var ConfirmModal = function(node, options) {
|
||||
Modal.call(this, node, options);
|
||||
};
|
||||
|
||||
object.inherits(ConfirmModal, Modal);
|
||||
|
||||
ConfirmModal.prototype.open = function (cfg) {
|
||||
ConfirmModal.prototype.open = function(cfg) {
|
||||
var that = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
cfg = cfg || {};
|
||||
|
||||
cfg.handler = resolve;
|
||||
@ -417,12 +437,12 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
});
|
||||
};
|
||||
|
||||
ConfirmModal.prototype.clear = function (cfg) {
|
||||
ConfirmModal.prototype.clear = function(cfg) {
|
||||
this.$.find('[data-modal-confirm]').off('click');
|
||||
this.$.find('[data-modal-cancel]').off('click');
|
||||
};
|
||||
|
||||
ConfirmModal.prototype.initButtons = function (cfg) {
|
||||
ConfirmModal.prototype.initButtons = function(cfg) {
|
||||
//Set button text
|
||||
var $cancelButton = this.$.find('[data-modal-cancel]');
|
||||
$cancelButton.text(cfg['cancelText']);
|
||||
@ -432,97 +452,97 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
|
||||
//Init handler
|
||||
var that = this;
|
||||
if (cfg['handler']) {
|
||||
$confirmButton.one('click', function (evt) {
|
||||
if(cfg['handler']) {
|
||||
$confirmButton.one('click', function(evt) {
|
||||
that.clear();
|
||||
cfg['handler'](true);
|
||||
});
|
||||
}
|
||||
|
||||
if (cfg['handler']) {
|
||||
$cancelButton.one('click', function (evt) {
|
||||
if(cfg['handler']) {
|
||||
$cancelButton.one('click', function(evt) {
|
||||
that.clear();
|
||||
cfg['handler'](false);
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var init = function () {
|
||||
module.global = new Modal('#globalModal');
|
||||
module.global.$.on('hidden.bs.modal', function (e) {
|
||||
var init = function() {
|
||||
module.global = Modal.instance('#globalModal');
|
||||
module.global.$.on('hidden.bs.modal', function(e) {
|
||||
module.global.reset();
|
||||
});
|
||||
|
||||
module.globalConfirm = new ConfirmModal('#globalModalConfirm');
|
||||
module.confirm = function (cfg) {
|
||||
module.globalConfirm = ConfirmModal.instance('#globalModalConfirm');
|
||||
module.confirm = function(cfg) {
|
||||
return module.globalConfirm.open(cfg);
|
||||
};
|
||||
|
||||
_setModalEnforceFocus();
|
||||
_setGlobalModalTargetHandler();
|
||||
|
||||
$(document).on('show.bs.modal', '.modal', function (event) {
|
||||
$(document).on('show.bs.modal', '.modal', function(event) {
|
||||
$(this).appendTo($('body'));
|
||||
});
|
||||
|
||||
$(document).on('shown.bs.modal', '.modal.in', function (event) {
|
||||
$(document).on('shown.bs.modal', '.modal.in', function(event) {
|
||||
_setModalsAndBackdropsOrder();
|
||||
});
|
||||
|
||||
$(document).on('hidden.bs.modal', '.modal', function (event) {
|
||||
$(document).on('hidden.bs.modal', '.modal', function(event) {
|
||||
_setModalsAndBackdropsOrder();
|
||||
});
|
||||
};
|
||||
|
||||
var _setModalsAndBackdropsOrder = function () {
|
||||
var _setModalsAndBackdropsOrder = function() {
|
||||
var modalZIndex = 1040;
|
||||
$('.modal.in').each(function (index) {
|
||||
$('.modal.in').each(function(index) {
|
||||
var $modal = $(this);
|
||||
modalZIndex++;
|
||||
$modal.css('zIndex', modalZIndex);
|
||||
$modal.next('.modal-backdrop.in').addClass('hidden').css('zIndex', modalZIndex - 1);
|
||||
});
|
||||
$('.modal.in:visible:last').focus().next('.modal-backdrop.in').removeClass('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* To allow other frameworks to overlay focusable nodes over an active modal we have
|
||||
* to explicitly allow ith within this overwritten function.
|
||||
*
|
||||
*/
|
||||
var _setModalEnforceFocus = function () {
|
||||
$.fn.modal.Constructor.prototype.enforceFocus = function () {
|
||||
var _setModalEnforceFocus = function() {
|
||||
$.fn.modal.Constructor.prototype.enforceFocus = function() {
|
||||
var that = this;
|
||||
$(document).on('focusin.modal', function (e) {
|
||||
$(document).on('focusin.modal', function(e) {
|
||||
var $target = $(e.target);
|
||||
if ($target.hasClass('select2-input') || $target.hasClass('select2-search__field') || $target.hasClass('hexInput')) {
|
||||
if($target.hasClass('select2-input') || $target.hasClass('select2-search__field') || $target.hasClass('hexInput')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var $parent = $(e.target.parentNode);
|
||||
if ($parent.hasClass('cke_dialog_ui_input_select') || $parent.hasClass('cke_dialog_ui_input_text')) {
|
||||
if($parent.hasClass('cke_dialog_ui_input_select') || $parent.hasClass('cke_dialog_ui_input_text')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow stacking of modals
|
||||
if ($target.closest('.modal.in').length) {
|
||||
if($target.closest('.modal.in').length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (that.$element[0] !== e.target && !that.$element.has(e.target).length) {
|
||||
if(that.$element[0] !== e.target && !that.$element.has(e.target).length) {
|
||||
that.$element.focus();
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
var _setGlobalModalTargetHandler = function () {
|
||||
var _setGlobalModalTargetHandler = function() {
|
||||
|
||||
// unbind all previously-attached events
|
||||
$("a[data-target='#globalModal']").off('.humhub:globalModal');
|
||||
|
||||
// deprecated use action handler instead @see get action
|
||||
$(document).off('click.humhub:globalModal').on('click.humhub:globalModal', "a[data-target='#globalModal']", function (evt) {
|
||||
$(document).off('click.humhub:globalModal').on('click.humhub:globalModal', "a[data-target='#globalModal']", function(evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
var options = {
|
||||
@ -535,52 +555,60 @@ humhub.module('ui.modal', function (module, require, $) {
|
||||
|
||||
var target = $(this).attr("href");
|
||||
|
||||
client.html(target).then(function (response) {
|
||||
client.html(target).then(function(response) {
|
||||
module.global.setDialog(response.html);
|
||||
if (!module.global.$.is(':visible')) {
|
||||
if(!module.global.$.is(':visible')) {
|
||||
module.global.show();
|
||||
}
|
||||
}).catch(function (error) {
|
||||
}).catch(function(error) {
|
||||
module.log.error(error, true);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var submit = function (evt) {
|
||||
var submit = function(evt) {
|
||||
evt.$form = evt.$form || evt.$trigger.closest('form');
|
||||
|
||||
|
||||
if(!evt.$form.length) {
|
||||
evt.$form = evt.$target;
|
||||
}
|
||||
|
||||
client.submit(evt, {'dataType': 'html'}).then(function (response) {
|
||||
|
||||
client.submit(evt, {'dataType': 'html'}).then(function(response) {
|
||||
module.global.setDialog(response.html);
|
||||
if (!module.global.$.is(':visible')) {
|
||||
if(!module.global.$.is(':visible')) {
|
||||
module.global.show();
|
||||
}
|
||||
}).catch(function (error) {
|
||||
}).catch(function(error) {
|
||||
module.log.error(error, true);
|
||||
});
|
||||
};
|
||||
|
||||
var load = function (evt) {
|
||||
module.global.load(evt).catch(function (err) {
|
||||
var load = function(evt) {
|
||||
module.global.load(evt).catch(function(err) {
|
||||
module.log.error(err, true);
|
||||
});
|
||||
};
|
||||
|
||||
var get = function (id, options) {
|
||||
var get = function(id, options) {
|
||||
var modal = !(modals[id]) ? new Modal(id) : modals[id];
|
||||
if (options) {
|
||||
if(options) {
|
||||
modal.set(options);
|
||||
}
|
||||
return modal;
|
||||
};
|
||||
|
||||
var post = function(evt) {
|
||||
module.global.post(evt).catch(function(err) {
|
||||
module.log.error(err, true);
|
||||
});
|
||||
};
|
||||
|
||||
module.export({
|
||||
init: init,
|
||||
Modal: Modal,
|
||||
ConfirmModal: ConfirmModal,
|
||||
get: get,
|
||||
post: post,
|
||||
load: load,
|
||||
submit: submit
|
||||
});
|
||||
|
@ -218,7 +218,7 @@ humhub.module('ui.picker', function(module, require, $) {
|
||||
selectionWithImage: '{imageNode}<span class="with-image">{text}</span> <i class="fa fa-times-circle picker-close"></i>',
|
||||
selectionNoImage: '<span class="no-image">{text}</span> <i class="fa fa-times-circle picker-close"></i>',
|
||||
result: '<a href="#" tabindex="-1" style="margin-right:5px;">{imageNode} {text}</a>',
|
||||
resultDisabled: '<a href="#" title="{title}" data-placement="right" tabindex="-1" style="margin-right:5px;opacity: 0.4;cursor:not-allowed">{imageNode} {text}</a>',
|
||||
resultDisabled: '<a href="#" title="{disabledText}" data-placement="right" tabindex="-1" style="margin-right:5px;opacity: 0.4;cursor:not-allowed">{imageNode} {text}</a>',
|
||||
imageNode: '<img class="img-rounded" src="{image}" alt="24x24" style="width:24px;height:24px;" height="24" width="24">',
|
||||
option: '<option value="{id}" data-image="{image}" selected>{text}</option>',
|
||||
};
|
||||
@ -236,7 +236,7 @@ humhub.module('ui.picker', function(module, require, $) {
|
||||
}
|
||||
|
||||
item.imageNode = this.getImageNode(item);
|
||||
|
||||
item.disabledText = item.disabledText || '';
|
||||
var template = (item.disabled) ? Picker.template.resultDisabled : Picker.template.result;
|
||||
|
||||
var $result = $(string.template(template, item))
|
||||
|
@ -45,7 +45,7 @@ humhub.module('ui.progress', function(module, require, $) {
|
||||
|
||||
Progress.prototype.value = function() {
|
||||
var width = this.$progressBar[0].style.width;
|
||||
return (width) ? parseInt(string.cutsuffix(width, '%')) : 0;
|
||||
return (width) ? parseInt(string.cutSuffix(width, '%')) : 0;
|
||||
};
|
||||
|
||||
Progress.prototype.reset = function() {
|
||||
|
445
js/humhub/humhub.ui.richtext.js
Normal file
445
js/humhub/humhub.ui.richtext.js
Normal file
@ -0,0 +1,445 @@
|
||||
/**
|
||||
* Manages the client/server communication. Handles humhub json api responses and
|
||||
* pjax requests.
|
||||
*/
|
||||
humhub.module('ui.richtext', function(module, require, $) {
|
||||
|
||||
var Widget = require('ui.widget').Widget;
|
||||
var util = require('util');
|
||||
var object = util.object;
|
||||
var string = util.string;
|
||||
|
||||
var Richtext = function(node, options) {
|
||||
Widget.call(this, node, options);
|
||||
};
|
||||
|
||||
object.inherits(Richtext, Widget);
|
||||
|
||||
Richtext.component = 'humhub-ui-richtext';
|
||||
|
||||
Richtext.prototype.init = function() {
|
||||
this.$input = $('#' + this.$.attr('id') + '_input').hide();
|
||||
this.features = [];
|
||||
this.checkPlaceholder();
|
||||
this.initEvents();
|
||||
if(this.options.disabled) {
|
||||
this.disable();
|
||||
}
|
||||
};
|
||||
|
||||
Richtext.prototype.initEvents = function() {
|
||||
var that = this;
|
||||
this.$.on('focus', function() {
|
||||
that.checkPlaceholder(true);
|
||||
// Initialize features on first focus.
|
||||
if(!that.featuresInitialized) {
|
||||
that.initFeatures();
|
||||
}
|
||||
}).on('focusout', function() {
|
||||
that.update();
|
||||
that.checkPlaceholder();
|
||||
}).on('paste', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
var text = "";
|
||||
if(event.originalEvent.clipboardData) { //Forefox, Webkit
|
||||
text = event.originalEvent.clipboardData.getData('text/plain');
|
||||
} else if(window.clipboardData) { // IE
|
||||
text = window.clipboardData.getData("Text");
|
||||
}
|
||||
|
||||
that.insertTextAtCursor(text);
|
||||
}).on('keydown', function(e) {
|
||||
that.checkForEmptySpans();
|
||||
}).on('keypress', function(e) {
|
||||
switch(e.which) {
|
||||
case 8: // Backspace
|
||||
// Note chrome won't fire the backspace keypress event, but we don't need the workaround for chrome so its ok..
|
||||
that.checkLinkRemoval();
|
||||
break;
|
||||
}
|
||||
}).on('clear', function(e) {
|
||||
that.clear();
|
||||
}).on('disable', function(e) {
|
||||
that.disable();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a workaround for deleting links as a whole in firefox https://bugzilla.mozilla.org/show_bug.cgi?id=685445
|
||||
*/
|
||||
Richtext.prototype.checkLinkRemoval = function() {
|
||||
var position = this.$.caret('offset');
|
||||
|
||||
if(!position) {
|
||||
return;
|
||||
}
|
||||
|
||||
var that = this;
|
||||
// Check if the caret position is right before a link, if yes remove link and perhaps also the parent if empty.
|
||||
this.$.find('.richtext-link, .atwho-emoji').each(function() {
|
||||
var $this = $(this);
|
||||
var offset = $this.offset();
|
||||
var right = offset.left + $this.outerWidth(true);
|
||||
|
||||
// The caret top position seems a bit out in some cases...
|
||||
if(Math.abs(position.left - right) < 1 && Math.abs(position.top - offset.top) < 18) {
|
||||
$this.remove();
|
||||
|
||||
// This is a workaround for a caret position issue in firefox
|
||||
_checkCaretPositionAfterRemove(that.$);
|
||||
return false; // leave loop
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* This will prevent an caret glitch in firefox https://bugzilla.mozilla.org/show_bug.cgi?id=904846
|
||||
* @param {type} $node
|
||||
* @returns {undefined}
|
||||
*/
|
||||
var _checkCaretPositionAfterRemove = function($node) {
|
||||
if(!$node.text().length) {
|
||||
var spaceText = document.createTextNode("\u00a0");
|
||||
$node.prepend(spaceText);
|
||||
|
||||
var sel = window.getSelection();
|
||||
var range = document.createRange();
|
||||
|
||||
range.setStart(spaceText, 0);
|
||||
range.collapse(true);
|
||||
|
||||
sel.removeAllRanges();
|
||||
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
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Richtext.prototype.checkForEmptySpans = function($node) {
|
||||
$node = $node || this.$;
|
||||
$node.find('span').each(function() {
|
||||
_checkEmptySpan($(this));
|
||||
});
|
||||
};
|
||||
|
||||
var _checkEmptySpan = function($node) {
|
||||
if($node.is('span') && !$node.contents().length) {
|
||||
var $parent = $node.parent();
|
||||
$node.remove();
|
||||
_checkEmptySpan($parent);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Inserts the given text at the current cursor position.
|
||||
* This function gets sure not to insert unwanted html by escaping html chars.
|
||||
*
|
||||
* @param {type} text
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Richtext.prototype.insertTextAtCursor = function(text) {
|
||||
// Get rid of unwanted html
|
||||
var text = $('<div></div>').append(string.escapeHtml(text)).text();
|
||||
|
||||
var lastNode;
|
||||
var sel = window.getSelection();
|
||||
|
||||
// Clear current selection, we'll overwrite selected text
|
||||
var range = sel.getRangeAt(0);
|
||||
range.deleteContents();
|
||||
|
||||
// Remove leading line-breaks and spaces
|
||||
text = text.replace(/^(?:\r\n|\r|\n)/g, '').trim();
|
||||
|
||||
// We insert the lines reversed since we don't have to align the range
|
||||
var lines = text.split(/(?:\r\n|\r|\n)/g).reverse();
|
||||
|
||||
$.each(lines, function(i, line) {
|
||||
// Prevent break after last line
|
||||
if(i !== 0) {
|
||||
var br = document.createElement("br");
|
||||
range.insertNode(br);
|
||||
}
|
||||
|
||||
// Insert actual text node
|
||||
var newNode = document.createTextNode(line.trim());
|
||||
range.insertNode(newNode);
|
||||
|
||||
//Insert leading spaces as textnodes
|
||||
var leadingSpaces = line.match(/^\s+/);
|
||||
if(leadingSpaces) {
|
||||
var spaceCount = leadingSpaces[0].length;
|
||||
while(spaceCount > 0) {
|
||||
var spaceNode = document.createTextNode("\u00a0");
|
||||
range.insertNode(spaceNode);
|
||||
spaceCount--;
|
||||
}
|
||||
}
|
||||
|
||||
// The last node of the loop is the first node in dom since we insert reversed
|
||||
if(i === 0) {
|
||||
lastNode = newNode;
|
||||
}
|
||||
});
|
||||
|
||||
// Align range after insertion
|
||||
range.setStartAfter(lastNode);
|
||||
range.setEndAfter(lastNode);
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
};
|
||||
|
||||
Richtext.prototype.update = function() {
|
||||
this.$input.val(this.getPlainText());
|
||||
};
|
||||
|
||||
Richtext.prototype.checkPlaceholder = function(focus) {
|
||||
if(!focus && !this.$.text().trim().length) {
|
||||
this.$.addClass('atwho-placeholder');
|
||||
this.$.html(this.options.placeholder);
|
||||
this.$.attr('spellcheck', 'false');
|
||||
} else if(this.$.hasClass('atwho-placeholder')) {
|
||||
this.$.removeClass('atwho-placeholder');
|
||||
this.$.attr('spellcheck', 'true');
|
||||
this.$.html('');
|
||||
}
|
||||
};
|
||||
|
||||
Richtext.prototype.initFeatures = function() {
|
||||
var that = this;
|
||||
$.each(Richtext.features, function(id, feature) {
|
||||
if(!that.isFeatureEnabled(id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(feature.atwho) {
|
||||
that.initAtwhoFeature(id, feature);
|
||||
}
|
||||
});
|
||||
|
||||
// It seems atwho detatches the original element so we have to do a requery
|
||||
this.$ = $('#' + this.$.attr('id'));
|
||||
this.featuresInitialized = true;
|
||||
};
|
||||
|
||||
Richtext.prototype.isFeatureEnabled = function(id) {
|
||||
if(this.options.excludes && this.options.excludes.indexOf(id) >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(this.options.includes && this.options.includes.length && this.options.includes.indexOf(id) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
Richtext.prototype.initAtwhoFeature = function(id, feature) {
|
||||
var options = (object.isFunction(feature.atwho))
|
||||
? feature.atwho.call(this, feature)
|
||||
: $.extend({}, feature.atwho);
|
||||
|
||||
if(object.isFunction(feature.init)) {
|
||||
feature.init.call(this, feature, options);
|
||||
}
|
||||
|
||||
if(feature.atwho) {
|
||||
this.$.atwho(options);
|
||||
}
|
||||
|
||||
this.features.push(id);
|
||||
};
|
||||
|
||||
Richtext.prototype.disable = function(tooltip) {
|
||||
tooltip = tooltip || this.options.disabledText;
|
||||
this.$.removeAttr('contenteditable').attr({
|
||||
disabled: 'disabled',
|
||||
title : tooltip,
|
||||
}).tooltip({
|
||||
placement : 'bottom'
|
||||
});
|
||||
};
|
||||
|
||||
Richtext.prototype.clear = function() {
|
||||
this.$.html('');
|
||||
this.checkPlaceholder();
|
||||
};
|
||||
|
||||
Richtext.prototype.focus = function() {
|
||||
this.$.trigger('focus');
|
||||
};
|
||||
|
||||
Richtext.prototype.getFeatures = function() {
|
||||
var result = [];
|
||||
$.each(this.features, function(i, id) {
|
||||
result.push(Richtext.features[id]);
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
Richtext.prototype.getPlainText = function() {
|
||||
// GENERATE USER GUIDS
|
||||
var that = this;
|
||||
|
||||
var $clone = this.$.clone();
|
||||
$.each(this.getFeatures(), function(id, feature) {
|
||||
if(object.isFunction(feature.parse)) {
|
||||
feature.parse($clone, that, feature);
|
||||
}
|
||||
});
|
||||
|
||||
var html = $clone.html();
|
||||
|
||||
// replace html space
|
||||
html = html.replace(/\ /g, ' ');
|
||||
|
||||
// rebuild tag structure for webkit browsers
|
||||
html = html.replace(/\<div>\s*<br\s*\\*>\<\/div>/g, '<div></div>');
|
||||
|
||||
// replace all div tags with br tags (webkit)
|
||||
html = html.replace(/\<div>/g, '<br>');
|
||||
|
||||
// replace all p tags with br tags (IE)
|
||||
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,'');
|
||||
|
||||
// replace all <br> with new line break
|
||||
$clone.html(html.replace(/\<br\s*\>/g, '\n'));
|
||||
|
||||
// return plain text without html tags
|
||||
return $clone.text().trim();
|
||||
};
|
||||
|
||||
Richtext.features = {};
|
||||
|
||||
Richtext.features.emoji = {
|
||||
'emojis': [
|
||||
"Relaxed", "Yum", "Relieved", "Hearteyes", "Cool", "Smirk",
|
||||
"KissingClosedEyes", "StuckOutTongue", "StuckOutTongueWinkingEye", "StuckOutTongueClosedEyes", "Disappointed", "Frown",
|
||||
"ColdSweat", "TiredFace", "Grin", "Sob", "Gasp", "Gasp2",
|
||||
"Laughing", "Joy", "Sweet", "Satisfied", "Innocent", "Wink",
|
||||
"Ambivalent", "Expressionless", "Sad", "Slant", "Worried", "Kissing",
|
||||
"KissingHeart", "Angry", "Naughty", "Furious", "Cry", "OpenMouth",
|
||||
"Fearful", "Confused", "Weary", "Scream", "Astonished", "Flushed",
|
||||
"Sleeping", "NoMouth", "Mask", "Worried", "Smile", "Muscle",
|
||||
"Facepunch", "ThumbsUp", "ThumbsDown", "Beers", "Cocktail", "Burger",
|
||||
"PoultryLeg", "Party", "Cake", "Sun", "Fire", "Heart"
|
||||
],
|
||||
'atwho': {
|
||||
at: ":",
|
||||
highlightFirst: true,
|
||||
limit: 100
|
||||
},
|
||||
init: function(feature, options) {
|
||||
options.data = feature.emojis;
|
||||
options.insertTpl = "<img data-emoji-name=';${name};' class='atwho-emoji' with='18' height='18' src='" + module.config['emoji.url'] + "${name}.svg' />";
|
||||
options.displayTpl = "<li class='atwho-emoji-entry' data-value=';${name};'><img with='18' height='18' src='" + module.config['emoji.url'] + "${name}.svg' /></li>";
|
||||
},
|
||||
parse: function($clone) {
|
||||
$clone.find('.atwho-emoji').each(function() {
|
||||
$(this).replaceWith($(this).data('emoji-name'));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Mentioning feature supports mentionings by typing @ the default mentioning calls an url after typing three digits.
|
||||
* Other mentionings can be registered by adding Richtext.features with the at option @:<prefix>
|
||||
*/
|
||||
Richtext.features.mentioning = {};
|
||||
Richtext.features.mentioning.atwho = function() {
|
||||
// this is the widget instance.
|
||||
var that = this;
|
||||
return {
|
||||
at: "@",
|
||||
data: [{image: '', 'cssClass': 'hint', name: module.text('info.minInput')}],
|
||||
insertTpl: "<a href='${link}' class='atwho-user richtext-mention richtext-link' contenteditable='false' target='_blank' data-guid='${atwho-at}-${type}${guid}'>${name}</a>",
|
||||
displayTpl: "<li class='hint' data-value=''>${name}</li>",
|
||||
limit: 10,
|
||||
highlightFirst: false,
|
||||
callbacks: {
|
||||
matcher: function(flag, subtext, should_start_with_space) {
|
||||
var match, regexp;
|
||||
regexp = new RegExp(/(\s+|^)@([\u00C0-\u1FFF\u2C00-\uD7FF\w\s\-\']*$)/);
|
||||
match = regexp.exec(subtext);
|
||||
|
||||
this.setting.tpl = "<li class='hint' data-value=''>${name}</li>";
|
||||
|
||||
if(match && typeof match[2] !== 'undefined') {
|
||||
return match[2];
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
remoteFilter: function(query, callback) {
|
||||
this.setting.highlight_first = false;
|
||||
|
||||
// check the char length and data-query attribute for changing plugin settings for showing results
|
||||
if(query.length >= 3) {
|
||||
// Render loading user feedback.
|
||||
this.setting.displayTpl = "<li class='hint' data-value=''>${name}</li>";
|
||||
this.view.render([{"type": "test", "cssClass": "hint", "name": module.text('info.loading'), "image": "", "link": ""}]);
|
||||
|
||||
// set plugin settings for showing results
|
||||
this.setting.highlightFirst = true;
|
||||
this.setting.displayTpl = '<li class="${cssClass}" data-value="@${name}">${image} ${name}</li>';
|
||||
$.getJSON(that.options.mentioningUrl, {keyword: query}, function(data) {
|
||||
callback(data);
|
||||
});
|
||||
|
||||
// reset query count
|
||||
query.length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Richtext.features.mentioning.init = function(feature, options) {
|
||||
var widget = this;
|
||||
// This is a workaround for mobile browsers especially for Android Chrome which is not able to remove contenteditable="false" nodes.
|
||||
// This will enable mobile browsers to delete images and links by taphold/contextmenu event.
|
||||
if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|Opera Mini/i.test(navigator.userAgent)) {
|
||||
this.$.on('contextmenu', 'a, img', function() {
|
||||
var $this = $(this);
|
||||
if($this.parent().is('span')) {
|
||||
$this.parent().remove();
|
||||
} else {
|
||||
$this.remove();
|
||||
}
|
||||
|
||||
widget.checkForEmptySpans();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to parse the feature elements.
|
||||
*
|
||||
* @param {type} $clone
|
||||
* @param {type} widget
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Richtext.features.mentioning.parse = function($clone, widget) {
|
||||
$clone.find('.atwho-user, .atwho-space').each(function() {
|
||||
$(this).text($(this).data('guid'));
|
||||
});
|
||||
};
|
||||
|
||||
module.export({
|
||||
Richtext: Richtext
|
||||
});
|
||||
});
|
@ -10,6 +10,7 @@ humhub.module('ui.widget', function(module, require, $) {
|
||||
var additions = require('ui.additions');
|
||||
var Component = require('action').Component;
|
||||
var object = require('util').object;
|
||||
var string = require('util').string;
|
||||
var action = require('action');
|
||||
var event = require('event');
|
||||
|
||||
@ -21,26 +22,61 @@ humhub.module('ui.widget', function(module, require, $) {
|
||||
this.errors = [];
|
||||
this.options = this.initOptions(options || {});
|
||||
|
||||
// Use internal events
|
||||
event.sub(this);
|
||||
this.initWidgetEvents();
|
||||
|
||||
if(!this.validate()) {
|
||||
module.log.warn('Could not initialize widget.', this.errors);
|
||||
} else {
|
||||
var initData = this.$.data('ui-init');
|
||||
this.fire('before-init', [this, initData]);
|
||||
this.fire('beforeInit', [this, initData]);
|
||||
this.init(initData);
|
||||
this.fire('after-init', [this]);
|
||||
this.fire('afterInit', [this]);
|
||||
}
|
||||
};
|
||||
|
||||
object.inherits(Widget, Component);
|
||||
|
||||
Widget.prototype.fire = function(event, args) {
|
||||
if(this.$.data('action-' + event)) {
|
||||
action.trigger(this.$, event, {params: args});
|
||||
|
||||
Widget.prototype.initWidgetEvents = function() {
|
||||
// Use internal event object for handling widget events.
|
||||
event.sub(this);
|
||||
|
||||
var that = this;
|
||||
|
||||
// Bind dom events to widget events actions.
|
||||
$.each(this.options, function(key, value) {
|
||||
if(string.startsWith(key, 'widgetAction')) {
|
||||
var eventType = string.cutPrefix(key, 'widgetAction').toLowerCase();
|
||||
that.$.on(eventType+'.humhub:widget:events', function() {
|
||||
that.fire(eventType);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Widget.prototype.fire = function(event, args, triggerDom) {
|
||||
// Trigger action if there is an action handler set
|
||||
var widgetAction = 'widgetAction' + string.capitalize(event);
|
||||
if(this.options[widgetAction]){
|
||||
var handler = this.options[widgetAction];
|
||||
if(string.startsWith(handler, 'this.')) {
|
||||
handler = string.cutPrefix(handler, 'this.');
|
||||
var handlerFunc = object.resolve(this, handler);
|
||||
if(object.isFunction(handlerFunc)) {
|
||||
handlerFunc.apply(this);
|
||||
}
|
||||
} else {
|
||||
action.trigger(this.$, event, {handler: handler, params: args});
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger internal widget event
|
||||
this.trigger(event, args);
|
||||
|
||||
// If required, trigger dom event
|
||||
if(triggerDom) {
|
||||
this.$.trigger(event, args);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -71,6 +107,10 @@ humhub.module('ui.widget', function(module, require, $) {
|
||||
return true;
|
||||
};
|
||||
|
||||
Widget.prototype.isVisible = function() {
|
||||
return this.$.is(':visible');
|
||||
};
|
||||
|
||||
/**
|
||||
* Can be overwritten to initialize the widget.
|
||||
* @returns {undefined}
|
||||
|
@ -59,7 +59,7 @@ humhub.module('util', function(module, require, $) {
|
||||
var handlers = [];
|
||||
Array.prototype.push.apply(handlers, arguments);
|
||||
handlers.shift();
|
||||
|
||||
|
||||
return function() {
|
||||
var _arguments = arguments;
|
||||
handlers.forEach(function(handler) {
|
||||
@ -107,17 +107,33 @@ humhub.module('util', function(module, require, $) {
|
||||
}
|
||||
};
|
||||
|
||||
var entityMap = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
'/': '/',
|
||||
'`': '`',
|
||||
'=': '='
|
||||
};
|
||||
|
||||
var string = {
|
||||
escapeHtml: function(string) {
|
||||
return String(string).replace(/[&<>"'`=\/]/g, function(s) {
|
||||
return entityMap[s];
|
||||
});
|
||||
},
|
||||
capitalize: function(string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
},
|
||||
cutprefix: function(val, prefix) {
|
||||
cutPrefix: function(val, prefix) {
|
||||
if(!this.startsWith(val, prefix)) {
|
||||
return val;
|
||||
}
|
||||
return val.substring(prefix.length, val.length);
|
||||
},
|
||||
cutsuffix: function(val, suffix) {
|
||||
cutSuffix: function(val, suffix) {
|
||||
return val.slice(0, suffix.length * -1);
|
||||
},
|
||||
startsWith: function(val, prefix) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* Handles conflicting jquery ui and bootstrap namespaces.
|
||||
*/
|
||||
|
||||
$.widget.bridge('uibutton', $.ui.button);
|
||||
$.widget.bridge('uitooltip', $.ui.tooltip);
|
@ -1,5 +1,12 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'bootstrap' => ['debug'],
|
||||
'modules' => [
|
||||
'debug' => [
|
||||
'class' => 'yii\debug\Module',
|
||||
//'allowedIPs' => ['*'],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
|
@ -87,6 +87,7 @@ class AppAsset extends AssetBundle
|
||||
'humhub\modules\content\assets\ContentAsset',
|
||||
'humhub\modules\user\assets\UserAsset',
|
||||
'humhub\modules\user\assets\UserPickerAsset',
|
||||
'humhub\modules\post\assets\PostAsset',
|
||||
'humhub\modules\comment\assets\CommentAsset',
|
||||
'humhub\assets\NProgressAsset',
|
||||
'humhub\assets\IE9FixesAsset',
|
||||
|
@ -59,6 +59,7 @@ class CoreApiAsset extends AssetBundle
|
||||
'js/humhub/humhub.ui.navigation.js',
|
||||
'js/humhub/humhub.ui.gallery.js',
|
||||
'js/humhub/humhub.ui.picker.js',
|
||||
'js/humhub/humhub.ui.richtext.js',
|
||||
// Note this should stay at last for other click event listeners beeing able to prevent pjax handling (e.g gallery)
|
||||
'js/humhub/humhub.client.pjax.js',
|
||||
];
|
||||
|
@ -9,6 +9,7 @@
|
||||
namespace humhub\assets;
|
||||
|
||||
use yii\web\AssetBundle;
|
||||
use yii\web\View;
|
||||
|
||||
/**
|
||||
* select2
|
||||
@ -17,6 +18,8 @@ use yii\web\AssetBundle;
|
||||
*/
|
||||
class PjaxAsset extends AssetBundle
|
||||
{
|
||||
public $jsOptions = ['position' => View::POS_HEAD];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
@ -33,7 +36,7 @@ class PjaxAsset extends AssetBundle
|
||||
public $js = ['js/jquery.pjax.modified.js'];
|
||||
|
||||
public $depends = [
|
||||
'yii\web\YiiAsset',
|
||||
//'yii\web\YiiAsset',
|
||||
];
|
||||
|
||||
}
|
||||
|
@ -11,13 +11,21 @@ HumHub Change Log
|
||||
- Enh: Refactored File module (luke-)
|
||||
- Enh: Added yii2-imagine Extension (luke-)
|
||||
- Enh: Use of blueimp image gallery (buddha)
|
||||
- Enh: JS module system (buddha)
|
||||
- Enh: JS core api (buddha)
|
||||
- Enh: JS module system with build in logging/text/config features (buddha)
|
||||
- Enh: JS core api under humhub namespace (buddha)
|
||||
- Enh: Use of compressed assets (js,css) in production mode (buddha)
|
||||
- Enh: Enhanced testability (buddha)
|
||||
- Enh: Added backend group permissions (buddha)
|
||||
- Enh: Enhanced AccessControll filter with permission rules. (buddha)
|
||||
- Enh: Added administrative backend group permissions (buddha)
|
||||
- Enh: Enhanced AccessControl filter with permission rules. (buddha)
|
||||
- Enh: Splitted less files to facilitate theming. (buddha)
|
||||
- Enh: Added user status bar for user feedback (buddha)
|
||||
- Enh: Better UserFeedack (buttons/messages) / Replacement of old DataSaved widget (buddha)
|
||||
- Enh: Overwrite default permission settings (buddha)
|
||||
- Enh: SpaceChooser rewrite with following spaces and remote search (buddha)
|
||||
- Enh: Modal widget rewrite.
|
||||
- Enh: Enhanced Archived Space handling (buddha)
|
||||
- Enh: Upload widget rewrite. (buddha)
|
||||
- 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.
|
||||
|
@ -47,6 +47,12 @@ Please see the following file for 1.2 changes (at the buttom):
|
||||
- modules/space/views/membership/invite.php
|
||||
- modules/comment/widget/views/showComment.php
|
||||
- modules/files/widget/views/showFiles.php
|
||||
- modules/post/widget/views/post.php
|
||||
- Richtext rewrite:
|
||||
- modules/comment/widget/views/form.php
|
||||
- modules/comment/views/comment/edit.php
|
||||
- modules/post/views/post/edit.php
|
||||
- modules/post/widget/views/form.php
|
||||
- UserPicker rewrite:
|
||||
- modules/admin/views/group/edit.php
|
||||
- modules/admin/views/group/members.php
|
||||
|
@ -17,13 +17,17 @@ humhub.module('comment', function(module, require, $) {
|
||||
client.submit(evt, {dataType: 'html'}).then(function(response) {
|
||||
that.addComment(response.html);
|
||||
that.getInput().val('').trigger('autosize.resize');
|
||||
that.getRichtext().addClass('atwho-placeholder').focus();
|
||||
that.getRichtext().$.addClass('atwho-placeholder').focus();
|
||||
that.getUpload().reset();
|
||||
}).catch(function(err) {
|
||||
module.log.error(err, true);
|
||||
});
|
||||
};
|
||||
|
||||
Form.prototype.getRichtext = function(html) {
|
||||
return Widget.instance(this.$.find('div.humhub-ui-richtext'));
|
||||
};
|
||||
|
||||
Form.prototype.addComment = function(html) {
|
||||
var $html = $(html).hide();
|
||||
this.getCommentsContainer().append($html);
|
||||
@ -41,10 +45,6 @@ humhub.module('comment', function(module, require, $) {
|
||||
Form.prototype.getInput = function() {
|
||||
return this.$.find('textarea');
|
||||
};
|
||||
|
||||
Form.prototype.getRichtext = function() {
|
||||
return this.$.find('[contenteditable]');
|
||||
};
|
||||
|
||||
var Comment = function(node) {
|
||||
Content.call(this, node);
|
||||
@ -58,13 +58,17 @@ humhub.module('comment', function(module, require, $) {
|
||||
var that = this;
|
||||
client.post(evt, {dataType: 'html'}).then(function(response) {
|
||||
that.$.find('.comment_edit_content').replaceWith(response.html);
|
||||
that.$.find('[contenteditable]').focus();
|
||||
that.getRichtext().focus();
|
||||
that.$.find('.comment-cancel-edit-link').show();
|
||||
that.$.find('.comment-edit-link').hide();
|
||||
}).finally(function() {
|
||||
that.loader(false);
|
||||
});
|
||||
};
|
||||
|
||||
Comment.prototype.getRichtext = function() {
|
||||
return Widget.instance(this.$.find('div.humhub-ui-richtext'));
|
||||
};
|
||||
|
||||
Comment.prototype.delete = function() {
|
||||
this.super('delete', {modal: module.config.modal.delteConfirm}).then(function($confirm) {
|
||||
|
@ -10,17 +10,13 @@ use yii\helpers\Url;
|
||||
<?php $form = CActiveForm::begin(); ?>
|
||||
<?= Html::hiddenInput('contentModel', $contentModel); ?>
|
||||
<?= Html::hiddenInput('contentId', $contentId); ?>
|
||||
<?= $form->textArea($comment, 'message', array('class' => 'form-control', 'id' => 'comment_input_' . $comment->id, 'placeholder' => Yii::t('CommentModule.views_edit', 'Edit your comment...'))); ?>
|
||||
|
||||
<!-- create contenteditable div for HEditorWidget to place the data -->
|
||||
<div id="comment_input_<?= $comment->id; ?>_contenteditable" class="form-control atwho-input"
|
||||
contenteditable="true"><?= \humhub\widgets\RichText::widget(['text' => $comment->message, 'edit' => true]); ?></div>
|
||||
|
||||
<?= \humhub\widgets\RichTextEditor::widget(array(
|
||||
'id' => 'comment_input_' . $comment->id,
|
||||
'inputContent' => $comment->message,
|
||||
'record' => $comment,
|
||||
));?>
|
||||
|
||||
<?= humhub\widgets\RichtextField::widget([
|
||||
'id' => 'comment_input_'.$comment->id,
|
||||
'placeholder' => Yii::t('CommentModule.views_edit', 'Edit your comment...'),
|
||||
'model' => $comment,
|
||||
'attribute' => 'message'
|
||||
]); ?>
|
||||
|
||||
<div class="comment-buttons">
|
||||
|
||||
|
@ -11,10 +11,12 @@ use yii\helpers\Url;
|
||||
<?php echo Html::hiddenInput('contentModel', $modelName); ?>
|
||||
<?php echo Html::hiddenInput('contentId', $modelId); ?>
|
||||
|
||||
<?php echo Html::textarea("message", "", array('id' => 'newCommentForm_' . $id, 'rows' => '1', 'class' => 'form-control autosize commentForm', 'placeholder' => Yii::t('CommentModule.widgets_views_form', 'Write a new comment...'))); ?>
|
||||
|
||||
<?php echo humhub\widgets\RichTextEditor::widget(['id' => 'newCommentForm_' . $id]); ?>
|
||||
|
||||
<?= humhub\widgets\RichtextField::widget([
|
||||
'id' => 'newCommentForm_' . $id,
|
||||
'placeholder' => Yii::t('CommentModule.widgets_views_form', 'Write a new comment...'),
|
||||
'name' => 'message'
|
||||
]); ?>
|
||||
|
||||
<div class="comment-buttons">
|
||||
<?= humhub\modules\file\widgets\UploadButton::widget([
|
||||
'id' => 'comment_create_upload_' . $id,
|
||||
|
@ -8,10 +8,7 @@ humhub.module('content.form', function(module, require, $) {
|
||||
|
||||
var object = require('util').object;
|
||||
var client = require('client');
|
||||
|
||||
var config = require('config').module(module);
|
||||
var event = require('event');
|
||||
|
||||
var Widget = require('ui.widget').Widget;
|
||||
|
||||
var instance;
|
||||
@ -28,15 +25,20 @@ humhub.module('content.form', function(module, require, $) {
|
||||
// Hide options by default
|
||||
$('.contentForm_options').hide();
|
||||
$('#contentFormError').hide();
|
||||
// Remove info text from the textinput
|
||||
$('#contentFormBody').click(function() {
|
||||
// Hide options by default
|
||||
$('.contentForm_options').fadeIn();
|
||||
});
|
||||
|
||||
this.setDefaultVisibility();
|
||||
|
||||
this.$.fadeIn('fast');
|
||||
|
||||
if(!module.config['disabled']) {
|
||||
// Remove info text from the textinput
|
||||
$('#contentFormBody').on('click.humhub:content:form', function() {
|
||||
// Hide options by default
|
||||
$('.contentForm_options').fadeIn();
|
||||
});
|
||||
} else {
|
||||
$('#contentFormBody').find('.humhub-ui-richtext').trigger('disable');
|
||||
}
|
||||
};
|
||||
|
||||
CreateForm.prototype.actions = function() {
|
||||
@ -80,15 +82,14 @@ humhub.module('content.form', function(module, require, $) {
|
||||
this.resetFilePreview();
|
||||
|
||||
$('#public').attr('checked', false);
|
||||
$('#contentForm_message_contenteditable').addClass('atwho-placeholder').attr('spellcheck', 'false');
|
||||
$('#contentFormBody').find('.atwho-input').trigger('clear');
|
||||
$('#contentFormBody').find('.humhub-ui-richtext').trigger('clear');
|
||||
};
|
||||
|
||||
CreateForm.prototype.resetNotifyUser = function() {
|
||||
$('#notifyUserContainer').hide();
|
||||
Widget.instance('#notifyUserInput').reset();
|
||||
};
|
||||
|
||||
|
||||
CreateForm.prototype.resetFilePreview = function() {
|
||||
Widget.instance($('#contentFormFiles_preview')).reset();
|
||||
};
|
||||
@ -118,22 +119,22 @@ humhub.module('content.form', function(module, require, $) {
|
||||
};
|
||||
|
||||
CreateForm.prototype.setDefaultVisibility = function() {
|
||||
if(config['defaultVisibility']) {
|
||||
if(module.config['defaultVisibility']) {
|
||||
this.setPublicVisibility();
|
||||
} else {
|
||||
this.setPrivateVisibility();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
CreateForm.prototype.setPublicVisibility = function() {
|
||||
$('#contentForm_visibility').prop("checked", true);
|
||||
$('#contentForm_visibility_entry').html('<i class="fa fa-lock"></i>' + config['text']['makePrivate']);
|
||||
$('#contentForm_visibility_entry').html('<i class="fa fa-lock"></i>' + module.text(['makePrivate']));
|
||||
$('.label-public').removeClass('hidden');
|
||||
};
|
||||
|
||||
CreateForm.prototype.setPrivateVisibility = function() {
|
||||
$('#contentForm_visibility').prop("checked", false);
|
||||
$('#contentForm_visibility_entry').html('<i class="fa fa-unlock"></i>' + config['text']['makePublic']);
|
||||
$('#contentForm_visibility_entry').html('<i class="fa fa-unlock"></i>' + module.text(['makePublic']));
|
||||
$('.label-public').addClass('hidden');
|
||||
};
|
||||
|
||||
@ -143,12 +144,21 @@ humhub.module('content.form', function(module, require, $) {
|
||||
};
|
||||
|
||||
var init = function() {
|
||||
instance = Widget.instance($(CREATE_FORM_ROOT_SELECTOR));
|
||||
var $root = $(CREATE_FORM_ROOT_SELECTOR);
|
||||
if($root.length) {
|
||||
instance = Widget.instance($root);
|
||||
}
|
||||
};
|
||||
|
||||
var unload = function() {
|
||||
instance = undefined;
|
||||
}
|
||||
|
||||
module.export({
|
||||
CreateForm: CreateForm,
|
||||
instance: instance,
|
||||
init: init
|
||||
init: init,
|
||||
initOnPjaxLoad: true,
|
||||
unload: unload
|
||||
});
|
||||
});
|
@ -213,5 +213,15 @@ abstract class ContentContainerActiveRecord extends ActiveRecord
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns weather or not the contentcontainer is archived. (Default false).
|
||||
* @return boolean
|
||||
* @since 1.2
|
||||
*/
|
||||
public function isArchived()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -336,7 +336,7 @@ class Content extends \humhub\components\ActiveRecord
|
||||
*/
|
||||
public function isArchived()
|
||||
{
|
||||
return ($this->archived);
|
||||
return $this->archived || $this->getContainer()->isArchived();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -19,7 +19,8 @@ class ContentFixture extends ActiveFixture
|
||||
public $depends = [
|
||||
'humhub\modules\content\tests\codeception\fixtures\ContentContainerFixture',
|
||||
'humhub\modules\post\tests\codeception\fixtures\PostFixture',
|
||||
'humhub\modules\comment\tests\codeception\fixtures\CommentFixture'
|
||||
'humhub\modules\comment\tests\codeception\fixtures\CommentFixture',
|
||||
'humhub\modules\like\tests\codeception\fixtures\LikeFixture'
|
||||
];
|
||||
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ class ArchiveLink extends \yii\base\Widget
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
if (!Yii::$app->controller instanceof ContentContainerController || !$this->content->content->canArchive()) {
|
||||
if (!Yii::$app->controller instanceof ContentContainerController || !$this->content->content->canArchive() || $this->content->content->getContainer()->isArchived()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,10 @@ class DeleteLink extends \yii\base\Widget
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
if($this->content->content->isArchived()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->content->content->canEdit()) {
|
||||
return $this->render('deleteLink', array(
|
||||
'model' => $this->content->content->object_model,
|
||||
|
@ -18,14 +18,13 @@ $this->registerJsConfig('content.form', [
|
||||
<div class="panel panel-default clearfix">
|
||||
<div class="panel-body" id="contentFormBody" style="display:none;" data-action-component="content.form.CreateForm" >
|
||||
<?= Html::beginForm($submitUrl, 'POST'); ?>
|
||||
|
||||
<?= $form; ?>
|
||||
|
||||
<div id="notifyUserContainer" class="form-group" style="margin-top: 15px;display:none;">
|
||||
|
||||
<?php $memberPickerUrl = ($contentContainer instanceof Space) ? $contentContainer->createUrl('/space/membership/search') : null ?>
|
||||
<?= humhub\modules\user\widgets\UserPickerField::widget([
|
||||
'id' => 'notifyUserInput',
|
||||
'url' => $memberPickerUrl,
|
||||
'url' => ($contentContainer instanceof Space) ? $contentContainer->createUrl('/space/membership/search') : null,
|
||||
'formName' => 'notifyUserInput',
|
||||
'maxSelection' => 10,
|
||||
'disabledItems' => [Yii::$app->user->guid],
|
||||
@ -35,7 +34,6 @@ $this->registerJsConfig('content.form', [
|
||||
|
||||
<?= Html::hiddenInput("containerGuid", $contentContainer->guid); ?>
|
||||
<?= Html::hiddenInput("containerClass", get_class($contentContainer)); ?>
|
||||
|
||||
|
||||
<ul id="contentFormError"></ul>
|
||||
|
||||
|
@ -102,7 +102,7 @@ class DashboardStreamTest extends HumHubDbTestCase
|
||||
$post2->save();
|
||||
$w2 = $post2->content->id;
|
||||
|
||||
$this->assertEquals($this->getStreamActionIds(2), array($w2, $w1));
|
||||
$this->assertEquals($this->getStreamActionIds(2), [$w2, $w1]);
|
||||
|
||||
$this->becomeUser('User3');
|
||||
$ids = $this->getStreamActionIds(2);
|
||||
|
24
protected/humhub/modules/like/tests/codeception.yml
Normal file
24
protected/humhub/modules/like/tests/codeception.yml
Normal file
@ -0,0 +1,24 @@
|
||||
actor: Tester
|
||||
namespace: like
|
||||
settings:
|
||||
bootstrap: _bootstrap.php
|
||||
suite_class: \PHPUnit_Framework_TestSuite
|
||||
colors: true
|
||||
shuffle: false
|
||||
memory_limit: 1024M
|
||||
log: true
|
||||
|
||||
# This value controls whether PHPUnit attempts to backup global variables
|
||||
# See https://phpunit.de/manual/current/en/appendixes.annotations.html#appendixes.annotations.backupGlobals
|
||||
backup_globals: true
|
||||
paths:
|
||||
tests: codeception
|
||||
log: codeception/_output
|
||||
data: codeception/_data
|
||||
helpers: codeception/_support
|
||||
envs: config/env
|
||||
config:
|
||||
# the entry script URL (with host info) for functional and acceptance tests
|
||||
# PLEASE ADJUST IT TO THE ACTUAL ENTRY SCRIPT URL
|
||||
test_entry_url: http://localhost:8080/index-test.php
|
||||
|
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* This is the initial test bootstrap, which will load the default test bootstrap from the humhub core
|
||||
*/
|
||||
// Parse the environment arguments (Note: only simple --env ENV is supported no comma sepration merge...)
|
||||
$argv = $_SERVER['argv'];
|
||||
$env = [];
|
||||
for ($i = 0; $i < count($argv); $i++) {
|
||||
if ($argv[$i] === '--env') {
|
||||
$env[] = explode(',', $argv[++$i]);
|
||||
}
|
||||
}
|
||||
|
||||
// If environment was set try loading special environment config else load default
|
||||
if (count($env) > 0) {
|
||||
\Codeception\Configuration::append(['environment' => $env]);
|
||||
|
||||
echo 'Run execution environment: ' . $env[0][0] . PHP_EOL;
|
||||
|
||||
$envCfgFile = dirname(__DIR__) . '/config/env/test.' . $env[0][0] . '.php';
|
||||
|
||||
if (file_exists($envCfgFile)) {
|
||||
$cfg = array_merge(require_once(__DIR__ . '/../config/test.php'), require_once($envCfgFile));
|
||||
}
|
||||
}
|
||||
|
||||
// If no environment is set we have to load the default config
|
||||
if (!isset($cfg)) {
|
||||
$cfg = require_once(__DIR__ . '/../config/test.php');
|
||||
}
|
||||
|
||||
// If no humhub_root is given we assume our module is in the a root to be in /protected/humhub/modules/<module>/tests/codeception directory
|
||||
$cfg['humhub_root'] = isset($cfg['humhub_root']) ? $cfg['humhub_root'] : dirname(__DIR__) . '/../../../../..';
|
||||
|
||||
echo 'Using HumHub Root: ' . $cfg['humhub_root'] . PHP_EOL;
|
||||
|
||||
// Load default test bootstrap
|
||||
require_once($cfg['humhub_root'] . '/protected/humhub/tests/codeception/_bootstrap.php');
|
||||
|
||||
// Overwrite the default test alias
|
||||
Yii::setAlias('@tests', dirname(__DIR__));
|
||||
Yii::setAlias('@env', '@tests/config/env');
|
||||
Yii::setAlias('@root', $cfg['humhub_root']);
|
||||
Yii::setAlias('@humhubTests', $cfg['humhub_root'] . '/protected/humhub/tests');
|
||||
|
||||
// Load all supporting test classes needed for test execution
|
||||
\Codeception\Util\Autoload::addNamespace('', Yii::getAlias('@humhubTests/codeception/_support'));
|
||||
\Codeception\Util\Autoload::addNamespace('tests\codeception\fixtures', Yii::getAlias('@humhubTests/codeception/fixtures'));
|
||||
\Codeception\Util\Autoload::addNamespace('', Yii::getAlias('@humhubTests/codeception/_pages'));
|
||||
if(isset($cfg['modules'])) {
|
||||
\Codeception\Configuration::append(['humhub_modules' => $cfg['modules']]);
|
||||
}
|
||||
|
||||
if(isset($cfg['fixtures'])) {
|
||||
\Codeception\Configuration::append(['fixtures' => $cfg['fixtures']]);
|
||||
}
|
||||
?>
|
4
protected/humhub/modules/like/tests/codeception/_output/.gitignore
vendored
Normal file
4
protected/humhub/modules/like/tests/codeception/_output/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
namespace like;
|
||||
|
||||
/**
|
||||
* Inherited Methods
|
||||
* @method void wantToTest($text)
|
||||
* @method void wantTo($text)
|
||||
* @method void execute($callable)
|
||||
* @method void expectTo($prediction)
|
||||
* @method void expect($prediction)
|
||||
* @method void amGoingTo($argumentation)
|
||||
* @method void am($role)
|
||||
* @method void lookForwardTo($achieveValue)
|
||||
* @method void comment($description)
|
||||
* @method \Codeception\Lib\Friend haveFriend($name, $actorClass = null)
|
||||
*
|
||||
* @SuppressWarnings(PHPMD)
|
||||
*/
|
||||
class AcceptanceTester extends \AcceptanceTester
|
||||
{
|
||||
use _generated\AcceptanceTesterActions;
|
||||
|
||||
/**
|
||||
* Define custom actions here
|
||||
*/
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
namespace like;
|
||||
|
||||
/**
|
||||
* Inherited Methods
|
||||
* @method void wantToTest($text)
|
||||
* @method void wantTo($text)
|
||||
* @method void execute($callable)
|
||||
* @method void expectTo($prediction)
|
||||
* @method void expect($prediction)
|
||||
* @method void amGoingTo($argumentation)
|
||||
* @method void am($role)
|
||||
* @method void lookForwardTo($achieveValue)
|
||||
* @method void comment($description)
|
||||
* @method \Codeception\Lib\Friend haveFriend($name, $actorClass = null)
|
||||
*
|
||||
* @SuppressWarnings(PHPMD)
|
||||
*/
|
||||
class FunctionalTester extends \FunctionalTester
|
||||
{
|
||||
use _generated\FunctionalTesterActions;
|
||||
|
||||
/**
|
||||
* Define custom actions here
|
||||
*/
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
namespace like;
|
||||
|
||||
/**
|
||||
* Inherited Methods
|
||||
* @method void wantToTest($text)
|
||||
* @method void wantTo($text)
|
||||
* @method void execute($callable)
|
||||
* @method void expectTo($prediction)
|
||||
* @method void expect($prediction)
|
||||
* @method void amGoingTo($argumentation)
|
||||
* @method void am($role)
|
||||
* @method void lookForwardTo($achieveValue)
|
||||
* @method void comment($description)
|
||||
* @method \Codeception\Lib\Friend haveFriend($name, $actorClass = null)
|
||||
*
|
||||
* @SuppressWarnings(PHPMD)
|
||||
*/
|
||||
class UnitTester extends \UnitTester
|
||||
{
|
||||
use _generated\UnitTesterActions;
|
||||
|
||||
/**
|
||||
* Define custom actions here
|
||||
*/
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,38 @@
|
||||
<?php //[STAMP] 0468b7e2480518455b63a22d3aa6f7c2
|
||||
namespace like\_generated;
|
||||
|
||||
// This class was automatically generated by build task
|
||||
// You should not change it manually as it will be overwritten on next build
|
||||
// @codingStandardsIgnoreFile
|
||||
|
||||
use tests\codeception\_support\CodeHelper;
|
||||
|
||||
trait UnitTesterActions
|
||||
{
|
||||
/**
|
||||
* @return \Codeception\Scenario
|
||||
*/
|
||||
abstract protected function getScenario();
|
||||
|
||||
|
||||
/**
|
||||
* [!] Method is generated. Documentation taken from corresponding module.
|
||||
*
|
||||
*
|
||||
* @see \tests\codeception\_support\CodeHelper::assertContainsError()
|
||||
*/
|
||||
public function assertContainsError($model, $message) {
|
||||
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertContainsError', func_get_args()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* [!] Method is generated. Documentation taken from corresponding module.
|
||||
*
|
||||
*
|
||||
* @see \tests\codeception\_support\CodeHelper::assertNotContainsError()
|
||||
*/
|
||||
public function assertNotContainsError($model, $message) {
|
||||
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotContainsError', func_get_args()));
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
# Codeception Test Suite Configuration
|
||||
|
||||
# suite for acceptance tests.
|
||||
# perform tests in browser using the Selenium-like tools.
|
||||
# powered by Mink (http://mink.behat.org).
|
||||
# (tip: that's what your customer will see).
|
||||
# (tip: test your ajax and javascript by one of Mink drivers).
|
||||
|
||||
# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
|
||||
|
||||
class_name: AcceptanceTester
|
||||
modules:
|
||||
enabled:
|
||||
- WebDriver
|
||||
- tests\codeception\_support\WebHelper
|
||||
- tests\codeception\_support\DynamicFixtureHelper
|
||||
config:
|
||||
WebDriver:
|
||||
url: http://localhost:8080/
|
||||
browser: phantomjs
|
||||
window_size: 1024x768
|
||||
restart: true
|
||||
lang: en
|
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* Initialize the HumHub Application for functional testing. The default application configuration for this suite can be overwritten
|
||||
* in @tests/config/functional.php
|
||||
*/
|
||||
require(Yii::getAlias('@humhubTests/codeception/acceptance/_bootstrap.php'));
|
@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
return \tests\codeception\_support\HumHubTestConfiguration::getSuiteConfig('functional');
|
@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
return \tests\codeception\_support\HumHubTestConfiguration::getSuiteConfig('unit');
|
@ -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\like\tests\codeception\fixtures;
|
||||
|
||||
use yii\test\ActiveFixture;
|
||||
|
||||
class LikeFixture extends ActiveFixture
|
||||
{
|
||||
|
||||
public $modelClass = 'humhub\modules\like\models\Like';
|
||||
public $dataFile = '@modules/like/tests/codeception/fixtures/data/like.php';
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
<?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 [];
|
@ -0,0 +1,18 @@
|
||||
# Codeception Test Suite Configuration
|
||||
|
||||
# suite for functional (integration) tests.
|
||||
# emulate web requests and make application process them.
|
||||
# (tip: better to use with frameworks).
|
||||
|
||||
# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
|
||||
class_name: FunctionalTester
|
||||
modules:
|
||||
enabled:
|
||||
- Filesystem
|
||||
- Yii2
|
||||
- tests\codeception\_support\TestHelper
|
||||
- tests\codeception\_support\DynamicFixtureHelper
|
||||
- tests\codeception\_support\HumHubHelper
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/functional.php'
|
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* Initialize the HumHub Application for functional testing. The default application configuration for this suite can be overwritten
|
||||
* in @tests/config/functional.php
|
||||
*/
|
||||
require(Yii::getAlias('@humhubTests/codeception/functional/_bootstrap.php'));
|
@ -0,0 +1,9 @@
|
||||
# Codeception Test Suite Configuration
|
||||
|
||||
# suite for unit (internal) tests.
|
||||
# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
|
||||
|
||||
class_name: UnitTester
|
||||
modules:
|
||||
enabled:
|
||||
- tests\codeception\_support\CodeHelper
|
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* Initialize the HumHub Application for functional testing. The default application configuration for this suite can be overwritten
|
||||
* in @tests/config/functional.php
|
||||
*/
|
||||
require(Yii::getAlias('@humhubTests/codeception/unit/_bootstrap.php'));
|
5
protected/humhub/modules/like/tests/config/common.php
Normal file
5
protected/humhub/modules/like/tests/config/common.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
* This config is shared by all suites (unit/functional/acceptance) and can be overwritten by a suite config (e.g. functional.php)
|
||||
*/
|
||||
return [];
|
4
protected/humhub/modules/like/tests/config/env/.gitignore
vendored
Normal file
4
protected/humhub/modules/like/tests/config/env/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
* Here you can overwrite the default config for the functional suite. The default config resides in @humhubTests/codeception/config/config.php
|
||||
*/
|
||||
return [];
|
11
protected/humhub/modules/like/tests/config/test.php
Normal file
11
protected/humhub/modules/like/tests/config/test.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'fixtures' => [
|
||||
'default',
|
||||
'content' => 'humhub\modules\content\tests\codeception\fixtures\ContentFixture',
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
|
5
protected/humhub/modules/like/tests/config/unit.php
Normal file
5
protected/humhub/modules/like/tests/config/unit.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
* Here you can overwrite your functional humhub config. The default config resiedes in @humhubTests/codeception/config/config.php
|
||||
*/
|
||||
return [];
|
26
protected/humhub/modules/post/assets/PostAsset.php
Executable file
26
protected/humhub/modules/post/assets/PostAsset.php
Executable file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
namespace humhub\modules\post\assets;
|
||||
|
||||
use yii\web\AssetBundle;
|
||||
|
||||
class PostAsset extends AssetBundle
|
||||
{
|
||||
|
||||
public $sourcePath = '@post/resources';
|
||||
public $css = [];
|
||||
public $js = [
|
||||
'js/humhub.post.js'
|
||||
];
|
||||
|
||||
public $depends = [
|
||||
'humhub\assets\CoreApiAsset'
|
||||
];
|
||||
|
||||
}
|
83
protected/humhub/modules/post/resources/js/humhub.post.js
Normal file
83
protected/humhub/modules/post/resources/js/humhub.post.js
Normal file
@ -0,0 +1,83 @@
|
||||
humhub.module('post', function(module, require, $) {
|
||||
var Widget = require('ui.widget').Widget;
|
||||
var object = require('util').object;
|
||||
|
||||
var Post = function(node, options) {
|
||||
Widget.call(this, node, options);
|
||||
};
|
||||
|
||||
object.inherits(Post, Widget);
|
||||
|
||||
Post.prototype.getDefaultOptions = function() {
|
||||
return {
|
||||
collapse: 310,
|
||||
readMore: module.text('default.readMore'),
|
||||
readLess: module.text('default.readLess')
|
||||
};
|
||||
};
|
||||
|
||||
Post.prototype.init = function() {
|
||||
this.initCollapse();
|
||||
};
|
||||
|
||||
Post.prototype.initCollapse = function() {
|
||||
var that = this;
|
||||
|
||||
var height = this.$.outerHeight();
|
||||
this.$collapseButton = this.$.siblings('.showMore');
|
||||
|
||||
if(this.options.prevCollapse) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If height expands the max height we init the collapse post logic
|
||||
if(height > this.options.collapse) {
|
||||
if(!this.$collapseButton.length) {
|
||||
this.$collapseButton = $(Post.templates.showMore);
|
||||
this.$.after(this.$collapseButton);
|
||||
}
|
||||
|
||||
// Init collapse button
|
||||
this.$collapseButton.on('click', function(evt) {
|
||||
evt.preventDefault();
|
||||
that.toggleCollapse();
|
||||
}).show();
|
||||
|
||||
// Set init state
|
||||
if(this.data('state') !== 'expanded') {
|
||||
this.collapse();
|
||||
} else {
|
||||
this.expand();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Post.prototype.toggleCollapse = function() {
|
||||
if(this.$.data('state') === 'collapsed') {
|
||||
this.expand();
|
||||
} else {
|
||||
this.collapse();
|
||||
}
|
||||
};
|
||||
|
||||
Post.prototype.collapse = function() {
|
||||
this.$.css({'display': 'block', 'max-height': this.options.collapse+'px'});
|
||||
this.$collapseButton.html('<i class="fa fa-arrow-down"></i> ' + this.options.readMore);
|
||||
this.$.data('state', 'collapsed');
|
||||
};
|
||||
|
||||
Post.prototype.expand = function() {
|
||||
this.$.css('max-height', '');
|
||||
this.$collapseButton.html('<i class="fa fa-arrow-up"></i> ' + this.options.readLess);
|
||||
this.$.data('state', 'expanded');
|
||||
};
|
||||
|
||||
Post.templates = {
|
||||
showMore : '<a href="#" style="display:block;margin: 5px 0;"></a>'
|
||||
};
|
||||
|
||||
module.export({
|
||||
Post: Post
|
||||
});
|
||||
|
||||
});
|
@ -1,46 +1,47 @@
|
||||
<?php
|
||||
|
||||
use humhub\compat\CActiveForm;
|
||||
use yii\helpers\Html;
|
||||
|
||||
?>
|
||||
<div class="content content_edit" id="post_edit_<?php echo $post->id; ?>">
|
||||
<?php $form = CActiveForm::begin(['id' => 'post-edit-form_' . $post->id]); ?>
|
||||
|
||||
|
||||
<!-- create contenteditable div for HEditorWidget to place the data -->
|
||||
<div id="post_input_<?php echo $post->id; ?>_contenteditable" class="form-control atwho-input" contenteditable="true">
|
||||
<?php echo \humhub\widgets\RichText::widget(['text' => $post->message, 'edit' => true]); ?>
|
||||
</div>
|
||||
|
||||
<?= Html::activeTextarea($post, 'message', ['class' => 'form-control', 'id' => 'post_input_' . $post->id, 'placeholder' => Yii::t('PostModule.views_edit', 'Edit your post...')]) ?>
|
||||
|
||||
<?= \humhub\widgets\RichTextEditor::widget(['id' => 'post_input_' . $post->id, 'inputContent' => $post->message, 'record' => $post]); ?>
|
||||
<?= humhub\widgets\RichtextField::widget([
|
||||
'id' => 'post_input_'. $post->id,
|
||||
'placeholder' => Yii::t('PostModule.views_edit', 'Edit your post...'),
|
||||
'model' => $post,
|
||||
'attribute' => 'message'
|
||||
]); ?>
|
||||
|
||||
<div class="comment-buttons">
|
||||
|
||||
<?= \humhub\modules\file\widgets\UploadButton::widget([
|
||||
<?=
|
||||
\humhub\modules\file\widgets\UploadButton::widget([
|
||||
'id' => 'post_upload_' . $post->id,
|
||||
'model' => $post,
|
||||
'dropZone' => '#post_edit_'.$post->id.':parent',
|
||||
'preview' => '#post_upload_preview_'.$post->id,
|
||||
'progress' => '#post_upload_progress_'.$post->id
|
||||
]) ?>
|
||||
'dropZone' => '#post_edit_' . $post->id . ':parent',
|
||||
'preview' => '#post_upload_preview_' . $post->id,
|
||||
'progress' => '#post_upload_progress_' . $post->id
|
||||
])
|
||||
?>
|
||||
|
||||
<!-- editSubmit action of surrounding StreamEntry component -->
|
||||
<button type="submit" class="btn btn-default btn-sm btn-comment-submit" data-ui-loader data-action-click="editSubmit" data-action-url="<?= $post->content->container->createUrl('/post/post/edit', ['id' => $post->id]) ?>">
|
||||
<?= Yii::t('PostModule.views_edit', 'Save') ?>
|
||||
<?= Yii::t('PostModule.views_edit', 'Save') ?>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div id="post_upload_progress_<?= $post->id ?>" style="display:none;margin:10px 0px;"></div>
|
||||
|
||||
<?= \humhub\modules\file\widgets\FilePreview::widget([
|
||||
'id' => 'post_upload_preview_'.$post->id,
|
||||
|
||||
<?=
|
||||
\humhub\modules\file\widgets\FilePreview::widget([
|
||||
'id' => 'post_upload_preview_' . $post->id,
|
||||
'options' => ['style' => 'margin-top:10px'],
|
||||
'model' => $post,
|
||||
'edit' => true
|
||||
]) ?>
|
||||
|
||||
<?php CActiveForm::end(); ?>
|
||||
])
|
||||
?>
|
||||
|
||||
<?php CActiveForm::end(); ?>
|
||||
</div>
|
@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
use yii\helpers\Html;
|
||||
?>
|
||||
|
||||
<?php echo Html::textarea("message", '', array('id' => 'contentForm_message', 'class' => 'form-control autosize contentForm', 'rows' => '1', 'placeholder' => Yii::t("PostModule.widgets_views_postForm", "What's on your mind?"))); ?>
|
||||
|
||||
<?php
|
||||
|
||||
/* Modify textarea for mention input */
|
||||
echo \humhub\widgets\RichTextEditor::widget(array(
|
||||
'id' => 'contentForm_message',
|
||||
));
|
||||
?>
|
@ -1,14 +1,7 @@
|
||||
<?php
|
||||
|
||||
use yii\helpers\Html;
|
||||
?>
|
||||
|
||||
<?php echo Html::textarea("message", '', array('id' => 'contentForm_message', 'class' => 'form-control autosize contentForm', 'rows' => '1', 'placeholder' => Yii::t("PostModule.widgets_views_postForm", "What's on your mind?"))); ?>
|
||||
|
||||
<?php
|
||||
|
||||
/* Modify textarea for mention input */
|
||||
echo \humhub\widgets\RichTextEditor::widget(array(
|
||||
'id' => 'contentForm_message',
|
||||
));
|
||||
?>
|
||||
<?= humhub\widgets\RichtextField::widget([
|
||||
'id' => 'contentForm_message',
|
||||
'placeholder' => Yii::t("PostModule.widgets_views_postForm", "What's on your mind?"),
|
||||
'name' => 'message',
|
||||
'disabled' => (property_exists(Yii::$app->controller, 'contentContainer') && Yii::$app->controller->contentContainer->isArchived()),
|
||||
'disabledText' => Yii::t("PostModule.widgets_views_postForm", "This space is archived."),
|
||||
]);?>
|
||||
|
@ -1,60 +1,3 @@
|
||||
<?php
|
||||
|
||||
use yii\helpers\Html;
|
||||
|
||||
$richOutput = humhub\widgets\RichText::widget(['text' => $post->message, 'record' => $post]);
|
||||
?>
|
||||
|
||||
<span id="post-content-<?php echo $post->id; ?>" style="overflow: hidden; margin-bottom: 5px;">
|
||||
<?php print $richOutput; ?>
|
||||
<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>
|
||||
<a class="more-link-post hidden" id="more-link-post-<?php echo $post->id; ?>" data-state="down"
|
||||
style="margin: 20px 0 20px 0;" href="javascript:showMore(<?php echo $post->id; ?>);"><i
|
||||
class="fa fa-arrow-down"></i> <?php echo Yii::t('PostModule.widgets_views_post', 'Read full post...'); ?>
|
||||
</a>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
// save the count of characters
|
||||
var _words = '<?php echo strlen(strip_tags($richOutput)); ?>';
|
||||
var _postHeight = $('#post-content-<?php echo $post->id; ?>').outerHeight();
|
||||
|
||||
|
||||
if (_postHeight > 310) {
|
||||
// show more-button
|
||||
$('#more-link-post-<?php echo $post->id; ?>').removeClass('hidden');
|
||||
// set limited height
|
||||
$('#post-content-<?php echo $post->id; ?>').css({'display': 'block', 'max-height': '310px'});
|
||||
}
|
||||
});
|
||||
|
||||
function showMore(post_id) {
|
||||
|
||||
// set current state
|
||||
var _state = $('#more-link-post-' + post_id).attr('data-state');
|
||||
|
||||
if (_state == "down") {
|
||||
|
||||
$('#post-content-' + post_id).css('max-height', '2000px');
|
||||
|
||||
// set new link content
|
||||
$('#more-link-post-' + post_id).html('<i class="fa fa-arrow-up"></i> <?php echo Html::encode(Yii::t('PostModule.widgets_views_post', 'Collapse')); ?>');
|
||||
|
||||
// update link state
|
||||
$('#more-link-post-' + post_id).attr('data-state', 'up');
|
||||
|
||||
} else {
|
||||
// set back to limited length
|
||||
$('#post-content-' + post_id).css('max-height', '310px');
|
||||
|
||||
// set new link content
|
||||
$('#more-link-post-' + post_id).html('<i class="fa fa-arrow-down"></i> <?php echo Html::encode(Yii::t('PostModule.widgets_views_post', 'Read full post...')); ?>');
|
||||
|
||||
// update link state
|
||||
$('#more-link-post-' + post_id).attr('data-state', 'down');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
@ -13,7 +13,7 @@ use yii\web\AssetBundle;
|
||||
class SpaceAsset extends AssetBundle
|
||||
{
|
||||
|
||||
public $jsOptions = ['position' => \yii\web\View::POS_END];
|
||||
public $jsOptions = ['position' => \yii\web\View::POS_BEGIN];
|
||||
public $sourcePath = '@space/resources';
|
||||
public $css = [];
|
||||
public $js = [
|
||||
|
@ -361,6 +361,17 @@ class Space extends ContentContainerActiveRecord implements \humhub\modules\sear
|
||||
$this->status = self::STATUS_ENABLED;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns wether or not a Space is archived.
|
||||
*
|
||||
* @return boolean
|
||||
* @since 1.2
|
||||
*/
|
||||
public function isArchived()
|
||||
{
|
||||
return $this->status === self::STATUS_ARCHIVED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an url in space scope.
|
||||
|
@ -64,6 +64,15 @@ class DefaultController extends Controller
|
||||
$this->ownerOnly();
|
||||
$space = $this->getSpace();
|
||||
$space->archive();
|
||||
|
||||
if(Yii::$app->request->isAjax) {
|
||||
Yii::$app->response->format = 'json';
|
||||
return [
|
||||
'success' => true,
|
||||
'space' => \humhub\modules\space\controllers\BrowseController::getSpaceResult($space, true, ['isMember' => true])
|
||||
];
|
||||
}
|
||||
|
||||
return $this->redirect($space->createUrl('/space/manage'));
|
||||
}
|
||||
|
||||
@ -75,6 +84,15 @@ class DefaultController extends Controller
|
||||
$this->ownerOnly();
|
||||
$space = $this->getSpace();
|
||||
$space->unarchive();
|
||||
|
||||
if(Yii::$app->request->isAjax) {
|
||||
Yii::$app->response->format = 'json';
|
||||
return [
|
||||
'success' => true,
|
||||
'space' => \humhub\modules\space\controllers\BrowseController::getSpaceResult($space, true, ['isMember' => true])
|
||||
];
|
||||
}
|
||||
|
||||
return $this->redirect($space->createUrl('/space/manage'));
|
||||
}
|
||||
|
||||
|
@ -32,11 +32,16 @@ use humhub\modules\space\modules\manage\widgets\DefaultMenu;
|
||||
<?php echo \humhub\widgets\DataSaved::widget(); ?>
|
||||
|
||||
<div class="pull-right">
|
||||
<?php if ($model->status == Space::STATUS_ENABLED) { ?>
|
||||
<?php echo Html::a(Yii::t('SpaceModule.views_admin_edit', 'Archive'), $model->createUrl('/space/manage/default/archive'), array('class' => 'btn btn-warning', 'data-post' => 'POST')); ?>
|
||||
<?php } elseif ($model->status == Space::STATUS_ARCHIVED) { ?>
|
||||
<?php echo Html::a(Yii::t('SpaceModule.views_admin_edit', 'Unarchive'), $model->createUrl('/space/manage/default/unarchive'), array('class' => 'btn btn-warning', 'data-post' => 'POST')); ?>
|
||||
<?php } ?>
|
||||
<?php if ($model->status == Space::STATUS_ENABLED || $model->status == Space::STATUS_ARCHIVED) : ?>
|
||||
<a href="#" <?= $model->status == Space::STATUS_ARCHIVED ? 'style="display:none;"' : '' ?> class="btn btn-warning archive"
|
||||
data-action-click="space.archive" data-action-url="<?= $model->createUrl('/space/manage/default/archive') ?>" data-ui-loader>
|
||||
<?= Yii::t('SpaceModule.views_admin_edit', 'Archive') ?>
|
||||
</a>
|
||||
<a href="#" <?= $model->status == Space::STATUS_ENABLED ? 'style="display:none;"' : '' ?> class="btn btn-warning unarchive"
|
||||
data-action-click="space.unarchive" data-action-url="<?= $model->createUrl('/space/manage/default/unarchive') ?>" data-ui-loader>
|
||||
<?= Yii::t('SpaceModule.views_admin_edit', 'Unarchive') ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php ActiveForm::end(); ?>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?php //[STAMP] 25c2b4e8b8fd2158213a3465a3ef3b60
|
||||
<?php //[STAMP] 74c0c3a2840b2412882616390669c863
|
||||
namespace space\_generated;
|
||||
|
||||
// This class was automatically generated by build task
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?php //[STAMP] 0468b7e2480518455b63a22d3aa6f7c2
|
||||
<?php //[STAMP] e93907d5924b39a3cd4134cc6a6ea3a3
|
||||
namespace space\_generated;
|
||||
|
||||
// This class was automatically generated by build task
|
||||
|
@ -67,7 +67,7 @@ use yii\helpers\Url;
|
||||
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn btn-primary"
|
||||
data-action-click="ui.modal.submit"
|
||||
data-action-click="ui.modal.post"
|
||||
data-ui-loader
|
||||
data-action-url="<?= Url::to(['/space/create/invite', 'spaceId' => $space->id]) ?>">
|
||||
<?= Yii::t('SpaceModule.views_create_create', 'Next'); ?>
|
||||
|
@ -44,7 +44,11 @@ class Chooser extends Widget
|
||||
}
|
||||
|
||||
$query->joinWith('space');
|
||||
$query->where(['space_membership.user_id' => Yii::$app->user->id, 'space_membership.status' => Membership::STATUS_MEMBER]);
|
||||
$query->where([
|
||||
'space_membership.user_id' => Yii::$app->user->id,
|
||||
'space_membership.status' => Membership::STATUS_MEMBER,
|
||||
'space.status' => \humhub\modules\space\models\Space::STATUS_ENABLED
|
||||
]);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
@ -56,6 +56,9 @@ class SpaceChooserItem extends Widget
|
||||
} else if($this->isFollowing) {
|
||||
$badge = '<i class="fa fa-star badge-space pull-right type tt" title="' . Yii::t('SpaceModule.widgets_spaceChooserItem', 'You are following this space') . '" aria-hidden="true"></i>';
|
||||
$data = 'data-space-following';
|
||||
} else if($this->space->isArchived()) {
|
||||
$badge = '<i class="fa fa-history badge-space pull-right type tt" title="' . Yii::t('SpaceModule.widgets_spaceChooserItem', 'This space is archived') . '" aria-hidden="true"></i>';
|
||||
$data = 'data-space-archived';
|
||||
} else {
|
||||
$data = 'data-space-none';
|
||||
}
|
||||
|
@ -76,9 +76,9 @@ $this->registerJsConfig('space.chooser', [
|
||||
<?php if ($canCreateSpace): ?>
|
||||
<li>
|
||||
<div class="dropdown-footer">
|
||||
<?= Html::a(Yii::t('SpaceModule.widgets_views_spaceChooser', 'Create new space'),
|
||||
Url::to(['/space/create/create']),
|
||||
['class' => 'btn btn-info col-md-12', 'data-target' => '#globalModal']);?>
|
||||
<a href="#" class="btn btn-info col-md-12" data-action-click="ui.modal.load" data-action-url="<?= Url::to(['/space/create/create']) ?>">
|
||||
<?= Yii::t('SpaceModule.widgets_views_spaceChooser', 'Create new space') ?>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
@ -140,8 +140,6 @@ abstract class Stream extends Action
|
||||
if (!Yii::$app->request->isConsoleRequest) {
|
||||
$this->streamQuery->load(Yii::$app->request->get());
|
||||
|
||||
$this->setActionSettings();
|
||||
|
||||
if (Yii::$app->getRequest()->get('mode', $this->mode) === self::MODE_ACTIVITY) {
|
||||
$this->streamQuery->includes(\humhub\modules\activity\models\Activity::className());
|
||||
$this->streamQuery->query()->leftJoin('activity', 'content.object_id=activity.id AND content.object_model=:activityModel', ['activityModel' => \humhub\modules\activity\models\Activity::className()]);
|
||||
@ -154,6 +152,8 @@ abstract class Stream extends Action
|
||||
}
|
||||
}
|
||||
|
||||
$this->setActionSettings();
|
||||
|
||||
// Build query and set activeQuery.
|
||||
$this->activeQuery = $this->streamQuery->query(true);
|
||||
$this->user = $this->streamQuery->user;
|
||||
|
355
protected/humhub/modules/stream/actions/Stream_1.php
Normal file
355
protected/humhub/modules/stream/actions/Stream_1.php
Normal file
@ -0,0 +1,355 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2016 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
namespace humhub\modules\stream\actions;
|
||||
|
||||
use Yii;
|
||||
use yii\base\Action;
|
||||
use humhub\modules\content\models\Content;
|
||||
use humhub\modules\user\models\User;
|
||||
use yii\base\ActionEvent;
|
||||
use yii\base\Exception;
|
||||
|
||||
/**
|
||||
* Stream is the basic action for content streams.
|
||||
*
|
||||
* @author luke
|
||||
* @since 0.11
|
||||
*/
|
||||
abstract class Stream_old extends Action
|
||||
{
|
||||
/**
|
||||
* @event ActionEvent Event triggered before this action is run.
|
||||
* This can be used for example to customize [[activeQuery]] before it gets executed.
|
||||
* @since 1.1.1
|
||||
*/
|
||||
const EVENT_BEFORE_RUN = 'beforeRun';
|
||||
/**
|
||||
* @event ActionEvent Event triggered after this action is run.
|
||||
* @since 1.1.1
|
||||
*/
|
||||
const EVENT_AFTER_RUN = 'afterRun';
|
||||
|
||||
|
||||
/**
|
||||
* Constants used for sorting
|
||||
*/
|
||||
const SORT_CREATED_AT = 'c';
|
||||
const SORT_UPDATED_AT = 'u';
|
||||
|
||||
/**
|
||||
* Modes
|
||||
*/
|
||||
const MODE_NORMAL = "normal";
|
||||
const MODE_ACTIVITY = "activity";
|
||||
|
||||
/**
|
||||
* Maximum wall entries per request
|
||||
*/
|
||||
const MAX_LIMIT = 50;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $mode;
|
||||
|
||||
/**
|
||||
* Used to load single content entries.
|
||||
* @since 1.2
|
||||
*/
|
||||
public $contentId;
|
||||
|
||||
/**
|
||||
* First wall entry id to deliver
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $from;
|
||||
|
||||
/**
|
||||
* Sorting Mode
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $sort;
|
||||
|
||||
/**
|
||||
* Maximum wall entries to return
|
||||
* @var int
|
||||
*/
|
||||
public $limit = 4;
|
||||
|
||||
/**
|
||||
* Filters
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $filters = [];
|
||||
|
||||
/**
|
||||
* @var \yii\db\ActiveQuery
|
||||
*/
|
||||
public $activeQuery;
|
||||
|
||||
/**
|
||||
* Optional stream user
|
||||
* if no user is specified, the current logged in user will be used.
|
||||
*
|
||||
* @var User
|
||||
*/
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* @inheritdocs
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->activeQuery = Content::find();
|
||||
|
||||
// If no user is set, take current if logged in
|
||||
if ($this->user === null && !Yii::$app->user->isGuest) {
|
||||
$this->user = Yii::$app->user->getIdentity();
|
||||
}
|
||||
|
||||
// Read parameters
|
||||
if (!Yii::$app->request->isConsoleRequest) {
|
||||
$this->contentId = Yii::$app->getRequest()->get('id');
|
||||
|
||||
$from = Yii::$app->getRequest()->get('from', 0);
|
||||
if ($from != 0) {
|
||||
$this->from = (int) $from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorting
|
||||
*/
|
||||
$sort = Yii::$app->getRequest()->get('sort', Yii::$app->getModule('content')->settings->get('stream.defaultSort'));
|
||||
if ($sort === static::SORT_CREATED_AT || $sort === static::SORT_UPDATED_AT) {
|
||||
$this->sort = $sort;
|
||||
} else {
|
||||
$this->sort = static::SORT_CREATED_AT;
|
||||
}
|
||||
|
||||
$limit = Yii::$app->getRequest()->get('limit', '');
|
||||
if ($limit != "" && $limit <= self::MAX_LIMIT) {
|
||||
$this->limit = $limit;
|
||||
}
|
||||
|
||||
$mode = Yii::$app->getRequest()->get('mode', '');
|
||||
if ($mode != "" && ($mode == self::MODE_ACTIVITY || $mode == self::MODE_NORMAL)) {
|
||||
$this->mode = $mode;
|
||||
}
|
||||
|
||||
foreach (explode(',', Yii::$app->getRequest()->get('filters', "")) as $filter) {
|
||||
$this->filters[] = trim($filter);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setupCriteria();
|
||||
$this->setupFilters();
|
||||
}
|
||||
|
||||
public function setupCriteria()
|
||||
{
|
||||
$this->activeQuery->joinWith('createdBy');
|
||||
$this->activeQuery->joinWith('contentContainer');
|
||||
|
||||
$this->activeQuery->limit($this->limit);
|
||||
$this->activeQuery->andWhere(['user.status' => User::STATUS_ENABLED]);
|
||||
|
||||
/**
|
||||
* Handle Stream Mode (Normal Stream or Activity Stream)
|
||||
*/
|
||||
if ($this->mode == self::MODE_ACTIVITY) {
|
||||
$this->activeQuery->andWhere(['content.object_model' => \humhub\modules\activity\models\Activity::className()]);
|
||||
|
||||
// Dont show own activities
|
||||
if ($this->user !== null) {
|
||||
$this->activeQuery->leftJoin('activity', 'content.object_id=activity.id AND content.object_model=:activityModel', ['activityModel' => \humhub\modules\activity\models\Activity::className()]);
|
||||
$this->activeQuery->andWhere('content.created_by != :userId', array(':userId' => $this->user->id));
|
||||
}
|
||||
} else {
|
||||
$this->activeQuery->andWhere(['!=', 'content.object_model', \humhub\modules\activity\models\Activity::className()]);
|
||||
}
|
||||
|
||||
if($this->isSingleContentQuery()) {
|
||||
$this->activeQuery->andWhere(['content.id' => $this->contentId]);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Sorting
|
||||
*/
|
||||
if ($this->sort == self::SORT_UPDATED_AT) {
|
||||
$this->activeQuery->orderBy('content.stream_sort_date DESC');
|
||||
if ($this->from != "") {
|
||||
$this->activeQuery->andWhere("content.stream_sort_date < (SELECT updated_at FROM content wd WHERE wd.id=" . $this->from . ")");
|
||||
}
|
||||
} else {
|
||||
$this->activeQuery->orderBy('content.id DESC');
|
||||
if ($this->from != "")
|
||||
$this->activeQuery->andWhere("content.id < " . $this->from);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup additional filters
|
||||
*/
|
||||
public function setupFilters()
|
||||
{
|
||||
if (in_array('entry_files', $this->filters)) {
|
||||
$fileSelector = (new \yii\db\Query())
|
||||
->select(["id"])
|
||||
->from('file')
|
||||
->where('file.object_model=content.object_model AND file.object_id=content.object_id')
|
||||
->limit(1);
|
||||
$fileSelectorSql = Yii::$app->db->getQueryBuilder()->build($fileSelector)[0];
|
||||
|
||||
$this->activeQuery->andWhere('(' . $fileSelectorSql . ') IS NOT NULL');
|
||||
}
|
||||
|
||||
// Setup Post specific filters
|
||||
if (in_array('posts_links', $this->filters)) {
|
||||
$this->activeQuery->leftJoin('post', 'content.object_id=post.id AND content.object_model=:postModel', ['postModel' => \humhub\modules\post\models\Post::className()]);
|
||||
$this->activeQuery->andWhere("post.url is not null");
|
||||
}
|
||||
|
||||
// Only apply archived filter when we should load more than one entry
|
||||
if ($this->limit != 1) {
|
||||
if (!in_array('entry_archived', $this->filters)) {
|
||||
$this->activeQuery->andWhere("(content.archived != 1 OR content.archived IS NULL)");
|
||||
}
|
||||
}
|
||||
|
||||
// Show only mine items
|
||||
if (in_array('entry_mine', $this->filters) && $this->user !== null) {
|
||||
$this->activeQuery->andWhere(['content.created_by' => $this->user->id]);
|
||||
}
|
||||
|
||||
// Show only items where the current user is userinvolvedz
|
||||
if (in_array('entry_userinvolved', $this->filters) && $this->user !== null) {
|
||||
|
||||
$this->activeQuery->leftJoin('user_follow', 'content.object_model=user_follow.object_model AND content.object_id=user_follow.object_id AND user_follow.user_id = :userId', ['userId' => $this->user->id]);
|
||||
$this->activeQuery->andWhere("user_follow.id IS NOT NULL");
|
||||
}
|
||||
|
||||
if (in_array('model_posts', $this->filters)) {
|
||||
$this->activeQuery->andWhere(["content.object_model" => \humhub\modules\post\models\Post::className()]);
|
||||
}
|
||||
// Visibility filters
|
||||
if (in_array('visibility_private', $this->filters)) {
|
||||
$this->activeQuery->andWhere(['content.visibility' => Content::VISIBILITY_PRIVATE]);
|
||||
}
|
||||
if (in_array('visibility_public', $this->filters)) {
|
||||
$this->activeQuery->andWhere(['content.visibility' => Content::VISIBILITY_PUBLIC]);
|
||||
}
|
||||
}
|
||||
|
||||
public function isSingleContentQuery() {
|
||||
return $this->contentId != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
|
||||
$output = [];
|
||||
|
||||
$this->init();
|
||||
|
||||
$output['content'] = [];
|
||||
|
||||
foreach ($this->activeQuery->all() as $content) {
|
||||
try {
|
||||
$output['content'][$content->id] = static::getContentResultEntry($content);
|
||||
} catch(Exception $e) {
|
||||
// We do not want to kill the stream action in prod environments in case the rendering of an entry fails.
|
||||
if(YII_ENV_PROD) {
|
||||
Yii::error($e);
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
$output['total'] = count($output['content']);
|
||||
$output['isLast'] = ($output['total'] < $this->activeQuery->limit);
|
||||
$output['contentOrder'] = array_keys($output['content']);
|
||||
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array contains all informations required to display a content
|
||||
* in stream.
|
||||
*
|
||||
* @param Content $content the content
|
||||
* @return array
|
||||
*/
|
||||
public static function getContentResultEntry(Content $content)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
// Get Underlying Object (e.g. Post, Poll, ...)
|
||||
$underlyingObject = $content->getPolymorphicRelation();
|
||||
if ($underlyingObject === null) {
|
||||
throw new Exception('Could not get contents underlying object!');
|
||||
}
|
||||
$underlyingObject->populateRelation('content', $content);
|
||||
|
||||
$result['output'] = Yii::$app->controller->renderAjax('@humhub/modules/content/views/layouts/wallEntry', [
|
||||
'entry' => $content,
|
||||
'user' => $underlyingObject->content->createdBy,
|
||||
'object' => $underlyingObject,
|
||||
'content' => $underlyingObject->getWallOut()
|
||||
], true);
|
||||
|
||||
$result['sticked'] = (boolean) $content->sticked;
|
||||
$result['archived'] = (boolean) $content->archived;
|
||||
$result['guid'] = $content->guid;
|
||||
$result['id'] = $content->id;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is inital stream requests (show first stream content)
|
||||
*
|
||||
* @return boolean Is initial request
|
||||
*/
|
||||
protected function isInitialRequest()
|
||||
{
|
||||
return ($this->from == '' && $this->limit != 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called right before `run()` is executed.
|
||||
* You may override this method to do preparation work for the action run.
|
||||
* If the method returns false, it will cancel the action.
|
||||
*
|
||||
* @return boolean whether to run the action.
|
||||
*/
|
||||
protected function beforeRun()
|
||||
{
|
||||
$event = new ActionEvent($this);
|
||||
$this->trigger(self::EVENT_BEFORE_RUN, $event);
|
||||
return $event->isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called right after `run()` is executed.
|
||||
* You may override this method to do post-processing work for the action run.
|
||||
*/
|
||||
protected function afterRun()
|
||||
{
|
||||
$event = new ActionEvent($this);
|
||||
$this->trigger(self::EVENT_AFTER_RUN, $event);
|
||||
}
|
||||
}
|
@ -238,8 +238,10 @@ humhub.module('stream', function(module, require, $) {
|
||||
// Sinc the response does not only include the node itself we have to search it.
|
||||
that.$ = $newEntry.find(DATA_STREAM_ENTRY_SELECTOR)
|
||||
.addBack(DATA_STREAM_ENTRY_SELECTOR);
|
||||
$newEntry.fadeIn(resolve);
|
||||
that.apply();
|
||||
$newEntry.fadeIn('fast', function() {
|
||||
that.apply();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@ -294,6 +296,8 @@ humhub.module('stream', function(module, require, $) {
|
||||
if(response.success) {
|
||||
that.reload().then(function() {
|
||||
module.log.success('success.unarchive', true);
|
||||
}).catch(function(err) {
|
||||
module.log.error('error.default', true);
|
||||
});
|
||||
}
|
||||
}).catch(function(e) {
|
||||
@ -327,7 +331,7 @@ humhub.module('stream', function(module, require, $) {
|
||||
}
|
||||
|
||||
this.$stream = this.$;
|
||||
|
||||
|
||||
//Cache some stream relevant data/nodes
|
||||
this.url = this.$.data(DATA_STREAM_URL);
|
||||
this.$content = this.$.find(this.cfg['contentSelector']);
|
||||
@ -417,31 +421,11 @@ humhub.module('stream', function(module, require, $) {
|
||||
this.loading = false;
|
||||
this.$content.empty();
|
||||
this.$.hide();
|
||||
//this.$.find(".s2_single").hide();
|
||||
this.hideLoader();
|
||||
this.$filter.hide();
|
||||
//this.$filter.hide();
|
||||
this.$.trigger('humhub:modules:stream:clear', this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads a single stream entry by a given content id.
|
||||
*
|
||||
* @param {type} contentId
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Stream.prototype.loadEntry = function(contentId, cfg) {
|
||||
cfg = cfg || {};
|
||||
cfg['contentId'] = contentId;
|
||||
|
||||
var that = this;
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
that.loadEntries(cfg).then(function($entryNode) {
|
||||
resolve($entryNode);
|
||||
}).catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Reloads a given entry either by providing the contentId or a StreamEntry instance.
|
||||
* This function returns a Promise instance.
|
||||
@ -456,7 +440,8 @@ humhub.module('stream', function(module, require, $) {
|
||||
|
||||
if(!entry) {
|
||||
module.log.warn('Attempt to reload non existing entry');
|
||||
return reject();
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
|
||||
var contentId = entry.getKey();
|
||||
@ -471,6 +456,18 @@ humhub.module('stream', function(module, require, $) {
|
||||
}, reject);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads a single stream entry by a given content id.
|
||||
*
|
||||
* @param {type} contentId
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Stream.prototype.loadEntry = function(contentId, cfg) {
|
||||
cfg = cfg || {};
|
||||
cfg['contentId'] = contentId;
|
||||
return this.loadEntries(cfg);
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads new entries to a stream by the given stream settings.
|
||||
@ -491,7 +488,7 @@ humhub.module('stream', function(module, require, $) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var $result;
|
||||
// Don't proceed if stream is already loading
|
||||
if(that.loading || that.lastEntryLoaded) {
|
||||
if(!cfg.contentId && (that.loading || that.lastEntryLoaded)) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
@ -583,17 +580,22 @@ humhub.module('stream', function(module, require, $) {
|
||||
Stream.prototype.prependEntry = function(html) {
|
||||
var $html = $(html).hide();
|
||||
this.$content.prepend($html);
|
||||
additions.applyTo($html);
|
||||
$html.fadeIn();
|
||||
this.onChange();
|
||||
var that = this;
|
||||
$html.fadeIn('fast', function() {
|
||||
additions.applyTo($html);
|
||||
that.onChange();
|
||||
});
|
||||
};
|
||||
|
||||
Stream.prototype.appendEntry = function(html) {
|
||||
var $html = $(html).hide();
|
||||
this.$content.append($html);
|
||||
additions.applyTo($html);
|
||||
$html.fadeIn();
|
||||
this.onChange();
|
||||
var that = this;
|
||||
$html.fadeIn('fast', function() {
|
||||
additions.applyTo($html);
|
||||
that.onChange();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -614,7 +616,6 @@ humhub.module('stream', function(module, require, $) {
|
||||
result += response.content[key].output;
|
||||
});
|
||||
|
||||
|
||||
var $result = $(result).hide();
|
||||
|
||||
if(cfg['preventInsert']) {
|
||||
@ -778,7 +779,8 @@ humhub.module('stream', function(module, require, $) {
|
||||
this.$.find('.emptyStreamMessage').show();
|
||||
this.$filter.hide();
|
||||
} else if(!hasEntries) {
|
||||
this.$.find('.emptyFilterStreamMessage').hide();
|
||||
this.$.find('.emptyFilterStreamMessage').show();
|
||||
this.$filter.show();
|
||||
} else if(!this.isShowSingleEntry()) {
|
||||
this.$filter.show();
|
||||
this.$.find('.emptyStreamMessage').hide();
|
||||
|
@ -28,7 +28,7 @@ class StreamQuery extends \yii\base\Model
|
||||
const FILTER_FILES = "entry_files";
|
||||
const FILTER_ARCHIVED = "entry_archived";
|
||||
const FILTER_MINE = "entry_mine";
|
||||
const FILTER_INVOLVED = "entry_userinvoled";
|
||||
const FILTER_INVOLVED = "entry_userinvolved";
|
||||
const FILTER_PRIVATE = "visibility_private";
|
||||
const FILTER_PUBLIC = "visibility_public";
|
||||
|
||||
@ -50,7 +50,7 @@ class StreamQuery extends \yii\base\Model
|
||||
* @var array Content type filter
|
||||
*/
|
||||
protected $_excludes;
|
||||
|
||||
|
||||
/**
|
||||
* Used to deactivate the default exclusion of activity entries
|
||||
* @var type
|
||||
@ -113,9 +113,8 @@ class StreamQuery extends \yii\base\Model
|
||||
* @var \yii\db\ActiveQuery
|
||||
*/
|
||||
protected $_query;
|
||||
|
||||
private $_built = false;
|
||||
|
||||
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
@ -148,7 +147,7 @@ class StreamQuery extends \yii\base\Model
|
||||
$instance = new static();
|
||||
return $instance->includes($includes)->excludes($excludes);
|
||||
}
|
||||
|
||||
|
||||
public function init()
|
||||
{
|
||||
$this->_query = Content::find();
|
||||
@ -158,7 +157,7 @@ class StreamQuery extends \yii\base\Model
|
||||
|
||||
public function content($contentId)
|
||||
{
|
||||
if(!is_int($contentId)) {
|
||||
if (!is_int($contentId)) {
|
||||
$this->contentId = $contentId;
|
||||
}
|
||||
return $this;
|
||||
@ -170,42 +169,42 @@ class StreamQuery extends \yii\base\Model
|
||||
$this->checkUser();
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function filters($filters = [])
|
||||
{
|
||||
$this->filters = (is_string($filters)) ? [$this->filters] : $this->filters;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function addFilter($filters)
|
||||
{
|
||||
if(!is_string($filters)) {
|
||||
if (!is_string($filters)) {
|
||||
$this->filters[] = $filters;
|
||||
} else if(is_array($filters)) {
|
||||
} else if (is_array($filters)) {
|
||||
$this->filters = \yii\helpers\ArrayHelper::merge($this->filters, $filters);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function includes($includes = [])
|
||||
{
|
||||
if(is_string($includes)) {
|
||||
if (is_string($includes)) {
|
||||
$this->_includes = [$includes];
|
||||
} else if(is_array ($includes)) {
|
||||
} else if (is_array($includes)) {
|
||||
$this->_includes = $includes;
|
||||
}
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function excludes($types = [])
|
||||
{
|
||||
if(is_string($types)) {
|
||||
if (is_string($types)) {
|
||||
$this->_excludes = [$types];
|
||||
} else if(is_array ($types)) {
|
||||
} else if (is_array($types)) {
|
||||
$this->_excludes = $types;
|
||||
}
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -234,18 +233,20 @@ class StreamQuery extends \yii\base\Model
|
||||
}
|
||||
|
||||
public function query($build = false)
|
||||
{
|
||||
if($build) {
|
||||
{
|
||||
if ($build) {
|
||||
$this->setupQuery();
|
||||
}
|
||||
|
||||
return $this->_query;
|
||||
}
|
||||
|
||||
|
||||
public function all()
|
||||
{
|
||||
if(!$this->_built) {
|
||||
if (!$this->_built) {
|
||||
$this->setupQuery();
|
||||
}
|
||||
|
||||
return $this->_query->all();
|
||||
}
|
||||
|
||||
@ -274,7 +275,7 @@ class StreamQuery extends \yii\base\Model
|
||||
$this->sort = static::SORT_CREATED_AT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function checkFrom()
|
||||
{
|
||||
if (empty($this->from)) {
|
||||
@ -335,13 +336,14 @@ class StreamQuery extends \yii\base\Model
|
||||
$this->_query->andWhere(['content.created_by' => $this->originator->id]);
|
||||
}
|
||||
}
|
||||
|
||||
public function isFIlter($filter) {
|
||||
|
||||
public function isFIlter($filter)
|
||||
{
|
||||
return in_array($filter, $this->filters);
|
||||
}
|
||||
|
||||
protected function setDefaultFilter()
|
||||
{
|
||||
{
|
||||
if ($this->isFilter(self::FILTER_FILES)) {
|
||||
$this->filterFile();
|
||||
}
|
||||
@ -349,14 +351,14 @@ class StreamQuery extends \yii\base\Model
|
||||
// Only apply archived filter when we should load more than one entry
|
||||
if (!$this->isSingleContentQuery() && !$this->isFilter(self::FILTER_ARCHIVED)) {
|
||||
$this->unFilterArchived();
|
||||
}
|
||||
}
|
||||
|
||||
// Show only mine items
|
||||
if ($this->isFilter(self::FILTER_MINE)) {
|
||||
$this->filterMine();
|
||||
}
|
||||
|
||||
// Show only items where the current user is involed
|
||||
// Show only items where the current user is invovled
|
||||
if ($this->isFilter(self::FILTER_INVOLVED)) {
|
||||
$this->filterInvolved();
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ use stream\AcceptanceTester;
|
||||
|
||||
class StreamCest
|
||||
{
|
||||
|
||||
public function testDeletePost(AcceptanceTester $I)
|
||||
{
|
||||
$I->amUser();
|
||||
@ -14,8 +13,8 @@ class StreamCest
|
||||
$I->wantToTest('the deletion of a stream entry');
|
||||
$I->amGoingTo('create a new post and delete it afterwards');
|
||||
|
||||
$I->click('#contentForm_message_contenteditable');
|
||||
$I->fillField('#contentForm_message_contenteditable', 'This is my stream test post!');
|
||||
$I->click('#contentForm_message');
|
||||
$I->fillField('#contentForm_message', 'This is my stream test post!');
|
||||
$I->click('#post_submit_button');
|
||||
|
||||
$newEntrySelector = '[data-content-key="12"]';
|
||||
@ -29,8 +28,8 @@ class StreamCest
|
||||
$I->click('Delete');
|
||||
|
||||
$I->waitForElementVisible('#globalModalConfirm', 5);
|
||||
$I->see('Do you really want to perform this action?');
|
||||
$I->click('Confirm');
|
||||
$I->see('Confirm post deletion');
|
||||
$I->click('Delete', '#globalModalConfirm');
|
||||
|
||||
$I->seeSuccess('The content has been deleted');
|
||||
}
|
||||
@ -144,16 +143,29 @@ class StreamCest
|
||||
$I->waitForElementVisible($newEntrySelector);
|
||||
$I->see('This is my first stream test post', '.wall-entry');
|
||||
|
||||
$I->amGoingTo('edit load the edit form');
|
||||
$I->click('.preferences', $newEntrySelector);
|
||||
$I->waitForText('Edit', 10);
|
||||
$I->click('Edit', $newEntrySelector);
|
||||
|
||||
$I->waitForElementVisible($newEntrySelector . ' .content_edit', 20);
|
||||
$I->amGoingTo('cancel my edit');
|
||||
$I->click('.preferences', $newEntrySelector);
|
||||
$I->waitForText('Cancel Edit', 10);
|
||||
$I->click('Cancel Edit', $newEntrySelector);
|
||||
$I->waitForElementNotVisible($newEntrySelector . ' .content_edit', 20);
|
||||
$I->waitForElementVisible($newEntrySelector . ' .content', 20);
|
||||
$I->see('This is my first stream test post!', $newEntrySelector);
|
||||
|
||||
$I->amGoingTo('edit my new post');
|
||||
$I->click('.preferences', $newEntrySelector);
|
||||
$I->waitForText('Edit', 10);
|
||||
$I->click('Edit', $newEntrySelector);
|
||||
|
||||
|
||||
$I->waitForElementVisible($newEntrySelector . ' .content_edit', 20);
|
||||
$I->fillField($newEntrySelector . ' [contenteditable]', 'This is my edited post!');
|
||||
$I->click('Save', $newEntrySelector);
|
||||
;
|
||||
|
||||
|
||||
$I->seeSuccess('Saved');
|
||||
$I->seeElement($newEntrySelector);
|
||||
$I->see('This is my edited post!', $newEntrySelector);
|
||||
@ -182,13 +194,52 @@ class StreamCest
|
||||
$I->click('Delete');
|
||||
|
||||
$I->waitForElementVisible('#globalModalConfirm', 5);
|
||||
$I->see('Do you really want to perform this action?');
|
||||
$I->click('Confirm');
|
||||
$I->see('Confirm post deletion');
|
||||
$I->click('Delete', '#globalModalConfirm');
|
||||
|
||||
$I->seeSuccess('The content has been deleted');
|
||||
$I->see('This space is still empty!');
|
||||
$I->dontSeeElement('#filter');
|
||||
}
|
||||
|
||||
public function testFilterInvolved(AcceptanceTester $I)
|
||||
{
|
||||
$I->amUser();
|
||||
$I->amOnSpace2();
|
||||
$I->waitForElementVisible('#filter');
|
||||
$I->click('.stream-filter', '#filter');
|
||||
$I->waitForElementVisible('#filter_entry_userinvolved');
|
||||
$I->click('#filter_entry_userinvolved');
|
||||
$I->waitForText('No matches with your selected filters!');
|
||||
|
||||
$I->createPost('Involved Post.');
|
||||
$I->dontSee('No matches with your selected filters!');
|
||||
|
||||
$I->amGoingTo('Reset filter');
|
||||
$I->click('.stream-filter', '#filter');
|
||||
$I->waitForElementVisible('#filter_entry_userinvolved');
|
||||
$I->click('#filter_entry_userinvolved');
|
||||
|
||||
$I->waitForElementVisible('[data-content-key="10"]');
|
||||
|
||||
$I->click('Comment', '[data-content-key="10"]');
|
||||
$I->waitForElementVisible('#newCommentForm_humhubmodulespostmodelsPost_10');
|
||||
$I->fillField('#newCommentForm_humhubmodulespostmodelsPost_10', 'My Comment');
|
||||
$I->click('Send', '#comment_create_form_humhubmodulespostmodelsPost_10');
|
||||
$I->waitForText('My Comment', 30, '#comment_humhubmodulespostmodelsPost_10 .comment');
|
||||
|
||||
$I->click('Like', '[data-content-key="11"]');
|
||||
|
||||
$I->click('.stream-filter', '#filter');
|
||||
$I->waitForElementVisible('#filter_entry_userinvolved');
|
||||
$I->click('#filter_entry_userinvolved');
|
||||
$I->wait(1);
|
||||
$I->waitForText('Involved Post.');
|
||||
|
||||
$I->seeElement('[data-content-key="10"]');
|
||||
$I->seeElement('[data-content-key="11"]');
|
||||
$I->seeElement('[data-content-key="12"]');
|
||||
}
|
||||
|
||||
public function testSortStream(AcceptanceTester $I)
|
||||
{
|
||||
|
@ -89,7 +89,7 @@ class StreamViewer extends \yii\base\Widget
|
||||
|
||||
// Add default Filters
|
||||
if (count($this->filters) === 0) {
|
||||
$this->filters['filter_entry_userinvoled'] = Yii::t('ContentModule.widgets_views_stream', 'Where I´m involved');
|
||||
$this->filters['filter_entry_userinvolved'] = Yii::t('ContentModule.widgets_views_stream', 'Where I´m involved');
|
||||
$this->filters['filter_entry_mine'] = Yii::t('ContentModule.widgets_views_stream', 'Created by me');
|
||||
$this->filters['filter_entry_files'] = Yii::t('ContentModule.widgets_views_stream', 'Content with attached files');
|
||||
$this->filters['filter_posts_links'] = Yii::t('ContentModule.widgets_views_stream', 'Posts with links');
|
||||
@ -135,7 +135,7 @@ class StreamViewer extends \yii\base\Widget
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
return $this->render('stream', ['streamUrl' => $this->getStreamUrl(), 'showFilters' => $this->showFilters, 'filters' => $this->filters]);
|
||||
return $this->render('stream', ['streamUrl' => $this->getStreamUrl(), 'showFilters' => $this->showFilters, 'filters' => $this->filters, '', 'contentContainer' => $this->contentContainer]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
<?php
|
||||
|
||||
use yii\helpers\Url;
|
||||
use yii\web\View;
|
||||
|
||||
\humhub\modules\stream\assets\StreamAsset::register($this);
|
||||
|
||||
@ -27,6 +26,9 @@ $this->registerJsVar('defaultStreamSort', ($defaultStreamSort != '') ? $defaultS
|
||||
?>
|
||||
|
||||
<!-- Stream filter section -->
|
||||
<?php if($contentContainer && $contentContainer->isArchived()) : ?>
|
||||
<span class="label label-warning pull-right" style="margin-top:10px;"><?php echo Yii::t('ContentModule.widgets_views_label', 'Archived'); ?></span>
|
||||
<?php endif; ?>
|
||||
<?php if ($this->context->showFilters) { ?>
|
||||
<ul class="nav nav-tabs wallFilterPanel" id="filter" style="display: none;">
|
||||
<li class=" dropdown">
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?php //[STAMP] 890a71cd7f21af7a261c0d3eceef09fb
|
||||
<?php //[STAMP] 11d1dbef7e50b480be5fa82a7c393e3e
|
||||
namespace user\_generated;
|
||||
|
||||
// This class was automatically generated by build task
|
||||
|
@ -19,4 +19,5 @@ modules:
|
||||
url: http://localhost:8080/
|
||||
browser: phantomjs
|
||||
window_size: 1024x768
|
||||
restart: true
|
||||
lang: en
|
||||
|
@ -2,7 +2,6 @@
|
||||
namespace user\acceptance;
|
||||
|
||||
use user\AcceptanceTester;
|
||||
use tests\codeception\_pages\DirectoryPage;
|
||||
use tests\codeception\_pages\AccountSettingsPage;
|
||||
|
||||
class ProfileCest
|
||||
@ -16,7 +15,9 @@ class ProfileCest
|
||||
$I->clickAccountDropDown();
|
||||
$I->click('Account settings');
|
||||
$I->expectTo('see the profile edit form');
|
||||
$I->seeElement('#profile-tabs');
|
||||
|
||||
$I->waitForElementVisible('#profile-tabs', 20);
|
||||
//$I->seeElement();
|
||||
|
||||
$I->amGoingTo('fill only my firstname');
|
||||
$I->fillField('#profile-firstname', 'MyFirstName');
|
||||
@ -29,7 +30,7 @@ class ProfileCest
|
||||
$I->amGoingTo('fill all required fields plus birthday and hide year field');
|
||||
$I->fillField('#profile-lastname', 'MyLastName');
|
||||
$I->fillField('#profile-birthday', '4/16/87');
|
||||
$I->click('/html/body/div[3]/div/div[2]/div/div[3]/form/div/div[1]/div[11]/label/div'); // Hide year in profile
|
||||
$I->click('.field-profile-birthday_hide_year div'); // Hide year in profile
|
||||
|
||||
$I->scrollToTop();
|
||||
|
||||
@ -61,10 +62,11 @@ class ProfileCest
|
||||
$I->wantTo('ensure that my profile works as expected.');
|
||||
|
||||
$I->amUser2();
|
||||
DirectoryPage::openBy($I);
|
||||
$I->click('Members');
|
||||
$directory = $I->amOnDirectory();
|
||||
$directory->clickMembers();
|
||||
$I->click('User1');
|
||||
|
||||
$I->waitForText('Profile menu');
|
||||
$I->expectTo('see the profile of User2');
|
||||
$I->see('Follow');
|
||||
$I->see('Stream');
|
||||
@ -75,15 +77,13 @@ class ProfileCest
|
||||
$I->see('Peter');
|
||||
$I->see('Tester');
|
||||
|
||||
AccountSettingsPage::openBy($I);
|
||||
$I->click('Security');
|
||||
#$I->wait(2);
|
||||
$accountSettings = AccountSettingsPage::openBy($I);
|
||||
$accountSettings->clickSecurity();
|
||||
$I->selectOption('select[data-attribute0*=ViewAboutPage]', 'Deny');
|
||||
|
||||
$I->amUser1(true);
|
||||
DirectoryPage::openBy($I);
|
||||
$I->click('Members');
|
||||
$directory = $I->amOnDirectory();
|
||||
$directory->clickMembers();
|
||||
$I->click('User2');
|
||||
|
||||
}
|
||||
}
|
@ -9,7 +9,9 @@ class UserFollowCest
|
||||
{
|
||||
$I->wantTo('test the user follow by directory link');
|
||||
|
||||
|
||||
$I->amUser1();
|
||||
//$I->wait(300);
|
||||
$I->amOnProfile();
|
||||
|
||||
$I->createPost('New User1 profile post');
|
||||
|
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
namespace humhub\modules\user\tests\codeception\fixtures;
|
||||
|
||||
use yii\test\ActiveFixture;
|
||||
|
||||
class ProfileFixture extends ActiveFixture
|
||||
{
|
||||
|
||||
public $modelClass = 'humhub\modules\user\models\Profile';
|
||||
public $dataFile = '@modules/user/tests/codeception/fixtures/data/profile.php';
|
||||
|
||||
}
|
@ -16,8 +16,10 @@ class UserFixture extends ActiveFixture
|
||||
public $modelClass = 'humhub\modules\user\models\User';
|
||||
public $dataFile = '@modules/user/tests/codeception/fixtures/data/user.php';
|
||||
public $depends = [
|
||||
'humhub\modules\user\tests\codeception\fixtures\UserProfileFixture',
|
||||
'humhub\modules\content\tests\codeception\fixtures\ContentContainerFixture',
|
||||
'humhub\modules\user\tests\codeception\fixtures\UserPasswordFixture',
|
||||
'humhub\modules\user\tests\codeception\fixtures\UserFollowFixture',
|
||||
'humhub\modules\user\tests\codeception\fixtures\GroupFixture'
|
||||
];
|
||||
|
||||
|
@ -10,13 +10,10 @@ namespace humhub\modules\user\tests\codeception\fixtures;
|
||||
|
||||
use yii\test\ActiveFixture;
|
||||
|
||||
class UserFixture extends ActiveFixture
|
||||
class UserProfileFixture extends ActiveFixture
|
||||
{
|
||||
|
||||
public $modelClass = 'humhub\modules\user\models\Profile';
|
||||
public $dataFile = '@modules/user/tests/codeception/fixtures/data/profile.php';
|
||||
public $depends = [
|
||||
'humhub\modules\user\tests\codeception\fixtures\UserProfileFixture'
|
||||
];
|
||||
|
||||
}
|
||||
|
@ -12,5 +12,11 @@ class AccountSettingsPage extends BasePage
|
||||
{
|
||||
|
||||
public $route = 'user/account/edit';
|
||||
|
||||
public function clickSecurity()
|
||||
{
|
||||
$this->actor->click('Security');
|
||||
$this->actor->waitForText('Security settings', 30);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,5 +12,11 @@ class DirectoryPage extends BasePage
|
||||
{
|
||||
|
||||
public $route = 'directory/directory';
|
||||
|
||||
public function clickMembers()
|
||||
{
|
||||
$this->actor->click('Member');
|
||||
$this->actor->waitForText('Member directory', 30);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ class ProfilePage extends BasePage
|
||||
public function clickAbout()
|
||||
{
|
||||
$this->actor->click('About');
|
||||
$this->actor->waitForText('About this user', 30);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -64,8 +64,8 @@ class AcceptanceTester extends \Codeception\Actor
|
||||
|
||||
public function createPost($text)
|
||||
{
|
||||
$this->click('#contentForm_message_contenteditable');
|
||||
$this->fillField('#contentForm_message_contenteditable', $text);
|
||||
$this->click('#contentForm_message');
|
||||
$this->fillField('#contentForm_message', $text);
|
||||
$this->click('#post_submit_button');
|
||||
$this->waitForText($text, 30, '.wall-entry');
|
||||
}
|
||||
@ -158,6 +158,11 @@ class AcceptanceTester extends \Codeception\Actor
|
||||
$this->jsClick('#account-dropdown-link');
|
||||
$this->wait(2);
|
||||
}
|
||||
|
||||
public function amOnDirectory()
|
||||
{
|
||||
return tests\codeception\_pages\DirectoryPage::openBy($this);
|
||||
}
|
||||
|
||||
public function amOnProfile()
|
||||
{
|
||||
|
@ -106,7 +106,6 @@ class DynamicFixtureHelper extends Module
|
||||
{
|
||||
return [
|
||||
'user' => ['class' => \humhub\modules\user\tests\codeception\fixtures\UserFixture::className()],
|
||||
'profile' => ['class' => \humhub\modules\user\tests\codeception\fixtures\ProfileFixture::className()],
|
||||
'group' => ['class' => \humhub\modules\user\tests\codeception\fixtures\GroupFixture::className()],
|
||||
'group_permission' => ['class' => \humhub\modules\user\tests\codeception\fixtures\GroupPermissionFixture::className()],
|
||||
'settings' => ['class' => \humhub\tests\codeception\fixtures\SettingFixture::className()],
|
||||
|
@ -61,6 +61,13 @@ class CoreJsConfig extends Widget
|
||||
'error.unknown' => Yii::t('base', 'No error information given.'),
|
||||
]
|
||||
],
|
||||
'ui.richtext' => [
|
||||
'emoji.url' => Yii::getAlias('@web/img/emoji/'),
|
||||
'text' => [
|
||||
'info.minInput' => Yii::t('base', 'Please type at least 3 characters'),
|
||||
'info.loading' => Yii::t('base', 'Loading...'),
|
||||
]
|
||||
],
|
||||
'log' => [
|
||||
'traceLevel' => (YII_DEBUG) ? 'DEBUG' : 'INFO',
|
||||
'text' => [
|
||||
@ -86,6 +93,12 @@ class CoreJsConfig extends Widget
|
||||
'showMore' => Yii::t('base', 'Show more'),
|
||||
]
|
||||
],
|
||||
'post' => [
|
||||
'text' => [
|
||||
'default.readMore' => Yii::t('PostModule.widgets_views_post', 'Read full post...'),
|
||||
'default.readLess' => Yii::t('PostModule.widgets_views_post', 'Collapse'),
|
||||
]
|
||||
],
|
||||
'content' => [
|
||||
'modal' => [
|
||||
'permalink' => [
|
||||
|
@ -25,6 +25,7 @@ class GlobalConfirmModal extends \yii\base\Widget
|
||||
{
|
||||
return \humhub\widgets\Modal::widget([
|
||||
'id' => 'globalModalConfirm',
|
||||
'jsWidget' => 'ui.modal.ConfirmModal',
|
||||
'size' => 'extra-small',
|
||||
'centerText' => true,
|
||||
'backdrop' => false,
|
||||
|
@ -61,16 +61,12 @@ class JsWidget extends Widget
|
||||
{
|
||||
$attributes = $this->getAttributes();
|
||||
$attributes['data'] = $this->getData();
|
||||
$attributes['id'] = $this->id;
|
||||
$attributes['id'] = $this->getId();
|
||||
|
||||
$this->setDefaultOptions();
|
||||
|
||||
$result = \yii\helpers\ArrayHelper::merge($attributes, $this->options);
|
||||
|
||||
if(empty($result['id'])) {
|
||||
$result['id'] = $this->getId(true);
|
||||
}
|
||||
|
||||
if(!$this->visible) {
|
||||
if(isset($result['style'])) {
|
||||
$result['style'] .= ';display:none;';
|
||||
@ -86,7 +82,7 @@ class JsWidget extends Widget
|
||||
{
|
||||
// Set event data
|
||||
foreach($this->events as $event => $handler) {
|
||||
$this->options['data']['action-'.$event] = $handler;
|
||||
$this->options['data']['widget-action-'.$event] = $handler;
|
||||
}
|
||||
|
||||
$this->options['data']['ui-widget'] = $this->jsWidget;
|
||||
@ -96,6 +92,14 @@ class JsWidget extends Widget
|
||||
}
|
||||
}
|
||||
|
||||
public function getId($autoGenerate = true)
|
||||
{
|
||||
if($this->id) {
|
||||
return $this->id;
|
||||
}
|
||||
return parent::getId($autoGenerate);
|
||||
}
|
||||
|
||||
protected function getData()
|
||||
{
|
||||
return [];
|
||||
|
@ -15,13 +15,12 @@ use yii\base\Widget;
|
||||
*
|
||||
* @author luke
|
||||
*/
|
||||
class Modal extends Widget
|
||||
{
|
||||
/**
|
||||
* Unique modal container id
|
||||
* @var type
|
||||
class Modal extends JsWidget
|
||||
{
|
||||
/*
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $id;
|
||||
public $jsWidget = 'ui.modal.Modal';
|
||||
|
||||
/**
|
||||
* Header text
|
||||
@ -104,22 +103,45 @@ class Modal extends Widget
|
||||
|
||||
public function run()
|
||||
{
|
||||
$modalData = '';
|
||||
$modalData .= !$this->backdrop ? 'data-backdrop="static"' : '';
|
||||
$modalData .= !$this->keyboard ? 'data-keyboard="false"' : '';
|
||||
$modalData .= $this->show ? 'data-show="true"' : '';
|
||||
|
||||
return $this->render('modal', [
|
||||
'id' => $this->id,
|
||||
'options' => $this->getOptions(),
|
||||
'header' => $this->header,
|
||||
'body' => $this->body,
|
||||
'footer' => $this->footer,
|
||||
'animation' => $this->animation,
|
||||
'size' => $this->size,
|
||||
'modalData' => $modalData,
|
||||
'centerText' => $this->centerText,
|
||||
'initialLoader' => $this->initialLoader
|
||||
]);
|
||||
}
|
||||
|
||||
public function getAttributes()
|
||||
{
|
||||
return [
|
||||
'class' => "modal",
|
||||
'tabindex' => "-1",
|
||||
'role' => "dialog",
|
||||
'aria-hidden' => "true"
|
||||
];
|
||||
}
|
||||
|
||||
public function getData()
|
||||
{
|
||||
$result = [];
|
||||
if(!$this->backdrop) {
|
||||
$result['backdrop'] = 'static';
|
||||
}
|
||||
|
||||
if(!$this->keyboard) {
|
||||
$result['keyboard'] = 'false';
|
||||
}
|
||||
|
||||
if($this->show) {
|
||||
$result['show'] = 'true';
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ use humhub\libs\ParameterEvent;
|
||||
*
|
||||
* @author luke
|
||||
*/
|
||||
class RichText extends \humhub\components\Widget
|
||||
class RichText extends JsWidget
|
||||
{
|
||||
|
||||
/**
|
||||
@ -166,7 +166,7 @@ REGEXP;
|
||||
$user = \humhub\modules\user\models\User::findOne(['guid' => $hit[2]]);
|
||||
if ($user !== null) {
|
||||
if ($buildAnchors) {
|
||||
return ' <span contenteditable="false"><a href="' . $user->getUrl() . '" target="_self" class="atwho-user" data-user-guid="@-u' . $user->guid . '">@' . Html::encode($user->getDisplayName()) . '​</a></span>' . $hit[3];
|
||||
return ' <span contenteditable="false"><a href="' . $user->getUrl() . '" target="_self" class="atwho-user" data-guid="@-u' . $user->guid . '">@' . Html::encode($user->getDisplayName()) . '​</a></span>' . $hit[3];
|
||||
}
|
||||
return " @" . Html::encode($user->getDisplayName()) . $hit[3];
|
||||
}
|
||||
@ -175,7 +175,7 @@ REGEXP;
|
||||
|
||||
if ($space !== null) {
|
||||
if ($buildAnchors) {
|
||||
return ' <span contenteditable="false"><a href="' . $space->getUrl() . '" target="_self" class="atwho-user" data-user-guid="@-s' . $space->guid . '">@' . Html::encode($space->name) . '​</a></span>' . $hit[3];
|
||||
return ' <span contenteditable="false"><a href="' . $space->getUrl() . '" target="_self" class="atwho-user" data-guid="@-s' . $space->guid . '">@' . Html::encode($space->name) . '​</a></span>' . $hit[3];
|
||||
}
|
||||
return " @" . Html::encode($space->name) . $hit[3];
|
||||
}
|
||||
|
191
protected/humhub/widgets/RichtextField.php
Normal file
191
protected/humhub/widgets/RichtextField.php
Normal file
@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
namespace humhub\widgets;
|
||||
|
||||
use Yii;
|
||||
use yii\helpers\Html;
|
||||
use \yii\helpers\Url;
|
||||
use humhub\widgets\RichText;
|
||||
|
||||
/**
|
||||
* @package humhub.modules_core.user.widgets
|
||||
* @since 1.2
|
||||
* @author buddha
|
||||
*/
|
||||
class RichtextField extends JsWidget
|
||||
{
|
||||
|
||||
/**
|
||||
* Defines the javascript picker implementation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $jsWidget = 'ui.richtext.Richtext';
|
||||
|
||||
/**
|
||||
* Minimum character input before triggering search query.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $minInput = 3;
|
||||
|
||||
/**
|
||||
* If the ActiveForm is set, it will be used to create the picker field,
|
||||
* otherwise it's created by Html::activeDropDownList
|
||||
*
|
||||
* @var \yii\widgets\ActiveForm
|
||||
*/
|
||||
public $form;
|
||||
|
||||
/**
|
||||
* Model instance. Requires the setting of an model $attribute.
|
||||
*
|
||||
* @var \yii\db\ActiveRecord
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* Model attribute which holds the picker value. The referenced model attribute has to be an
|
||||
* array.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $attribute;
|
||||
|
||||
/**
|
||||
* Input form name.
|
||||
* This can be provided if no form and model is provided for custom input field setting.
|
||||
*
|
||||
* @var type
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* Can be used to overwrite the default placeholder.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $placeholder;
|
||||
|
||||
/**
|
||||
* The url used for the default @ metioning.
|
||||
* If there is no $searchUrl is given, the $searchRoute will be used instead.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $mentioningUrl;
|
||||
|
||||
/**
|
||||
* Route used for the default @ mentioning. This will only be used if
|
||||
* not $searchUrl is given.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $mentioningRoute = "/search/search/mentioning";
|
||||
|
||||
/**
|
||||
* Richtext features supported for within this feature.
|
||||
* By default all features will be included.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $includes = [];
|
||||
|
||||
/**
|
||||
* Richtext features not supported in this richtext feature.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $excludes = [];
|
||||
|
||||
/**
|
||||
* Can be used to set the value in case no $model and $attribute is provided.
|
||||
* $model and $attribute is provided.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* If set to true the picker will be focused automatically.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $focus = false;
|
||||
|
||||
/**
|
||||
* Disables the input field.
|
||||
* @var boolean
|
||||
*/
|
||||
public $disabled = false;
|
||||
|
||||
/**
|
||||
* Will be used as userfeedback, why this richtext is disabled.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $disabledText = false;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @var boolean
|
||||
*/
|
||||
public $init = true;
|
||||
|
||||
public $visible = true;
|
||||
|
||||
/**
|
||||
* @inhertidoc
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$inputOptions = $this->getAttributes();
|
||||
$inputOptions['id'] = $this->getId(true).'_input';
|
||||
$inputOptions['style'] = 'display:none;color';
|
||||
unset($inputOptions['contenteditable']);
|
||||
$modelAttribute = $this->attribute;
|
||||
|
||||
if ($this->form != null) {
|
||||
$input = $this->form->field($this->model, $this->attribute)->textarea($inputOptions);
|
||||
$richText = Html::tag('div', RichText::widget(['text' => $this->model->$modelAttribute, 'edit' => true]), $this->getOptions());
|
||||
} else if ($this->model != null) {
|
||||
$input = Html::activeTextarea($this->model, $this->attribute, $inputOptions);
|
||||
$richText = Html::tag('div', RichText::widget(['text' => $this->model->$modelAttribute, 'edit' => true]), $this->getOptions());
|
||||
} else {
|
||||
$input = Html::textarea(((!$this->name) ? 'richtext' : $this->name), $this->value, $inputOptions);
|
||||
$richText = Html::tag('div', RichText::widget(['text' => $this->value, 'edit' => true]),$this->getOptions());
|
||||
}
|
||||
|
||||
return $input.$richText;
|
||||
}
|
||||
|
||||
public function getData()
|
||||
{
|
||||
$result = [
|
||||
'excludes' => $this->excludes,
|
||||
'includes' => $this->includes,
|
||||
'mentioning-url' => $this->getMentioningUrl(),
|
||||
'placeholder' => $this->placeholder,
|
||||
];
|
||||
|
||||
if($this->disabled) {
|
||||
$result['disabled'] = true;
|
||||
$result['disabled-text'] = $this->disabledText;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getAttributes()
|
||||
{
|
||||
return [
|
||||
'class' => "atwho-input form-control humhub-ui-richtext",
|
||||
'contenteditable' => "true",
|
||||
];
|
||||
}
|
||||
|
||||
public function getMentioningUrl()
|
||||
{
|
||||
return ($this->mentioningUrl) ? $this->mentioningUrl : Url::to([$this->mentioningRoute]);
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
<div id="<?= $id ?>" <?= $modalData ?> class="modal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<?= yii\helpers\Html::beginTag('div', $options) ?>
|
||||
|
||||
<?= \humhub\widgets\ModalDialog::widget([
|
||||
'header' => $header,
|
||||
'animation' => $animation,
|
||||
@ -8,4 +9,5 @@
|
||||
'footer' => $footer,
|
||||
'initialLoader' => $initialLoader
|
||||
]); ?>
|
||||
</div>
|
||||
|
||||
<?= yii\helpers\Html::endTag('div') ?>
|
@ -39,11 +39,11 @@ use yii\helpers\Url;
|
||||
$editableContent.atwho({
|
||||
at: "@",
|
||||
data: [{image: '', 'cssClass': 'hint', name: "<?= Yii::t('base', 'Please type at least 3 characters') ?>"}],
|
||||
insert_tpl: "<a href='${link}' target='_blank' class='atwho-user' data-user-guid='${atwho-at}-${type}${guid}'>${atwho-data-value}​</a>",
|
||||
tpl: "<li class='hint' data-value=''>${name}</li>",
|
||||
insertTpl: "<a href='${link}' target='_blank' class='atwho-user' data-user-guid='${atwho-at}-${type}${guid}'>${name}​</a>",
|
||||
displayTpl: "<li class='hint' data-value=''>${name}</li>",
|
||||
search_key: "name",
|
||||
limit: 10,
|
||||
highlight_first: false,
|
||||
highlightFirst: false,
|
||||
callbacks: {
|
||||
matcher: function (flag, subtext, should_start_with_space) {
|
||||
var match, regexp;
|
||||
@ -58,18 +58,18 @@ use yii\helpers\Url;
|
||||
|
||||
return null;
|
||||
},
|
||||
remote_filter: function (query, callback) {
|
||||
remoteFilter: function (query, callback) {
|
||||
this.setting.highlight_first = false;
|
||||
|
||||
// Render loading user feedback.
|
||||
this.setting.tpl = "<li class='hint' data-value=''>${name}</li>";
|
||||
this.setting.displayTpl = "<li class='hint' data-value=''>${name}</li>";
|
||||
this.view.render([{"type":"test","cssClass": "hint", "name":"<?= Yii::t('base', 'Loading...') ?>","image":"","link":""}]);
|
||||
|
||||
// check the char length and data-query attribute for changing plugin settings for showing results
|
||||
if (query.length >= 3 && $('#<?= $id; ?>_contenteditable').attr('data-query') == '1') {
|
||||
// set plugin settings for showing results
|
||||
this.setting.highlight_first = true;
|
||||
this.setting.tpl = '<li class="${cssClass}" data-value="@${name}">${image} ${name}</li>';
|
||||
this.setting.highlightFirst = true;
|
||||
this.setting.displayTpl = '<li class="${cssClass}" data-value="@${name}">${image} ${name}</li>';
|
||||
$.getJSON("<?php echo Url::to([$userSearchUrl]); ?>", {keyword: query}, function (data) {
|
||||
callback(data);
|
||||
});
|
||||
@ -81,10 +81,10 @@ use yii\helpers\Url;
|
||||
}
|
||||
}).atwho({
|
||||
at: ":",
|
||||
insert_tpl: "<img data-emoji-name=';${name};' class='atwho-emoji' with='18' height='18' src='<?php echo Yii::getAlias('@web/img/emoji/${name}.svg'); ?>' />",
|
||||
tpl: "<li class='atwho-emoji-entry' data-value=';${name};'><img with='18' height='18' src='<?php echo Yii::getAlias('@web/img/emoji/${name}.svg'); ?>'/></li>",
|
||||
insertTpl: "<img data-emoji-name=';${name};' class='atwho-emoji' with='18' height='18' src='<?php echo Yii::getAlias('@web/img/emoji/${name}.svg'); ?>' />",
|
||||
displayTpl: "<li class='atwho-emoji-entry' data-value=';${name};'><img with='18' height='18' src='<?php echo Yii::getAlias('@web/img/emoji/${name}.svg'); ?>'/></li>",
|
||||
data: emojis_list,
|
||||
highlight_first: true,
|
||||
highlightFirst: true,
|
||||
limit: 100
|
||||
});
|
||||
|
||||
@ -217,13 +217,15 @@ use yii\helpers\Url;
|
||||
|
||||
// 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,'');
|
||||
|
||||
// replace all <br> with new line break
|
||||
element.html(html.replace(/\<br\s*\>/g, '\n'));
|
||||
|
||||
|
||||
// return plain text without html tags
|
||||
return element.text().trim();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user