diff --git a/.gitignore b/.gitignore
index 57f9e1cb88..378ffc49d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,8 +4,7 @@ assets/*
protected/runtime/*
!protected/runtime/.gitignore
-node_modules/*
-!node_modules/.gitignore
+node_modules
protected/config/local/*
!protected/config/local/.gitignore
diff --git a/Gruntfile.js b/Gruntfile.js
index b6bf03953f..596010fb9f 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -7,8 +7,8 @@ module.exports = function (grunt) {
},
dist: {
src: ['js/humhub.core.js', 'js/humhub.util.js' ,'js/humhub.additions.js',
- 'js/humhub.client.js', 'js/humhub.ui.js', 'js/humhub.actions.js', 'js/humhub.content.js',
- 'js/humhub.stream.js', 'js/humhub.ui.modal.js'],
+ 'js/humhub.client.js', 'js/humhub.ui.js', 'js/humhub.ui.modal.js', 'js/humhub.actions.js',
+ 'js/humhub.content.js', 'js/humhub.stream.js'],
dest: 'js/dist/humhub.all.js'
}
},
diff --git a/composer.json b/composer.json
index d5f542fb66..e9078d1870 100644
--- a/composer.json
+++ b/composer.json
@@ -34,6 +34,7 @@
"bower-asset/fontawesome": "^4.3.0",
"bower-asset/bootstrap-markdown": "2.10.*",
"bower-asset/select2" : "^4.0.2",
+ "bower-asset/bluebird" : "^3.3.5",
"bower-asset/select2-bootstrap-theme" : "0.1.0-beta.4"
},
"require-dev": {
diff --git a/js/dist/humhub.all.js b/js/dist/humhub.all.js
index 8ad2c03710..302f848fd9 100644
--- a/js/dist/humhub.all.js
+++ b/js/dist/humhub.all.js
@@ -149,6 +149,38 @@ var humhub = humhub || (function($) {
}
};
+ /**
+ * Config implementation
+ */
+
+ var config = {
+ get : function(module, key, defaultVal) {
+ if(_isDefined(key)) {
+ var result = this.getModuleConfig(module)[key];
+ return (_isDefined(result)) ? result : defaultVal;
+ }
+ },
+ getModuleConfig: function(module) {
+ if(!this.module) {
+ this.module = {};
+ }
+ return this.module;
+ },
+
+ is : function(module, key, defaultVal) {
+ return this.get(module, key,defaultVal) === true;
+ },
+
+ set : function(module, key, value) {
+ //Moduleid with multiple values
+ if(arguments.length === 2) {
+ $.extend(this.getModuleConfig(module), key);
+ } else if(arguments.length === 3) {
+ this.getModuleConfig(module)[key] = value;
+ }
+ }
+ };
+
/**
* Cuts the prefix humub.modules or modules. from the given value.
* @param {type} value
@@ -186,6 +218,10 @@ var humhub = humhub || (function($) {
return val.indexOf(prefix) === 0;
};
+ var _isDefined = function(obj) {
+ return typeof obj !== 'undefined';
+ };
+
//Initialize all initial modules
$(document).ready(function() {
$.each(initialModules, function(i, module) {
@@ -197,8 +233,12 @@ var humhub = humhub || (function($) {
});
});
+
+
return {
- initModule: initModule
+ initModule: initModule,
+ modules: modules,
+ config: config
};
})($);;/**
* Util module with sub module for object and string utility functions
@@ -217,6 +257,9 @@ humhub.initModule('util', function(module, require, $) {
isArray: function(obj) {
return $.isArray(obj);
},
+ isEmpty: function(obj) {
+ return $.isEmptyObject(obj);
+ },
isString: function (obj) {
return typeof obj === 'string';
},
@@ -248,7 +291,7 @@ humhub.initModule('util', function(module, require, $) {
var string = {
cutprefix : function(val, prefix) {
- if(!this.startsWith(prefix)) {
+ if(!this.startsWith(val, prefix)) {
return val;
}
return val.substring(prefix.length, val.length);
@@ -359,6 +402,7 @@ humhub.initModule('client', function (module, require, $) {
*/
var Response = function (data) {
this.data = data;
+ $.extend(this, data);
};
/**
@@ -378,14 +422,10 @@ humhub.initModule('client', function (module, require, $) {
Response.prototype.isError = function () {
return this.getStatus() > 0 || this.getErrors().length;
};
-
- Response.prototype.getStatus = function () {
- return (this.data && object.isDefined(this.data.status)) ? this.data.status : -1;
- };
- Response.prototype.getErrorTitle = function() {
- return (this.data) ? this.data.errorTitle : undefined;
- };
+ Response.prototype.getStatus = function () {
+ return (this.status) ? this.status : -1;
+ };
Response.prototype.getFirstError = function() {
var errors = this.getErrors();
@@ -397,9 +437,8 @@ humhub.initModule('client', function (module, require, $) {
Response.prototype.setAjaxError = function(xhr, errorThrown, textStatus,data , status) {
this.xhr = xhr;
this.textStatus = textStatus;
- this.data = data || {};
- this.data.status = status || xhr.status;
- this.data.errors = [errorThrown];
+ this.status = status || xhr.status;
+ this.errors = [errorThrown];
};
/**
@@ -408,40 +447,8 @@ humhub.initModule('client', function (module, require, $) {
* @returns {array} error array or empty array
*/
Response.prototype.getErrors = function () {
- if (this.data) {
- var errors = this.data.errors || [];
- return (object.isString(errors)) ? [errors] : errors;
- }
- return [];
- };
-
- /**
- * Returns the raw content object. The content object can either be an
- * object with multiple partials {partialId: content string} or a single content string.
- * @param {type} id
- * @returns {undefined|humhub.client_L5.Response.data.content}1
- */
- Response.prototype.getContent = function () {
- return this.data.content;
- };
-
- /**
- * Returns the response partial. If no id is given we return the first partial
- * we find.
- * @returns {humhub.client_L5.Response.data.content}
- */
- Response.prototype.getPartial = function (id) {
- if (!this.data) {
- return;
- }
- //TODO: handleResponse filter...
- if (object.isObject(this.data.content)) {
- return (id) ? this.data.content[id] : this.data.content;
- } else if (!id) {
- return this.data.content;
- }
-
- return;
+ var errors = this.errors || [];
+ return (object.isString(errors)) ? [errors] : errors;
};
Response.prototype.toString = function () {
@@ -452,53 +459,62 @@ humhub.initModule('client', function (module, require, $) {
var cfg = cfg || {};
$form = object.isString($form) ? $($form) : $form;
cfg.type = $form.attr('method') || 'post';
- cfg.data = $form.serialize()
+ cfg.data = $form.serialize();
ajax($form.attr('action'), cfg);
};
- var ajax = function (path, cfg) {
+ var post = function(path, cfg) {
var cfg = cfg || {};
- var async = cfg.async || true;
- var dataType = cfg.dataType || "json";
+ cfg.type = 'POST';
+ return ajax(path, cfg);
+ };
- var error = function (xhr, textStatus, errorThrown, data, status) {
- //Textstatus = "timeout", "error", "abort", "parsererror", "application"
- if (cfg.error && object.isFunction(cfg.error)) {
- var response = new Response();
- response.setAjaxError(xhr, errorThrown, textStatus, data, status);
- cfg.error(response);
- } else {
- console.warn('Unhandled ajax error: ' + path + " type" + type + " error: " + errorThrown);
- }
- };
+ var ajax = function (path, cfg) {
+ return new Promise(function(resolve, reject) {
+ var cfg = cfg || {};
+ var async = cfg.async || true;
+ var dataType = cfg.dataType || "json";
- var success = function (json, textStatus, xhr) {
- var response = new Response(json);
- if (response.isError()) { //Application errors
- return error(xhr, "application", response.getErrors(), json, response.getStatus() );
- } else if (cfg.success) {
- response.textStatus = textStatus;
- response.xhr = xhr;
- cfg.success(response);
- }
- };
+ var error = function (xhr, textStatus, errorThrown, data, status) {
+ //Textstatus = "timeout", "error", "abort", "parsererror", "application"
+ if (cfg.error && object.isFunction(cfg.error)) {
+ var response = new Response();
+ response.setAjaxError(xhr, errorThrown, textStatus, data, status);
+ cfg.error(response);
+ }
+ reject(xhr, textStatus, errorThrown, data, status);
+ };
- $.ajax({
- url: path,
- data: cfg.data,
- type: cfg.type,
- beforeSend: cfg.beforeSend,
- processData: cfg.processData,
- contentType: cfg.contentType,
- async: async,
- dataType: dataType,
- success: success,
- error: error
+ var success = function (json, textStatus, xhr) {
+ var response = new Response(json);
+ if (response.isError()) { //Application errors
+ return error(xhr, "application", response.getErrors(), json, response.getStatus() );
+ } else if (cfg.success) {
+ response.textStatus = textStatus;
+ response.xhr = xhr;
+ cfg.success(response);
+ }
+ resolve(response);
+ };
+
+ $.ajax({
+ url: path,
+ data: cfg.data,
+ type: cfg.type,
+ beforeSend: cfg.beforeSend,
+ processData: cfg.processData,
+ contentType: cfg.contentType,
+ async: async,
+ dataType: dataType,
+ success: success,
+ error: error
+ });
});
};
module.export({
ajax: ajax,
+ post: post,
submit: submit,
init: init
});
@@ -532,515 +548,7 @@ humhub.initModule('client', function (module, require, $) {
additions.registerAddition('.autosize', function($match) {
$match.autosize();
});
- }
-});;/**
- * Thid module can be used by humhub sub modules for registering handlers and serves as core module for executing actions triggered in the gui.
- * A module can either register global handler by using the registerHandler and registerAjaxHandler functions or use the content mechanism.
- */
-humhub.initModule('actions', function (module, require, $) {
- var _handler = {};
- var object = require('util').object;
- var string = require('util').string;
- var client = require('client');
-
- /**
- * Constructor for initializing the module.
- */
- module.init = function () {
- //Binding default action types
- this.bindAction(document, 'click', '[data-action-click]');
- this.bindAction(document, 'dblclick', '[data-action-dblclick]');
- this.bindAction(document, 'change', '[data-action-mouseout]');
-
- //Add addition for loader buttons
- require('additions').registerAddition('[data-action-load-button]', function () {
- var that = this;
- this.on('click.humhub-action-load-button', function (evt) {
- if (!that.find('.action-loader').length) {
- that.append('');
- }
- });
- });
};
-
- /**
- * Registers a given handler with the given id.
- *
- * This handler will be called e.g. after clicking a button with the handler id as
- * data-action-click attribute.
- *
- * The handler can access additional event information through the argument event.
- * The this object within the handler will be the trigger of the event.
- *
- * @param {string} id handler id should contain the module namespace
- * @param {function} handler function with one event argument
- * @returns {undefined}
- */
- module.registerHandler = function (id, handler) {
- if (!id) {
- return;
- }
-
- if (handler) {
- _handler[id] = handler;
- }
- };
-
- /**
- * Registers an ajax eventhandler.
- * The function can either be called with four arguments (id, successhandler, errorhandler, additional config)
- * or with two (id, cfg) where tha handlers are contained in the config object itself.
- *
- * The successhandler will be called only if the response does not contain any errors or errormessages.
- * So the errorhandler is called for application and http errors.
- *
- * The config can contain additional ajax settings.
- *
- * @param {type} id
- * @param {type} success
- * @param {type} error
- * @param {type} cfg
- * @returns {undefined}
- */
- module.registerAjaxHandler = function (id, success, error, cfg) {
- cfg = cfg || {};
- if (!id) {
- return;
- }
-
- if (object.isFunction(success)) {
- cfg.success = success;
- cfg.error = error;
- } else {
- cfg = success;
- }
-
- if (success) {
- _handler[id] = function (event) {
- var path = $(this).data('url-' + event.type) || $(this).data('url');
- client.ajax(path, cfg, event);
- };
- }
- };
-
- /**
- * Binds an delegate wrapper event handler to the parent node. This is used to detect action handlers like
- * data-action-click events and map the call to either a stand alone handler or a content
- * action handler. The trigger of a contentAction has to be contained in a data-content-base node.
- *
- * This function uses the jQuery event delegation:
- *
- * $(parent).on(type, selector, function(){...});
- *
- * This assures the event binding for dynamic content (ajax content etc..)
- *
- * @see {@link humhub.modules.content.handleAction}
- * @param {Node|jQuery} parent - the event target
- * @param {string} type - event type e.g. click, change,...
- * @param {string} selector - jQuery selector
- * @param {string} selector - jQuery selector
- */
- module.bindAction = function (parent, type, selector, directHandler) {
- parent = parent || document;
- var $parent = parent.jquery ? parent : $(parent);
- $parent.on(type+'.humhub-action', selector, function (evt) {
- evt.preventDefault();
- //The element which triggered the action e.g. a button or link
- $trigger = $(this);
-
- //Get the handler id, either a stand alone handler or a content handler function e.g: 'edit'
- var handlerId = $trigger.data('action' + '-' + type);
- var event = {type: type, $trigger: $trigger, handler: handlerId};
-
- event.finish = function() {
- _removeLoaderFromEventTarget(evt);
- };
-
- //TODO: handle with $.Event
- //var event = $.Event(type, {$trigger: $trigger});
- //event.originalEvent = evt;
-
- //Search and execute a stand alone handler or try to call the content action handler
- try {
- if(object.isFunction(directHandler)) {
- //Direct action handler
- directHandler.apply($trigger, [event]);
- } else if (_handler[handlerId]) {
- //Registered action handler
- var handler = _handler[handlerId];
- handler.apply($trigger, [event]);
- } else if (!_handler['humhub.modules.content.actiontHandler'](event)) { //Content action handler
- //If the content handler did not accept this event we try to find a handler by namespace
- var splittedNS = handlerId.split('.');
- var handler = splittedNS[splittedNS.length - 1];
- var target = require(string.cutsuffix(handlerId, '.' + handler));
- target[handler]({type: type, $trigger: $trigger});
- }
- } catch (e) {
- //TODO: handle error !
- console.error('Error while handling action event for handler "' + handlerId+'"', e);
- _removeLoaderFromEventTarget(evt);
- }
- });
- };
-
- var _removeLoaderFromEventTarget = function (evt) {
- if (evt.target) {
- $target = $(evt.target);
- $loader = $target.find('.action-loader');
-
- if ($loader.length) {
- $loader.remove();
- }
- }
- };
-});;/**
- * This module provides an api for handling content objects e.g. Posts, Polls...
- *
- * @type undefined|Function
- */
-
-humhub.initModule('content', function(module, require, $) {
- var client = require('client');
- var object = require('util').object;
- var actions = require('actions');
-
- module.init = function() {
- actions.registerHandler('humhub.modules.content.actiontHandler', function(event) {
- return module.handleAction(event);
- });
- };
-
- /**
- * Handles the given contentAction event. The event should provide the following properties:
- *
- * $trigger (required) : the trigger node of the event
- * handler (required) : the handler functionn name to be executed on the content
- * type (optoinal) : the event type 'click', 'change',...
- *
- * @param {object} event - event object
- * @returns {Boolean} true if the contentAction could be executed else false
- */
- module.handleAction = function(event) {
- var $contentBase = this.getContentBase(event.$trigger);
- if($contentBase.length) {
- //Initialize a content instance by means of the content-base type and execute the handler
- var ContentType = require($contentBase.data('content-base'));
- if(ContentType) {
- var content = new ContentType($contentBase);
- if(event.handler && content[event.handler]) {
- content[event.handler](event);
- return true;
- }
- } else {
- console.error('No ContentType found for '+$contentBase.data('content-base'));
- }
- }
- return false;
- };
-
- module.getContentBase = function($element) {
- return $element.closest('[data-content-base]');
- };
-
- var Content = function(id) {
- if(!id) { //Create content
- return;
- }
- if (typeof id === 'string') {
- this.id = id;
- this.$ = $('#' + id);
- } else if (id.jquery) {
- this.$ = id;
- this.id = this.$.attr('id');
- }
- };
-
- Content.prototype.getKey = function () {
- return this.$.data('content-key');
- };
-
- Content.prototype.getEditUrl = function () {
- var result = this.$.data('content-edit-url');
- if(!result) {
- var parentContent = this.getParentContentBase('[data-content-base]');
- if(parentContent) {
- return parentContent.getEditUrl();
- }
- }
- return result;
- };
-
- Content.prototype.getParentContentBase = function() {
- var $parent = this.$.parent().closest('[data-content-base]');
- if($parent.length) {
- try {
- var ParentType = require($parent.data('content-base'));
- return new ParentType($parent);
- } catch(err) {
- console.error('Could not instantiate parent content base: '+$parent.data('content-base'));
- }
- }
- };
-
- Content.prototype.create = function (addContentHandler) {
- //Note that this Content won't have an id, so the backend will create an instance
- this.edit(addContentHandler);
- };
-
- Content.prototype.edit = function (successHandler) {
- var editUrl = this.getEditUrl();
- var contentId = this.getKey();
- var modal = require('ui.modal').global;
-
- if(!editUrl) {
- //Todo: handle error
- console.error('No editUrl found for edit content action editUrl: '+editUrl+ ' contentId '+contentId);
- return;
- }
-
- var that = this;
-
- client.ajax(editUrl, {
- data: {
- 'id' : contentId
- },
- beforeSend: function() {
- modal.loader();
- },
- success: function(response) {
- //Successfully retrieved the edit form, now show it within a modal
- modal.content(response.getContent(), function() {
- //Bind direct action handler we could use a global registeredHandler but this is more efficient
- actions.bindAction(modal.getBody(), 'click', '[data-content-save]', function(event) {
- client.submit(modal.getForm(), {
- success : function(response) {
- if(object.isFunction(successHandler)) {
- if(successHandler(response, modal)) {modal.close();};
- } else {
- that.replaceContent(response.getContent());
- //TODO: check for content.highlight
- modal.close();
- }
- event.finish();
- },
- error : function(error) {
- //TODO: handle error
- modal.error(error);
- console.error('Error while submitting form :'+error);
- event.finish();
- }
- });
- });
- });
- },
- error: function(errResponse) {
- modal.error(errResponse);
- console.log('Error occured while editing content: '+errResponse.getFirstError());
- //Todo: handle error
- }
- });
- };
-
- Content.prototype.replaceContent = function(content) {
- try {
- this.$.html($(content).children());
- } catch(e) {
- console.error('Error occured while replacing content: '+this.id , e);
- }
- };
-
- Content.prototype.delete = function () {
- //Search for data-content-delte-url on root.
- //if(this.deleteModal) {open modal bla}
- //Call this url with data-content-pk
- //Trigger delete event
- };
-
- module.Content = Content;
-});;/**
- * Core module for managing Streams and StreamItems
- * @type Function
- */
-humhub.initModule('stream', function(module, require, $) {
-
- var ENTRY_ID_SELECTOR_PREFIX = '#wallEntry_';
- var WALLSTREAM_ID = 'wallStream';
-
- /**
- * Base class for all StreamItems
- * @param {type} id
- * @returns {undefined}
- */
- var StreamItem = function (id) {
- if (typeof id === 'string') {
- this.id = id;
- this.$ = $('#' + id);
- } else if (id.jquery) {
- this.$ = id;
- this.id = this.$.attr('id');
- }
- };
-
- /**
- * Removes the stream item from stream
- */
- StreamItem.prototype.remove = function () {
- this.$.remove();
- };
-
- StreamItem.prototype.getContentKey = function () {}
-
- StreamItem.prototype.edit = function () {
- //Search for data-content-edit-url on root.
- //Call this url with data-content-pk
- //Trigger delete event
- };
-
- StreamItem.prototype.delete = function () {
- //Search for data-content-delte-url on root.
- //Call this url with data-content-pk
- //Trigger delete event
- };
-
- StreamItem.prototype.getContent = function () {
- return this.$.find('.content');
- };
-
-/*
- module.StreamItem.prototype.highlightContent = function () {
- var $content = this.getContent();
- $content.addClass('highlight');
- $content.delay(200).animate({backgroundColor: 'transparent'}, 1000, function () {
- $content.removeClass('highlight');
- $content.css('backgroundColor', '');
- });
- };
-*/
- /**
- * Stream implementation
- * @param {type} id
- * @returns {undefined}
- */
- var Stream = function (id) {
- this.id = id;
- this.$ = $('#' + id);
- };
-
- Stream.prototype.getEntry = function (id) {
- //Search for data-content-base and try to initiate the Item class
-
- return new module.Entry(this.$.find(ENTRY_ID_SELECTOR_PREFIX + id));
- };
-
- Stream.prototype.wallStick = function (url) {
- $.ajax({
- dataType: "json",
- type: 'post',
- url: url
- }).done(function (data) {
- if (data.success) {
- if (currentStream) {
- $.each(data.wallEntryIds, function (k, wallEntryId) {
- currentStream.deleteEntry(wallEntryId);
- currentStream.prependEntry(wallEntryId);
- });
- $('html, body').animate({scrollTop: 0}, 'slow');
- }
- } else {
- alert(data.errorMessage);
- }
- });
- };
-
- Stream.prototype.wallUnstick = function (url) {
- $.ajax({
- dataType: "json",
- type: 'post',
- url: url
- }).done(function (data) {
- if (data.success) {
- //Reload the whole stream, since we have to reorder the entries
- currentStream.showStream();
- }
- });
- };
-
- /**
- * Click Handler for Archive Link of Wall Posts
- * (archiveLink.php)
- *
- * @param {type} className
- * @param {type} id
- */
- Stream.prototype.wallArchive = function (id) {
-
- url = wallArchiveLinkUrl.replace('-id-', id);
-
- $.ajax({
- dataType: "json",
- type: 'post',
- url: url
- }).done(function (data) {
- if (data.success) {
- if (currentStream) {
- $.each(data.wallEntryIds, function (k, wallEntryId) {
- //currentStream.reloadWallEntry(wallEntryId);
- // fade out post
- setInterval(fadeOut(), 1000);
-
- function fadeOut() {
- // fade out current archived post
- $('#wallEntry_' + wallEntryId).fadeOut('slow');
- }
- });
- }
- }
- });
- };
-
-
- /**
- * Click Handler for Un Archive Link of Wall Posts
- * (archiveLink.php)
- *
- * @param {type} className
- * @param {type} id
- */
- Stream.prototype.wallUnarchive = function (id) {
- url = wallUnarchiveLinkUrl.replace('-id-', id);
-
- $.ajax({
- dataType: "json",
- type: 'post',
- url: url
- }).done(function (data) {
- if (data.success) {
- if (currentStream) {
- $.each(data.wallEntryIds, function (k, wallEntryId) {
- currentStream.reloadWallEntry(wallEntryId);
- });
-
- }
- }
- });
- };
-
- var getStream = function () {
- if (!module.mainStream) {
- module.mainStream = new module.Stream(WALLSTREAM_ID);
- }
- return module.mainStream;
- };
-
- var getEntry = function (id) {
- return module.getStream().getEntry(id);
- };
-
- module.export({
- getStream : getStream,
- getEntry : getEntry
- });
});;/**
* Module for creating an manipulating modal dialoges.
* Normal layout of a dialog:
@@ -1059,8 +567,8 @@ humhub.initModule('stream', function(module, require, $) {
* @param {type} param2
*/
humhub.initModule('ui.modal', function (module, require, $) {
+ var object = require('util').object;
var additions = require('additions');
- var actions = require('actions');
//Keeps track of all initialized modals
var modals = [];
@@ -1328,10 +836,879 @@ humhub.initModule('ui.modal', function (module, require, $) {
return this.$modal.find('.modal-body');
};
+ var ConfirmModal = function(id, confirmHandler) {
+ Modal.call(this, id);
+ this.initButtons();
+ };
+
+ ConfirmModal.prototype.initButtons = function(confirmHandler) {
+ this.$confirm = this.$modal.find('[data-modal-submit]');
+ this.$confirm.on('click', confirmHandler);
+ };
+
+ object.inherits(ConfirmModal, Modal);
+
module.export({
init: function () {
module.global = new Modal('global-modal');
},
Modal: Modal
});
-});
\ No newline at end of file
+});;/**
+ * Thid module can be used by humhub sub modules for registering handlers and serves as core module for executing actions triggered in the gui.
+ * A module can either register global handler by using the registerHandler and registerAjaxHandler functions or use the content mechanism.
+ */
+humhub.initModule('actions', function (module, require, $) {
+ var _handler = {};
+ var object = require('util').object;
+ var string = require('util').string;
+ var client = require('client');
+
+ /**
+ * Constructor for initializing the module.
+ */
+ module.init = function () {
+ //Binding default action types
+ this.bindAction(document, 'click', '[data-action-click]');
+ this.bindAction(document, 'dblclick', '[data-action-dblclick]');
+ this.bindAction(document, 'change', '[data-action-mouseout]');
+
+ //Add addition for loader buttons
+ require('additions').registerAddition('[data-action-load-button]', function () {
+ var that = this;
+ this.on('click.humhub-action-load-button', function (evt) {
+ if (!that.find('.action-loader').length) {
+ that.append('');
+ }
+ });
+ });
+ };
+
+ /**
+ * Registers a given handler with the given id.
+ *
+ * This handler will be called e.g. after clicking a button with the handler id as
+ * data-action-click attribute.
+ *
+ * The handler can access additional event information through the argument event.
+ * The this object within the handler will be the trigger of the event.
+ *
+ * @param {string} id handler id should contain the module namespace
+ * @param {function} handler function with one event argument
+ * @returns {undefined}
+ */
+ module.registerHandler = function (id, handler) {
+ if (!id) {
+ return;
+ }
+
+ if (handler) {
+ _handler[id] = handler;
+ }
+ };
+
+ /**
+ * Registers an ajax eventhandler.
+ * The function can either be called with four arguments (id, successhandler, errorhandler, additional config)
+ * or with two (id, cfg) where tha handlers are contained in the config object itself.
+ *
+ * The successhandler will be called only if the response does not contain any errors or errormessages.
+ * So the errorhandler is called for application and http errors.
+ *
+ * The config can contain additional ajax settings.
+ *
+ * @param {type} id
+ * @param {type} success
+ * @param {type} error
+ * @param {type} cfg
+ * @returns {undefined}
+ */
+ module.registerAjaxHandler = function (id, success, error, cfg) {
+ cfg = cfg || {};
+ if (!id) {
+ return;
+ }
+
+ if (object.isFunction(success)) {
+ cfg.success = success;
+ cfg.error = error;
+ } else {
+ cfg = success;
+ }
+
+ if (success) {
+ _handler[id] = function (event) {
+ var path = $(this).data('url-' + event.type) || $(this).data('url');
+ client.ajax(path, cfg, event);
+ };
+ }
+ };
+
+ /**
+ * Binds an delegate wrapper event handler to the parent node. This is used to detect action handlers like
+ * data-action-click events and map the call to either a stand alone handler or a content
+ * action handler. The trigger of a contentAction has to be contained in a data-content-base node.
+ *
+ * This function uses the jQuery event delegation:
+ *
+ * $(parent).on(type, selector, function(){...});
+ *
+ * This assures the event binding for dynamic content (ajax content etc..)
+ *
+ * @see {@link humhub.modules.content.handleAction}
+ * @param {Node|jQuery} parent - the event target
+ * @param {string} type - event type e.g. click, change,...
+ * @param {string} selector - jQuery selector
+ * @param {string} selector - jQuery selector
+ */
+ module.bindAction = function (parent, type, selector, directHandler) {
+ parent = parent || document;
+ var $parent = parent.jquery ? parent : $(parent);
+ $parent.on(type+'.humhub-action', selector, function (evt) {
+ evt.preventDefault();
+ //The element which triggered the action e.g. a button or link
+ $trigger = $(this);
+
+ //Get the handler id, either a stand alone handler or a content handler function e.g: 'edit'
+ var handlerId = $trigger.data('action' + '-' + type);
+ var event = {type: type, $trigger: $trigger, handler: handlerId};
+
+ event.finish = function() {
+ _removeLoaderFromEventTarget(evt);
+ };
+
+ //TODO: handle with $.Event
+ //var event = $.Event(type, {$trigger: $trigger});
+ //event.originalEvent = evt;
+
+ //Search and execute a stand alone handler or try to call the content action handler
+ try {
+ if(object.isFunction(directHandler)) {
+ //Direct action handler
+ directHandler.apply($trigger, [event]);
+ } else if (_handler[handlerId]) {
+ //Registered action handler
+ var handler = _handler[handlerId];
+ handler.apply($trigger, [event]);
+ } else if (!_handler['humhub.modules.content.actiontHandler'](event)) { //Content action handler
+ //If the content handler did not accept this event we try to find a handler by namespace
+ var splittedNS = handlerId.split('.');
+ var handler = splittedNS[splittedNS.length - 1];
+ var target = require(string.cutsuffix(handlerId, '.' + handler));
+ target[handler]({type: type, $trigger: $trigger});
+ }
+ } catch (e) {
+ //TODO: handle error !
+ console.error('Error while handling action event for handler "' + handlerId+'"', e);
+ _removeLoaderFromEventTarget(evt);
+ }
+ });
+ };
+
+ var _removeLoaderFromEventTarget = function (evt) {
+ if (evt.target) {
+ $target = $(evt.target);
+ $loader = $target.find('.action-loader');
+
+ if ($loader.length) {
+ $loader.remove();
+ }
+ }
+ };
+});;/**
+ * This module provides an api for handling content objects e.g. Posts, Polls...
+ *
+ * @type undefined|Function
+ */
+
+humhub.initModule('content', function(module, require, $) {
+ var client = require('client');
+ var object = require('util').object;
+ var actions = require('actions');
+
+ var Content = function(container) {
+ if(!container) { //Create content
+ return;
+ }
+ this.$ = (object.isString(container)) ? $('#' + container) : container;
+ this.contentBase = this.$.data('content-base');
+ };
+
+ Content.prototype.getContentActions = function() {
+ return ['create','edit','delete'];
+ };
+
+ Content.prototype.getKey = function () {
+ return this.$.data('content-pk');
+ };
+
+ Content.prototype.data = function(dataSuffix) {
+ var result = this.$.data(dataSuffix);
+ if(!result) {
+ var parentContent = this.getParentContentBase();
+ if(parentContent) {
+ return parentContent.data(dataSuffix);
+ }
+ }
+ return result;
+ };
+
+ Content.prototype.getParentContentBase = function() {
+ var $parent = this.$.parent().closest('[data-content-base]');
+ if($parent.length) {
+ try {
+ var ParentType = require($parent.data('content-base'));
+ return new ParentType($parent);
+ } catch(err) {
+ console.error('Could not instantiate parent content base: '+$parent.data('content-base'));
+ }
+ }
+ };
+
+ Content.prototype.create = function (addContentHandler) {
+ //Note that this Content won't have an id, so the backend will create an instance
+ if(indexOf(this.getContentActions(), 'create') < 0) {
+ return;
+ }
+
+ this.edit(addContentHandler);
+ };
+
+ Content.prototype.edit = function (successHandler) {
+ if(indexOf(this.getContentActions(), 'edit') < 0) {
+ return;
+ }
+
+ var editUrl = this.data('content-edit-url');
+ var contentId = this.getKey();
+ var modal = require('ui.modal').global;
+
+ if(!editUrl) {
+ //Todo: handle error
+ console.error('No editUrl found for edit content action editUrl: '+editUrl+ ' contentId '+contentId);
+ return;
+ }
+
+ var that = this;
+
+ client.ajax(editUrl, {
+ data: {
+ 'id' : contentId
+ },
+ beforeSend: function() {
+ modal.loader();
+ },
+ success: function(response) {
+ //Successfully retrieved the edit form, now show it within a modal
+ modal.content(response.getContent(), function() {
+ //Bind direct action handler we could use a global registeredHandler but this is more efficient
+ actions.bindAction(modal.getBody(), 'click', '[data-content-save]', function(event) {
+ client.submit(modal.getForm(), {
+ success : function(response) {
+ if(object.isFunction(successHandler)) {
+ if(successHandler(response, modal)) {modal.close();};
+ } else {
+ that.replaceContent(response.getContent());
+ //TODO: check for content.highlight
+ modal.close();
+ }
+ event.finish();
+ },
+ error : function(error) {
+ //TODO: handle error
+ modal.error(error);
+ console.error('Error while submitting form :'+error);
+ event.finish();
+ }
+ });
+ });
+ });
+ },
+ error: function(errResponse) {
+ modal.error(errResponse);
+ console.log('Error occured while editing content: '+errResponse.getFirstError());
+ //Todo: handle error
+ }
+ });
+ };
+
+ Content.prototype.delete = function () {
+ if(this.getContentActions().indexOf('delete') < 0) {
+ return;
+ }
+
+ var that = this;
+ var url = this.data('content-delete-url');
+ if(url) {
+ client.post(url, {
+ data: {
+ id: that.getKey()
+ },
+ success: function(json) {
+ json.success;
+ that.remove();
+ },
+ error: function(json) {
+ console.error(json);
+ }
+ })
+ } else {
+ console.error('Content delete was called, but no url could be determined for '+this.contentBase);
+ }
+ };
+
+ Content.prototype.replaceContent = function(content) {
+ try {
+ var that = this;
+ this.$.animate({ opacity: 0 }, 'fast', function() {
+ that.$.html($(content).children());
+ that.$.stop().animate({ opacity: 1 }, 'fast');
+ if(that.highlight) {
+ that.highlight();
+ }
+ });
+ } catch(e) {
+ console.error('Error occured while replacing content: '+this.$.attr('id') , e);
+ }
+ };
+
+ Content.prototype.remove = function() {
+ var that = this;
+ this.$.animate({ height: 'toggle', opacity: 'toggle' }, 'fast', function() {
+ that.$.remove();
+ //TODO: fire global event
+ });
+ };
+
+ Content.getContentBase = function($element) {
+ return $element.closest('[data-content-base]');
+ };
+
+ Content.getInstance = function($contentBase) {
+ $contentBase = (object.isString($contentBase)) ? $('#'+$contentBase) : $contentBase;
+ var ContentType = require($contentBase.data('content-base'));
+ if(ContentType) {
+ return new ContentType($contentBase);
+ }
+ };
+
+ var init = function() {
+ actions.registerHandler('humhub.modules.content.actiontHandler', function(event) {
+ return module.handleAction(event);
+ });
+ };
+
+ /**
+ * Handles the given contentAction event. The event should provide the following properties:
+ *
+ * $trigger (required) : the trigger node of the event
+ * handler (required) : the handler functionn name to be executed on the content
+ * type (optoinal) : the event type 'click', 'change',...
+ *
+ * @param {object} event - event object
+ * @returns {Boolean} true if the contentAction could be executed else false
+ */
+ handleAction = function(event) {
+ var $contentBase = Content.getContentBase(event.$trigger);
+ if($contentBase.length) {
+ //Initialize a content instance by means of the content-base type and execute the handler
+ var content = Content.getInstance($contentBase);
+ if(content) {
+ //Check if the content instance provides this actionhandler
+ if(event.handler && content[event.handler]) {
+ content[event.handler](event);
+ return true;
+ }
+ } else {
+ console.error('No ContentType found for '+$contentBase.data('content-base'));
+ }
+ }
+ return false;
+ };
+
+ module.export({
+ Content : Content,
+ init : init,
+ handleAction: handleAction
+ });
+});;/**
+ * Core module for managing Streams and StreamItems
+ * @type Function
+ */
+humhub.initModule('stream', function (module, require, $) {
+
+ var util = require('util');
+ var object = util.object;
+ var string = util.string;
+ var client = require('client');
+ var modal = require('modal');
+ var Content = require('content').Content;
+
+ var STREAM_INIT_COUNT = 8;
+ var STREAM_LOAD_COUNT = 4;
+
+ //TODO: load streamUrl from config
+ //TODO: readonly
+
+ /**
+ * Base class for all StreamContent
+ * @param {type} id
+ * @returns {undefined}
+ */
+ var StreamEntry = function (id) {
+ this.$ = object.isString(id) ? this.$ = $('#' + id) : id;
+ Content.call(this);
+ };
+
+ object.inherits(StreamEntry, Content);
+
+ StreamEntry.prototype.getContentActions = function() {
+ return ['delete', 'edit'];
+ };
+
+ StreamEntry.prototype.delete = function () {
+ var content = this.getContentInstance();
+ if(content && content.delete) {
+ //TODO: modalconfirm
+ content.delete();
+ } else {
+ StreamEntry._super.delete.call(this);
+ }
+ };
+
+ StreamEntry.prototype.reload = function () {
+ getStream().reload(this);
+ };
+
+ StreamEntry.prototype.edit = function () {
+ //Search for data-content-edit-url on root.
+ //Call this url with data-content-pk
+ //Trigger delete event
+ };
+
+ StreamEntry.prototype.getContentInstance = function () {
+ return Content.getInstance(this.$.find('[data-content-base]'));
+ };
+
+ /**
+ * Stream implementation.
+ *
+ * @param {type} container id or jQuery object of the stream container
+ * @returns {undefined}
+ */
+ var Stream = function (container) {
+ this.$ = (object.isString(container)) ? $('#' + container) : container;
+
+ if (!this.$.length) {
+ console.error('Could not initialize stream, invalid container given'+ container);
+ return;
+ }
+
+ //If a contentId is set on the stream root we will only show the single content
+ if(this.$.data('stream-contentid')) {
+ this.contentId = parseInt(this.$.data('stream-contentid'));
+ }
+
+ this.$stream = this.$.find(".s2_stream");
+
+ //Cache some stream relevant data/nodes
+ this.url = this.$.data('stream'); //TODO: set this in config instead of data field
+ this.$loader = this.$stream.find(".streamLoader");
+ this.$content = this.$stream.find('.s2_streamContent');
+ this.$filter = $('.wallFilterPanel');
+
+ //TODO: make this configurable
+ this.filters = [];
+ this.sort = "c";
+
+ Content.call(this);
+ };
+
+ object.inherits(Stream, Content);
+
+ Stream.prototype.getContentActions = function() {
+ return [];
+ };
+
+ /**
+ * Initializes the stream, by clearing the stream and reloading initial stream entries,
+ * this should be called if any filter/sort settings are changed or the stream
+ * needs an reload.
+ *
+ * @returns {humhub.stream_L5.Stream.prototype}
+ */
+ Stream.prototype.init = function () {
+ this.clear();
+ this.$stream.show();
+ if (this.isShowSingleEntry()) {
+ this.loadSingleEntry(this.contentId);
+ } else {
+ this.loadEntries(STREAM_INIT_COUNT);
+ }
+ return this;
+ };
+
+ Stream.prototype.clear = function() {
+ this.lastEntryLoaded = false;
+ this.readOnly = false;
+ this.loading = false;
+ this.$.find(".s2_streamContent").empty();
+ this.$.find(".s2_stream").hide();
+ this.$.find(".s2_single").hide();
+ this.$.find(".streamLoader").hide();
+ this.$.find(".emptyStreamMessage").hide();
+ this.$.find(".emptyFilterStreamMessage").hide();
+ this.$.find('.back_button_holder').hide();
+ this.$filter.hide();
+ };
+
+ Stream.prototype.loadSingleEntry = function(contentId) {
+ this.$.find('.back_button_holder').show();
+ this.loadEntries(1, (contentId + 1), '');
+ };
+
+ Stream.prototype.reloadEntry = function(entry) {
+ var that = this;
+ return new Promise(function(resolve, reject) {
+ entry = (entry instanceof StreamEntry) ? entry : that.getEntry(entry);
+
+ if(!entry) {
+ console.warn('Attempt to reload of non existent entry: '+entry);
+ reject();
+ return;
+ }
+
+ var contentId = entry.getKey();
+ return that._load(1, (contentId + 1), '').then(function(response) {
+ if(response.content[contentId]) {
+ entry.replaceContent(response.content[contentId].output);
+ resolve(entry);
+ } else {
+ console.warn('Reload failed: ContentId not found in response: '+contentId);
+ reject();
+ }
+ }, reject);
+ });
+ };
+
+ Stream.prototype.loadEntries = function (limit, from, filter, sort) {
+ if (this.loading || this.lastEntryLoaded) {
+ return;
+ }
+
+ //Initialize loading process
+ this.$loader.show();
+ this.loading = true;
+
+ //Overwrite the stream settings if provided
+ limit = limit || STREAM_LOAD_COUNT;
+ from = from || this.getLastContentId();
+ filter = filter || this.getFilterString();
+ sort = sort || this.sort;
+
+ var that = this;
+ return new Promise(function(resolve, reject) {
+ that._load(limit, from, filter,sort).then(function(response) {
+ that.$loader.hide();
+ if (object.isEmpty(response.content)) {
+ that.lastEntryLoaded = true;
+ $('#btn-load-more').hide();
+ } else {
+ that.lastEntryLoaded = response.is_last;
+ that.appendEntries(response);
+ }
+
+ that.loading = false;
+ that.onChange();
+ resolve();
+ }).catch(function(err) {
+ //TODO: handle error
+ that.loading = false;
+ that.$loader.hide();
+ reject();
+ });
+ });
+ };
+
+ Stream.prototype._load = function (limit, from, filter, sort) {
+ return client.ajax(this.url, {
+ data: {
+ filters: filter,
+ sort: sort,
+ from: from,
+ limit: limit
+ }
+ });
+ };
+
+ Stream.prototype.getLastContentId = function () {
+ var $lastEntry = this.$stream.find('[data-content-pk]').last();
+ if ($lastEntry.length) {
+ return $lastEntry.data('stream-contentid');
+ }
+ };
+
+ Stream.prototype.appendEntries = function (response) {
+ var that = this;
+ var result = '';
+ $.each(response.contentIds, function (i, key) {
+ var $entry = that.$.find('[data-content-pk="' + key + '"]');
+ if ($entry.length) {
+ $entry.remove();
+ }
+ result += response.content[key].output;
+ });
+ return this.$content.append(result);
+ };
+
+ /**
+ * Fired when new entries are shown
+ */
+ Stream.prototype.onChange = function () {
+ if (this.readOnly) {
+ $('.wallReadOnlyHide').hide();
+ $('.wallReadOnlyShow').show();
+ } else {
+ $('.wallReadOnlyShow').hide();
+ }
+
+ var hasEntries = this.hasEntries();
+ if (!hasEntries && !this.hasFilter()) {
+ this.$.find('.emptyStreamMessage').show();
+ this.$filter.hide();
+ } else if (!hasEntries) {
+ this.$.find('.emptyFilterStreamMessage').hide();
+ } else if(!this.isShowSingleEntry()) {
+ this.$filter.show();
+ this.$.find('.emptyStreamMessage').hide();
+ this.$.find('.emptyFilterStreamMessage').hide();
+ }
+
+ //TODO: fire global event
+ };
+
+ Stream.prototype.isShowSingleEntry = function () {
+ return object.isDefined(this.contentId);
+ };
+
+ Stream.prototype.hasEntries = function () {
+ return this.getEntryCount() > 0;
+ };
+
+ Stream.prototype.getEntryCount = function () {
+ return this.$.find('[data-content-pk]').length;
+ };
+
+ Stream.prototype.hasFilter = function () {
+ return this.filters.length > 0;
+ };
+
+ Stream.prototype.getFilterString = function () {
+ var result = '';
+ $.each(this.filters, function(i, filter) {
+ result += filter+',';
+ });
+
+ return string.cutsuffix(result, ',');
+ };
+
+ Stream.prototype.setFilter = function (filterId) {
+ if(this.filters.indexOf(filterId) < 0) {
+ this.filters.push(filterId);
+ }
+ };
+
+ Stream.prototype.unsetFilter = function (filterId) {
+ var index = this.filters.indexOf(filterId);
+ if(index > -1) {
+ this.filters.splice(index, 1);
+ }
+ };
+
+ Stream.prototype.getEntry = function(key) {
+ return new StreamEntry(this.$.find('[data-content-pk="' + key + '"]'));
+ };
+
+ Stream.prototype.getEntryByNode = function($childNode) {
+ return new StreamEntry($childNode.closest('[data-content-pk]'));
+ };
+
+ var getStream = function () {
+ if (!module.instance) {
+ module.instance = new Stream($('[data-stream]'));
+ }
+ return module.instance;
+ };
+
+ var getEntry = function (id) {
+ return module.getStream().getEntry(id);
+ };
+
+ var init = function () {
+ var stream = getStream().init();
+ $(window).scroll(function () {
+ if ($(window).scrollTop() == $(document).height() - $(window).height()) {
+ if (stream && !stream.loading && !stream.isShowSingleEntry() && !stream.lastEntryLoaded) {
+ stream.loadEntries();
+ }
+ }
+ });
+
+ stream.$.on('click', '.singleBackLink', function() {
+ stream.contentId = undefined;
+ stream.init();
+ $(this).hide();
+ });
+
+ initFilterNav();
+ };
+
+ var initFilterNav = function() {
+ $(".wallFilter").click(function () {
+ var $filter = $(this);
+ var checkboxi = $filter.children("i");
+ checkboxi.toggleClass('fa-square-o').toggleClass('fa-check-square-o');
+ if(checkboxi.hasClass('fa-check-square-o')) {
+ getStream().setFilter($filter.attr('id').replace('filter_', ''));
+ } else {
+ getStream().unsetFilter($filter.attr('id').replace('filter_', ''));
+ }
+ getStream().init();
+ });
+
+ $(".wallSorting").click(function () {
+ var newSortingMode = $(this).attr('id');
+
+ // uncheck all sorting
+ $(".wallSorting").find('i')
+ .removeClass('fa-check-square-o')
+ .addClass('fa-square-o');
+
+ // check current sorting mode
+ $("#" + newSortingMode).children("i")
+ .removeClass('fa-square-o')
+ .addClass('fa-check-square-o');
+
+ // remove sorting id append
+ newSortingMode = newSortingMode.replace('sorting_', '');
+
+ // Switch sorting mode and reload stream
+ getStream().sort = newSortingMode;
+ getStream().init();
+ });
+ };
+
+ module.export({
+ StreamEntry: StreamEntry,
+ Stream: Stream,
+ getStream: getStream,
+ getEntry: getEntry,
+ init: init
+ });
+});
+
+/* TODO:
+ Stream.prototype.wallStick = function (url) {
+ $.ajax({
+ dataType: "json",
+ type: 'post',
+ url: url
+ }).done(function (data) {
+ if (data.success) {
+ if (currentStream) {
+ $.each(data.wallEntryIds, function (k, wallEntryId) {
+ currentStream.deleteEntry(wallEntryId);
+ currentStream.prependEntry(wallEntryId);
+ });
+ $('html, body').animate({scrollTop: 0}, 'slow');
+ }
+ } else {
+ alert(data.errorMessage);
+ }
+ });
+ };
+
+ Stream.prototype.wallUnstick = function (url) {
+ $.ajax({
+ dataType: "json",
+ type: 'post',
+ url: url
+ }).done(function (data) {
+ if (data.success) {
+ //Reload the whole stream, since we have to reorder the entries
+ currentStream.showStream();
+ }
+ });
+ };
+
+ /**
+ * Click Handler for Archive Link of Wall Posts
+ * (archiveLink.php)
+ *
+ * @param {type} className
+ * @param {type} id
+
+ Stream.prototype.wallArchive = function (id) {
+
+ url = wallArchiveLinkUrl.replace('-id-', id);
+
+ $.ajax({
+ dataType: "json",
+ type: 'post',
+ url: url
+ }).done(function (data) {
+ if (data.success) {
+ if (currentStream) {
+ $.each(data.wallEntryIds, function (k, wallEntryId) {
+ //currentStream.reloadWallEntry(wallEntryId);
+ // fade out post
+ setInterval(fadeOut(), 1000);
+
+ function fadeOut() {
+ // fade out current archived post
+ $('#wallEntry_' + wallEntryId).fadeOut('slow');
+ }
+ });
+ }
+ }
+ });
+ };
+
+
+ /**
+ * Click Handler for Un Archive Link of Wall Posts
+ * (archiveLink.php)
+ *
+ * @param {type} className
+ * @param {type} id
+
+ Stream.prototype.wallUnarchive = function (id) {
+ url = wallUnarchiveLinkUrl.replace('-id-', id);
+
+ $.ajax({
+ dataType: "json",
+ type: 'post',
+ url: url
+ }).done(function (data) {
+ if (data.success) {
+ if (currentStream) {
+ $.each(data.wallEntryIds, function (k, wallEntryId) {
+ currentStream.reloadWallEntry(wallEntryId);
+ });
+
+ }
+ }
+ });
+ };
+
+
+ /*
+ module.StreamItem.prototype.highlightContent = function () {
+ var $content = this.getContent();
+ $content.addClass('highlight');
+ $content.delay(200).animate({backgroundColor: 'transparent'}, 1000, function () {
+ $content.removeClass('highlight');
+ $content.css('backgroundColor', '');
+ });
+ };
+ */
\ No newline at end of file
diff --git a/js/dist/humhub.all.min.js b/js/dist/humhub.all.min.js
index f5b7b64553..9df265fbbf 100644
--- a/js/dist/humhub.all.min.js
+++ b/js/dist/humhub.all.min.js
@@ -1 +1 @@
-var humhub=humhub||function(a){var b={},c=[],d=!1,e=function(b,e){var i=g(b,!0);i.id="humhub.modules."+h(b),i.require=f,i["export"]=function(b){a.extend(i,b)},e(i,f,a),d?i.init():c.push(i)},f=function(a){var b=g(a);return b||console.warn("No module found for id: "+a),b},g=function(c,d){try{var e=h(c),f=b;return a.each(e.split("."),function(a,b){if(b in f)f=f[b];else{if(!d)return f=void 0,!1;f=f[b]={}}}),f}catch(g){return}},h=function(a){return i(i(a,"humhub."),"modules.")},i=function(a,b){return j(a,b)?a.substring(b.length,a.length):a},j=function(a,b){return a&&b?0===a.indexOf(b):!1};return a(document).ready(function(){a.each(c,function(a,b){b.init&&b.init(),d=!0,console.log("Module initialized: "+b.id)})}),{initModule:e}}($);humhub.initModule("util",function(a,b,c){var d={isFunction:function(a){return c.isFunction(a)},isObject:function(a){return c.isPlainObject(a)},isJQuery:function(a){return this.isDefined(a)&&a.jquery},isArray:function(a){return c.isArray(a)},isString:function(a){return"string"==typeof a},isNumber:function(a){return this.isDefined(a)&&!isNaN(parseFloat(a))&&isFinite(a)},isBoolean:function(a){return"boolean"==typeof a},isDefined:function(a){if(arguments.length>1){var b=!0,c=this;return this.each(arguments,function(a,b){return c.isDefined(b)?void 0:!1}),b}return"undefined"!=typeof a},inherits:function(a,b){a.prototype=Object.create(b.prototype),a._super=b.prototype}},e={cutprefix:function(a,b){return this.startsWith(b)?a.substring(b.length,a.length):a},cutsuffix:function(a,b){return a.slice(0,-1*b.length)},startsWith:function(a,b){return d.isDefined(a)&&d.isDefined(b)?0===a.indexOf(b):!1},endsWith:function(a,b){return d.isDefined(a)&&d.isDefined(b)?-1!==a.indexOf(b,a.length-b.length):!1}};a["export"]({object:d,string:e})}),humhub.initModule("additions",function(a,b,c){var d={};a.registerAddition=function(a,b){d[a]||(d[a]=[]),d[a].push(b)},a.applyTo=function(a){var b=c(a);c.each(d,function(a,d){c.each(d,function(d,e){c.each(b.find(a).addBack(a),function(){try{var d=c(this);e.apply(d,[d,b])}catch(f){console.error("Error while applying addition "+e+" on selector "+a)}})})})},a.init=function(){}}),humhub.initModule("client",function(a,b,c){var d=b("util").object,e=(b("scripts"),function(){}),f=function(a){this.data=a};f.prototype.isConfirmation=function(){return 0===this.getStatus()},f.prototype.isError=function(){return this.getStatus()>0||this.getErrors().length},f.prototype.getStatus=function(){return this.data&&d.isDefined(this.data.status)?this.data.status:-1},f.prototype.getErrorTitle=function(){return this.data?this.data.errorTitle:void 0},f.prototype.getFirstError=function(){var a=this.getErrors();return a.length?a[0]:void 0},f.prototype.setAjaxError=function(a,b,c,d,e){this.xhr=a,this.textStatus=c,this.data=d||{},this.data.status=e||a.status,this.data.errors=[b]},f.prototype.getErrors=function(){if(this.data){var a=this.data.errors||[];return d.isString(a)?[a]:a}return[]},f.prototype.getContent=function(){return this.data.content},f.prototype.getPartial=function(a){return this.data?d.isObject(this.data.content)?a?this.data.content[a]:this.data.content:a?void 0:this.data.content:void 0},f.prototype.toString=function(){return"{ status: "+this.getStatus()+" error: "+this.getErrors()+" data: "+this.getContent()+" }"};var g=function(a,b){var b=b||{};a=d.isString(a)?c(a):a,b.type=a.attr("method")||"post",b.data=a.serialize(),h(a.attr("action"),b)},h=function(a,b){var b=b||{},e=b.async||!0,g=b.dataType||"json",h=function(c,e,g,h,i){if(b.error&&d.isFunction(b.error)){var j=new f;j.setAjaxError(c,g,e,h,i),b.error(j)}else console.warn("Unhandled ajax error: "+a+" type"+type+" error: "+g)},i=function(a,c,d){var e=new f(a);return e.isError()?h(d,"application",e.getErrors(),a,e.getStatus()):void(b.success&&(e.textStatus=c,e.xhr=d,b.success(e)))};c.ajax({url:a,data:b.data,type:b.type,beforeSend:b.beforeSend,processData:b.processData,contentType:b.contentType,async:e,dataType:g,success:i,error:h})};a["export"]({ajax:h,submit:g,init:e})}),humhub.initModule("ui",function(a,b,c){var d=b("additions");a.init=function(){d.registerAddition(".autosize",function(a){a.autosize()})}}),humhub.initModule("actions",function(a,b,c){var d={},e=b("util").object,f=b("util").string,g=b("client");a.init=function(){this.bindAction(document,"click","[data-action-click]"),this.bindAction(document,"dblclick","[data-action-dblclick]"),this.bindAction(document,"change","[data-action-mouseout]"),b("additions").registerAddition("[data-action-load-button]",function(){var a=this;this.on("click.humhub-action-load-button",function(b){a.find(".action-loader").length||a.append('')})})},a.registerHandler=function(a,b){a&&b&&(d[a]=b)},a.registerAjaxHandler=function(a,b,f,h){h=h||{},a&&(e.isFunction(b)?(h.success=b,h.error=f):h=b,b&&(d[a]=function(a){var b=c(this).data("url-"+a.type)||c(this).data("url");g.ajax(b,h,a)}))},a.bindAction=function(a,g,i,j){a=a||document;var k=a.jquery?a:c(a);k.on(g+".humhub-action",i,function(a){a.preventDefault(),$trigger=c(this);var i=$trigger.data("action-"+g),k={type:g,$trigger:$trigger,handler:i};k.finish=function(){h(a)};try{if(e.isFunction(j))j.apply($trigger,[k]);else if(d[i]){var l=d[i];l.apply($trigger,[k])}else if(!d["humhub.modules.content.actiontHandler"](k)){var m=i.split("."),l=m[m.length-1],n=b(f.cutsuffix(i,"."+l));n[l]({type:g,$trigger:$trigger})}}catch(o){console.error('Error while handling action event for handler "'+i+'"',o),h(a)}})};var h=function(a){a.target&&($target=c(a.target),$loader=$target.find(".action-loader"),$loader.length&&$loader.remove())}}),humhub.initModule("content",function(a,b,c){var d=b("client"),e=b("util").object,f=b("actions");a.init=function(){f.registerHandler("humhub.modules.content.actiontHandler",function(b){return a.handleAction(b)})},a.handleAction=function(a){var c=this.getContentBase(a.$trigger);if(c.length){var d=b(c.data("content-base"));if(d){var e=new d(c);if(a.handler&&e[a.handler])return e[a.handler](a),!0}else console.error("No ContentType found for "+c.data("content-base"))}return!1},a.getContentBase=function(a){return a.closest("[data-content-base]")};var g=function(a){a&&("string"==typeof a?(this.id=a,this.$=c("#"+a)):a.jquery&&(this.$=a,this.id=this.$.attr("id")))};g.prototype.getKey=function(){return this.$.data("content-key")},g.prototype.getEditUrl=function(){var a=this.$.data("content-edit-url");if(!a){var b=this.getParentContentBase("[data-content-base]");if(b)return b.getEditUrl()}return a},g.prototype.getParentContentBase=function(){var a=this.$.parent().closest("[data-content-base]");if(a.length)try{var c=b(a.data("content-base"));return new c(a)}catch(d){console.error("Could not instantiate parent content base: "+a.data("content-base"))}},g.prototype.create=function(a){this.edit(a)},g.prototype.edit=function(a){var c=this.getEditUrl(),g=this.getKey(),h=b("ui.modal").global;if(!c)return void console.error("No editUrl found for edit content action editUrl: "+c+" contentId "+g);var i=this;d.ajax(c,{data:{id:g},beforeSend:function(){h.loader()},success:function(b){h.content(b.getContent(),function(){f.bindAction(h.getBody(),"click","[data-content-save]",function(b){d.submit(h.getForm(),{success:function(c){e.isFunction(a)?a(c,h)&&h.close():(i.replaceContent(c.getContent()),h.close()),b.finish()},error:function(a){h.error(a),console.error("Error while submitting form :"+a),b.finish()}})})})},error:function(a){h.error(a),console.log("Error occured while editing content: "+a.getFirstError())}})},g.prototype.replaceContent=function(a){try{this.$.html(c(a).children())}catch(b){console.error("Error occured while replacing content: "+this.id,b)}},g.prototype["delete"]=function(){},a.Content=g}),humhub.initModule("stream",function(a,b,c){var d="#wallEntry_",e="wallStream",f=function(a){"string"==typeof a?(this.id=a,this.$=c("#"+a)):a.jquery&&(this.$=a,this.id=this.$.attr("id"))};f.prototype.remove=function(){this.$.remove()},f.prototype.getContentKey=function(){},f.prototype.edit=function(){},f.prototype["delete"]=function(){},f.prototype.getContent=function(){return this.$.find(".content")};var g=function(a){this.id=a,this.$=c("#"+a)};g.prototype.getEntry=function(b){return new a.Entry(this.$.find(d+b))},g.prototype.wallStick=function(a){c.ajax({dataType:"json",type:"post",url:a}).done(function(a){a.success?currentStream&&(c.each(a.wallEntryIds,function(a,b){currentStream.deleteEntry(b),currentStream.prependEntry(b)}),c("html, body").animate({scrollTop:0},"slow")):alert(a.errorMessage)})},g.prototype.wallUnstick=function(a){c.ajax({dataType:"json",type:"post",url:a}).done(function(a){a.success&¤tStream.showStream()})},g.prototype.wallArchive=function(a){url=wallArchiveLinkUrl.replace("-id-",a),c.ajax({dataType:"json",type:"post",url:url}).done(function(a){a.success&¤tStream&&c.each(a.wallEntryIds,function(a,b){function d(){c("#wallEntry_"+b).fadeOut("slow")}setInterval(d(),1e3)})})},g.prototype.wallUnarchive=function(a){url=wallUnarchiveLinkUrl.replace("-id-",a),c.ajax({dataType:"json",type:"post",url:url}).done(function(a){a.success&¤tStream&&c.each(a.wallEntryIds,function(a,b){currentStream.reloadWallEntry(b)})})};var h=function(){return a.mainStream||(a.mainStream=new a.Stream(e)),a.mainStream},i=function(b){return a.getStream().getEntry(b)};a["export"]({getStream:h,getEntry:i})}),humhub.initModule("ui.modal",function(a,b,c){var d=b("additions"),e=(b("actions"),[]),f='
',g='',h='',i="Error",j="An unknown error occured!",k=function(a){this.$modal=c("#"+a),this.$modal.lengh||this.createModal(a),this.initModal(),e.push(this)};k.prototype.createModal=function(a){this.$modal=c(f).attr("id",a),c("body").append(this.$modal)},k.prototype.initModal=function(){this.reset();var a=this;this.getDialog().on("click","[data-modal-close]",function(){a.close()}).on("click","[data-modal-clear-error]",function(){a.clearErrorMessage()}).on("click",function(a){a.stopPropagation()}),this.$modal.on("click",function(){a.close()})},k.prototype.close=function(){var a=this;this.$modal.fadeOut("fast",function(){a.getContent().html(""),a.reset()})},k.prototype.loader=function(){this.reset(),this.show()},k.prototype.reset=function(){this.content(''),this.isFilled=!1},k.prototype.content=function(a,b){try{var c=this;this.clearErrorMessage(),this.getContent().html(a).promise().always(function(){d.applyTo(c.getContent()),!b||b(this.$modal)}),this.isFilled=!0}catch(e){console.error("Error while setting modal content",e),this.setErrorMessage(e.message),d.applyTo(c.$modal)}},k.prototype.error=function(a,b){1===arguments.length&&a&&(b=a.getFirstError?a.getFirstError():a,a=a.getErrorTitle?a.getErrorTitle():i),a=a||i,b=b||j,this.isFilled?this.setErrorMessage(b):(this.clear(),this.setTitle(a),this.setBody(""),this.setErrorMessage(b),this.$modal.show())},k.prototype.clearErrorMessage=function(){var a=this.getErrorMessage();a.length&&a.fadeOut("fast",function(){a.remove()})},k.prototype.setErrorMessage=function(a){var b=this.getErrorMessage();b.length?(b.css("opacity",0),b.text(a),b.animate({opacity:1},"fast")):this.getBody().prepend(''+a+"
")},k.prototype.getErrorMessage=function(){return this.getContent().find(".modal-error")},k.prototype.show=function(){this.$modal.show()},k.prototype.clear=function(){this.getContent().empty()},k.prototype.getContent=function(){return this.$modal.find(".modal-content:first")},k.prototype.getDialog=function(){return this.$modal.find(".modal-dialog")},k.prototype.getForm=function(){return this.$modal.find("form")},k.prototype.setTitle=function(a){var b=this.getHeader();b.length||(this.getContent().prepend(c(g)),b=this.getHeader()),b.find(".modal-title").html(a)},k.prototype.setBody=function(a){var b=this.getBody();b.length||(this.getContent().append(c(h)),b=this.getBody()),b.html(a)},k.prototype.getHeader=function(){return this.$modal.find(".modal-header")},k.prototype.getBody=function(){return this.$modal.find(".modal-body")},a["export"]({init:function(){a.global=new k("global-modal")},Modal:k})});
\ No newline at end of file
+var humhub=humhub||function(a){var b={},c=[],d=!1,e=function(b,e){var h=g(b,!0);h.id="humhub.modules."+i(b),h.require=f,h["export"]=function(b){a.extend(h,b)},e(h,f,a),d?h.init():c.push(h)},f=function(a){var b=g(a);return b||console.warn("No module found for id: "+a),b},g=function(c,d){try{var e=i(c),f=b;return a.each(e.split("."),function(a,b){if(b in f)f=f[b];else{if(!d)return f=void 0,!1;f=f[b]={}}}),f}catch(g){return}},h={get:function(a,b,c){if(l(b)){var d=this.getModuleConfig(a)[b];return l(d)?d:c}},getModuleConfig:function(a){return this.module||(this.module={}),this.module},is:function(a,b,c){return this.get(a,b,c)===!0},set:function(b,c,d){2===arguments.length?a.extend(this.getModuleConfig(b),c):3===arguments.length&&(this.getModuleConfig(b)[c]=d)}},i=function(a){return j(j(a,"humhub."),"modules.")},j=function(a,b){return k(a,b)?a.substring(b.length,a.length):a},k=function(a,b){return a&&b?0===a.indexOf(b):!1},l=function(a){return"undefined"!=typeof a};return a(document).ready(function(){a.each(c,function(a,b){b.init&&b.init(),d=!0,console.log("Module initialized: "+b.id)})}),{initModule:e,modules:b,config:h}}($);humhub.initModule("util",function(a,b,c){var d={isFunction:function(a){return c.isFunction(a)},isObject:function(a){return c.isPlainObject(a)},isJQuery:function(a){return this.isDefined(a)&&a.jquery},isArray:function(a){return c.isArray(a)},isEmpty:function(a){return c.isEmptyObject(a)},isString:function(a){return"string"==typeof a},isNumber:function(a){return this.isDefined(a)&&!isNaN(parseFloat(a))&&isFinite(a)},isBoolean:function(a){return"boolean"==typeof a},isDefined:function(a){if(arguments.length>1){var b=!0,c=this;return this.each(arguments,function(a,b){return c.isDefined(b)?void 0:!1}),b}return"undefined"!=typeof a},inherits:function(a,b){a.prototype=Object.create(b.prototype),a._super=b.prototype}},e={cutprefix:function(a,b){return this.startsWith(a,b)?a.substring(b.length,a.length):a},cutsuffix:function(a,b){return a.slice(0,-1*b.length)},startsWith:function(a,b){return d.isDefined(a)&&d.isDefined(b)?0===a.indexOf(b):!1},endsWith:function(a,b){return d.isDefined(a)&&d.isDefined(b)?-1!==a.indexOf(b,a.length-b.length):!1}};a["export"]({object:d,string:e})}),humhub.initModule("additions",function(a,b,c){var d={};a.registerAddition=function(a,b){d[a]||(d[a]=[]),d[a].push(b)},a.applyTo=function(a){var b=c(a);c.each(d,function(a,d){c.each(d,function(d,e){c.each(b.find(a).addBack(a),function(){try{var d=c(this);e.apply(d,[d,b])}catch(f){console.error("Error while applying addition "+e+" on selector "+a)}})})})},a.init=function(){}}),humhub.initModule("client",function(a,b,c){var d=b("util").object,e=(b("scripts"),function(){}),f=function(a){this.data=a,c.extend(this,a)};f.prototype.isConfirmation=function(){return 0===this.getStatus()},f.prototype.isError=function(){return this.getStatus()>0||this.getErrors().length},f.prototype.getStatus=function(){return this.status?this.status:-1},f.prototype.getFirstError=function(){var a=this.getErrors();return a.length?a[0]:void 0},f.prototype.setAjaxError=function(a,b,c,d,e){this.xhr=a,this.textStatus=c,this.status=e||a.status,this.errors=[b]},f.prototype.getErrors=function(){var a=this.errors||[];return d.isString(a)?[a]:a},f.prototype.toString=function(){return"{ status: "+this.getStatus()+" error: "+this.getErrors()+" data: "+this.getContent()+" }"};var g=function(a,b){var b=b||{};a=d.isString(a)?c(a):a,b.type=a.attr("method")||"post",b.data=a.serialize(),i(a.attr("action"),b)},h=function(a,b){var b=b||{};return b.type="POST",i(a,b)},i=function(a,b){return new Promise(function(b,e){var g=g||{},h=g.async||!0,i=g.dataType||"json",j=function(a,b,c,h,i){if(g.error&&d.isFunction(g.error)){var j=new f;j.setAjaxError(a,c,b,h,i),g.error(j)}e(a,b,c,h,i)},k=function(a,c,d){var e=new f(a);return e.isError()?j(d,"application",e.getErrors(),a,e.getStatus()):(g.success&&(e.textStatus=c,e.xhr=d,g.success(e)),void b(e))};c.ajax({url:a,data:g.data,type:g.type,beforeSend:g.beforeSend,processData:g.processData,contentType:g.contentType,async:h,dataType:i,success:k,error:j})})};a["export"]({ajax:i,post:h,submit:g,init:e})}),humhub.initModule("ui",function(a,b,c){var d=b("additions");a.init=function(){d.registerAddition(".autosize",function(a){a.autosize()})}}),humhub.initModule("ui.modal",function(a,b,c){var d=b("util").object,e=b("additions"),f=[],g='',h='',i='',j="Error",k="An unknown error occured!",l=function(a){this.$modal=c("#"+a),this.$modal.lengh||this.createModal(a),this.initModal(),f.push(this)};l.prototype.createModal=function(a){this.$modal=c(g).attr("id",a),c("body").append(this.$modal)},l.prototype.initModal=function(){this.reset();var a=this;this.getDialog().on("click","[data-modal-close]",function(){a.close()}).on("click","[data-modal-clear-error]",function(){a.clearErrorMessage()}).on("click",function(a){a.stopPropagation()}),this.$modal.on("click",function(){a.close()})},l.prototype.close=function(){var a=this;this.$modal.fadeOut("fast",function(){a.getContent().html(""),a.reset()})},l.prototype.loader=function(){this.reset(),this.show()},l.prototype.reset=function(){this.content(''),this.isFilled=!1},l.prototype.content=function(a,b){try{var c=this;this.clearErrorMessage(),this.getContent().html(a).promise().always(function(){e.applyTo(c.getContent()),!b||b(this.$modal)}),this.isFilled=!0}catch(d){console.error("Error while setting modal content",d),this.setErrorMessage(d.message),e.applyTo(c.$modal)}},l.prototype.error=function(a,b){1===arguments.length&&a&&(b=a.getFirstError?a.getFirstError():a,a=a.getErrorTitle?a.getErrorTitle():j),a=a||j,b=b||k,this.isFilled?this.setErrorMessage(b):(this.clear(),this.setTitle(a),this.setBody(""),this.setErrorMessage(b),this.$modal.show())},l.prototype.clearErrorMessage=function(){var a=this.getErrorMessage();a.length&&a.fadeOut("fast",function(){a.remove()})},l.prototype.setErrorMessage=function(a){var b=this.getErrorMessage();b.length?(b.css("opacity",0),b.text(a),b.animate({opacity:1},"fast")):this.getBody().prepend(''+a+"
")},l.prototype.getErrorMessage=function(){return this.getContent().find(".modal-error")},l.prototype.show=function(){this.$modal.show()},l.prototype.clear=function(){this.getContent().empty()},l.prototype.getContent=function(){return this.$modal.find(".modal-content:first")},l.prototype.getDialog=function(){return this.$modal.find(".modal-dialog")},l.prototype.getForm=function(){return this.$modal.find("form")},l.prototype.setTitle=function(a){var b=this.getHeader();b.length||(this.getContent().prepend(c(h)),b=this.getHeader()),b.find(".modal-title").html(a)},l.prototype.setBody=function(a){var b=this.getBody();b.length||(this.getContent().append(c(i)),b=this.getBody()),b.html(a)},l.prototype.getHeader=function(){return this.$modal.find(".modal-header")},l.prototype.getBody=function(){return this.$modal.find(".modal-body")};var m=function(a,b){l.call(this,a),this.initButtons()};m.prototype.initButtons=function(a){this.$confirm=this.$modal.find("[data-modal-submit]"),this.$confirm.on("click",a)},d.inherits(m,l),a["export"]({init:function(){a.global=new l("global-modal")},Modal:l})}),humhub.initModule("actions",function(a,b,c){var d={},e=b("util").object,f=b("util").string,g=b("client");a.init=function(){this.bindAction(document,"click","[data-action-click]"),this.bindAction(document,"dblclick","[data-action-dblclick]"),this.bindAction(document,"change","[data-action-mouseout]"),b("additions").registerAddition("[data-action-load-button]",function(){var a=this;this.on("click.humhub-action-load-button",function(b){a.find(".action-loader").length||a.append('')})})},a.registerHandler=function(a,b){a&&b&&(d[a]=b)},a.registerAjaxHandler=function(a,b,f,h){h=h||{},a&&(e.isFunction(b)?(h.success=b,h.error=f):h=b,b&&(d[a]=function(a){var b=c(this).data("url-"+a.type)||c(this).data("url");g.ajax(b,h,a)}))},a.bindAction=function(a,g,i,j){a=a||document;var k=a.jquery?a:c(a);k.on(g+".humhub-action",i,function(a){a.preventDefault(),$trigger=c(this);var i=$trigger.data("action-"+g),k={type:g,$trigger:$trigger,handler:i};k.finish=function(){h(a)};try{if(e.isFunction(j))j.apply($trigger,[k]);else if(d[i]){var l=d[i];l.apply($trigger,[k])}else if(!d["humhub.modules.content.actiontHandler"](k)){var m=i.split("."),l=m[m.length-1],n=b(f.cutsuffix(i,"."+l));n[l]({type:g,$trigger:$trigger})}}catch(o){console.error('Error while handling action event for handler "'+i+'"',o),h(a)}})};var h=function(a){a.target&&($target=c(a.target),$loader=$target.find(".action-loader"),$loader.length&&$loader.remove())}}),humhub.initModule("content",function(a,b,c){var d=b("client"),e=b("util").object,f=b("actions"),g=function(a){a&&(this.$=e.isString(a)?c("#"+a):a,this.contentBase=this.$.data("content-base"))};g.prototype.getContentActions=function(){return["create","edit","delete"]},g.prototype.getKey=function(){return this.$.data("content-pk")},g.prototype.data=function(a){var b=this.$.data(a);if(!b){var c=this.getParentContentBase();if(c)return c.data(a)}return b},g.prototype.getParentContentBase=function(){var a=this.$.parent().closest("[data-content-base]");if(a.length)try{var c=b(a.data("content-base"));return new c(a)}catch(d){console.error("Could not instantiate parent content base: "+a.data("content-base"))}},g.prototype.create=function(a){indexOf(this.getContentActions(),"create")<0||this.edit(a)},g.prototype.edit=function(a){if(!(indexOf(this.getContentActions(),"edit")<0)){var c=this.data("content-edit-url"),g=this.getKey(),h=b("ui.modal").global;if(!c)return void console.error("No editUrl found for edit content action editUrl: "+c+" contentId "+g);var i=this;d.ajax(c,{data:{id:g},beforeSend:function(){h.loader()},success:function(b){h.content(b.getContent(),function(){f.bindAction(h.getBody(),"click","[data-content-save]",function(b){d.submit(h.getForm(),{success:function(c){e.isFunction(a)?a(c,h)&&h.close():(i.replaceContent(c.getContent()),h.close()),b.finish()},error:function(a){h.error(a),console.error("Error while submitting form :"+a),b.finish()}})})})},error:function(a){h.error(a),console.log("Error occured while editing content: "+a.getFirstError())}})}},g.prototype["delete"]=function(){if(!(this.getContentActions().indexOf("delete")<0)){var a=this,b=this.data("content-delete-url");b?d.post(b,{data:{id:a.getKey()},success:function(b){b.success,a.remove()},error:function(a){console.error(a)}}):console.error("Content delete was called, but no url could be determined for "+this.contentBase)}},g.prototype.replaceContent=function(a){try{var b=this;this.$.animate({opacity:0},"fast",function(){b.$.html(c(a).children()),b.$.stop().animate({opacity:1},"fast"),b.highlight&&b.highlight()})}catch(d){console.error("Error occured while replacing content: "+this.$.attr("id"),d)}},g.prototype.remove=function(){var a=this;this.$.animate({height:"toggle",opacity:"toggle"},"fast",function(){a.$.remove()})},g.getContentBase=function(a){return a.closest("[data-content-base]")},g.getInstance=function(a){a=e.isString(a)?c("#"+a):a;var d=b(a.data("content-base"));return d?new d(a):void 0};var h=function(){f.registerHandler("humhub.modules.content.actiontHandler",function(b){return a.handleAction(b)})};handleAction=function(a){var b=g.getContentBase(a.$trigger);if(b.length){var c=g.getInstance(b);if(c){if(a.handler&&c[a.handler])return c[a.handler](a),!0}else console.error("No ContentType found for "+b.data("content-base"))}return!1},a["export"]({Content:g,init:h,handleAction:handleAction})}),humhub.initModule("stream",function(a,b,c){var d=b("util"),e=d.object,f=d.string,g=b("client"),h=(b("modal"),b("content").Content),i=8,j=4,k=function(a){this.$=e.isString(a)?this.$=c("#"+a):a,h.call(this)};e.inherits(k,h),k.prototype.getContentActions=function(){return["delete","edit"]},k.prototype["delete"]=function(){var a=this.getContentInstance();a&&a["delete"]?a["delete"]():k._super["delete"].call(this)},k.prototype.reload=function(){m().reload(this)},k.prototype.edit=function(){},k.prototype.getContentInstance=function(){return h.getInstance(this.$.find("[data-content-base]"))};var l=function(a){return this.$=e.isString(a)?c("#"+a):a,this.$.length?(this.$.data("stream-contentid")&&(this.contentId=parseInt(this.$.data("stream-contentid"))),this.$stream=this.$.find(".s2_stream"),this.url=this.$.data("stream"),this.$loader=this.$stream.find(".streamLoader"),this.$content=this.$stream.find(".s2_streamContent"),this.$filter=c(".wallFilterPanel"),this.filters=[],this.sort="c",void h.call(this)):void console.error("Could not initialize stream, invalid container given"+a)};e.inherits(l,h),l.prototype.getContentActions=function(){return[]},l.prototype.init=function(){return this.clear(),this.$stream.show(),this.isShowSingleEntry()?this.loadSingleEntry(this.contentId):this.loadEntries(i),this},l.prototype.clear=function(){this.lastEntryLoaded=!1,this.readOnly=!1,this.loading=!1,this.$.find(".s2_streamContent").empty(),this.$.find(".s2_stream").hide(),this.$.find(".s2_single").hide(),this.$.find(".streamLoader").hide(),this.$.find(".emptyStreamMessage").hide(),this.$.find(".emptyFilterStreamMessage").hide(),this.$.find(".back_button_holder").hide(),this.$filter.hide()},l.prototype.loadSingleEntry=function(a){this.$.find(".back_button_holder").show(),this.loadEntries(1,a+1,"")},l.prototype.reloadEntry=function(a){var b=this;return new Promise(function(c,d){if(a=a instanceof k?a:b.getEntry(a),!a)return console.warn("Attempt to reload of non existent entry: "+a),void d();var e=a.getKey();return b._load(1,e+1,"").then(function(b){b.content[e]?(a.replaceContent(b.content[e].output),c(a)):(console.warn("Reload failed: ContentId not found in response: "+e),d())},d)})},l.prototype.loadEntries=function(a,b,d,f){if(!this.loading&&!this.lastEntryLoaded){this.$loader.show(),this.loading=!0,a=a||j,b=b||this.getLastContentId(),d=d||this.getFilterString(),f=f||this.sort;var g=this;return new Promise(function(h,i){g._load(a,b,d,f).then(function(a){g.$loader.hide(),e.isEmpty(a.content)?(g.lastEntryLoaded=!0,c("#btn-load-more").hide()):(g.lastEntryLoaded=a.is_last,g.appendEntries(a)),g.loading=!1,g.onChange(),h()})["catch"](function(a){g.loading=!1,g.$loader.hide(),i()})})}},l.prototype._load=function(a,b,c,d){return g.ajax(this.url,{data:{filters:c,sort:d,from:b,limit:a}})},l.prototype.getLastContentId=function(){var a=this.$stream.find("[data-content-pk]").last();return a.length?a.data("stream-contentid"):void 0},l.prototype.appendEntries=function(a){var b=this,d="";return c.each(a.contentIds,function(c,e){var f=b.$.find('[data-content-pk="'+e+'"]');f.length&&f.remove(),d+=a.content[e].output}),this.$content.append(d)},l.prototype.onChange=function(){this.readOnly?(c(".wallReadOnlyHide").hide(),c(".wallReadOnlyShow").show()):c(".wallReadOnlyShow").hide();var a=this.hasEntries();a||this.hasFilter()?a?this.isShowSingleEntry()||(this.$filter.show(),this.$.find(".emptyStreamMessage").hide(),this.$.find(".emptyFilterStreamMessage").hide()):this.$.find(".emptyFilterStreamMessage").hide():(this.$.find(".emptyStreamMessage").show(),this.$filter.hide())},l.prototype.isShowSingleEntry=function(){return e.isDefined(this.contentId)},l.prototype.hasEntries=function(){return this.getEntryCount()>0},l.prototype.getEntryCount=function(){return this.$.find("[data-content-pk]").length},l.prototype.hasFilter=function(){return this.filters.length>0},l.prototype.getFilterString=function(){var a="";return c.each(this.filters,function(b,c){a+=c+","}),f.cutsuffix(a,",")},l.prototype.setFilter=function(a){this.filters.indexOf(a)<0&&this.filters.push(a)},l.prototype.unsetFilter=function(a){var b=this.filters.indexOf(a);b>-1&&this.filters.splice(b,1)},l.prototype.getEntry=function(a){return new k(this.$.find('[data-content-pk="'+a+'"]'))},l.prototype.getEntryByNode=function(a){return new k(a.closest("[data-content-pk]"))};var m=function(){return a.instance||(a.instance=new l(c("[data-stream]"))),a.instance},n=function(b){return a.getStream().getEntry(b)},o=function(){var a=m().init();c(window).scroll(function(){c(window).scrollTop()==c(document).height()-c(window).height()&&(!a||a.loading||a.isShowSingleEntry()||a.lastEntryLoaded||a.loadEntries())}),a.$.on("click",".singleBackLink",function(){a.contentId=void 0,a.init(),c(this).hide()}),p()},p=function(){c(".wallFilter").click(function(){var a=c(this),b=a.children("i");b.toggleClass("fa-square-o").toggleClass("fa-check-square-o"),b.hasClass("fa-check-square-o")?m().setFilter(a.attr("id").replace("filter_","")):m().unsetFilter(a.attr("id").replace("filter_","")),m().init()}),c(".wallSorting").click(function(){var a=c(this).attr("id");c(".wallSorting").find("i").removeClass("fa-check-square-o").addClass("fa-square-o"),c("#"+a).children("i").removeClass("fa-square-o").addClass("fa-check-square-o"),a=a.replace("sorting_",""),m().sort=a,m().init()})};a["export"]({StreamEntry:k,Stream:l,getStream:m,getEntry:n,init:o})});
\ No newline at end of file
diff --git a/js/humhub.client.js b/js/humhub.client.js
index 3c2e56d208..3d355a686b 100644
--- a/js/humhub.client.js
+++ b/js/humhub.client.js
@@ -33,6 +33,7 @@ humhub.initModule('client', function (module, require, $) {
*/
var Response = function (data) {
this.data = data;
+ $.extend(this, data);
};
/**
@@ -52,14 +53,10 @@ humhub.initModule('client', function (module, require, $) {
Response.prototype.isError = function () {
return this.getStatus() > 0 || this.getErrors().length;
};
-
- Response.prototype.getStatus = function () {
- return (this.data && object.isDefined(this.data.status)) ? this.data.status : -1;
- };
- Response.prototype.getErrorTitle = function() {
- return (this.data) ? this.data.errorTitle : undefined;
- };
+ Response.prototype.getStatus = function () {
+ return (this.status) ? this.status : -1;
+ };
Response.prototype.getFirstError = function() {
var errors = this.getErrors();
@@ -71,9 +68,8 @@ humhub.initModule('client', function (module, require, $) {
Response.prototype.setAjaxError = function(xhr, errorThrown, textStatus,data , status) {
this.xhr = xhr;
this.textStatus = textStatus;
- this.data = data || {};
- this.data.status = status || xhr.status;
- this.data.errors = [errorThrown];
+ this.status = status || xhr.status;
+ this.errors = [errorThrown];
};
/**
@@ -82,40 +78,8 @@ humhub.initModule('client', function (module, require, $) {
* @returns {array} error array or empty array
*/
Response.prototype.getErrors = function () {
- if (this.data) {
- var errors = this.data.errors || [];
- return (object.isString(errors)) ? [errors] : errors;
- }
- return [];
- };
-
- /**
- * Returns the raw content object. The content object can either be an
- * object with multiple partials {partialId: content string} or a single content string.
- * @param {type} id
- * @returns {undefined|humhub.client_L5.Response.data.content}1
- */
- Response.prototype.getContent = function () {
- return this.data.content;
- };
-
- /**
- * Returns the response partial. If no id is given we return the first partial
- * we find.
- * @returns {humhub.client_L5.Response.data.content}
- */
- Response.prototype.getPartial = function (id) {
- if (!this.data) {
- return;
- }
- //TODO: handleResponse filter...
- if (object.isObject(this.data.content)) {
- return (id) ? this.data.content[id] : this.data.content;
- } else if (!id) {
- return this.data.content;
- }
-
- return;
+ var errors = this.errors || [];
+ return (object.isString(errors)) ? [errors] : errors;
};
Response.prototype.toString = function () {
@@ -126,53 +90,62 @@ humhub.initModule('client', function (module, require, $) {
var cfg = cfg || {};
$form = object.isString($form) ? $($form) : $form;
cfg.type = $form.attr('method') || 'post';
- cfg.data = $form.serialize()
+ cfg.data = $form.serialize();
ajax($form.attr('action'), cfg);
};
- var ajax = function (path, cfg) {
+ var post = function(path, cfg) {
var cfg = cfg || {};
- var async = cfg.async || true;
- var dataType = cfg.dataType || "json";
+ cfg.type = 'POST';
+ return ajax(path, cfg);
+ };
- var error = function (xhr, textStatus, errorThrown, data, status) {
- //Textstatus = "timeout", "error", "abort", "parsererror", "application"
- if (cfg.error && object.isFunction(cfg.error)) {
- var response = new Response();
- response.setAjaxError(xhr, errorThrown, textStatus, data, status);
- cfg.error(response);
- } else {
- console.warn('Unhandled ajax error: ' + path + " type" + type + " error: " + errorThrown);
- }
- };
+ var ajax = function (path, cfg) {
+ return new Promise(function(resolve, reject) {
+ var cfg = cfg || {};
+ var async = cfg.async || true;
+ var dataType = cfg.dataType || "json";
- var success = function (json, textStatus, xhr) {
- var response = new Response(json);
- if (response.isError()) { //Application errors
- return error(xhr, "application", response.getErrors(), json, response.getStatus() );
- } else if (cfg.success) {
- response.textStatus = textStatus;
- response.xhr = xhr;
- cfg.success(response);
- }
- };
+ var error = function (xhr, textStatus, errorThrown, data, status) {
+ //Textstatus = "timeout", "error", "abort", "parsererror", "application"
+ if (cfg.error && object.isFunction(cfg.error)) {
+ var response = new Response();
+ response.setAjaxError(xhr, errorThrown, textStatus, data, status);
+ cfg.error(response);
+ }
+ reject(xhr, textStatus, errorThrown, data, status);
+ };
- $.ajax({
- url: path,
- data: cfg.data,
- type: cfg.type,
- beforeSend: cfg.beforeSend,
- processData: cfg.processData,
- contentType: cfg.contentType,
- async: async,
- dataType: dataType,
- success: success,
- error: error
+ var success = function (json, textStatus, xhr) {
+ var response = new Response(json);
+ if (response.isError()) { //Application errors
+ return error(xhr, "application", response.getErrors(), json, response.getStatus() );
+ } else if (cfg.success) {
+ response.textStatus = textStatus;
+ response.xhr = xhr;
+ cfg.success(response);
+ }
+ resolve(response);
+ };
+
+ $.ajax({
+ url: path,
+ data: cfg.data,
+ type: cfg.type,
+ beforeSend: cfg.beforeSend,
+ processData: cfg.processData,
+ contentType: cfg.contentType,
+ async: async,
+ dataType: dataType,
+ success: success,
+ error: error
+ });
});
};
module.export({
ajax: ajax,
+ post: post,
submit: submit,
init: init
});
diff --git a/js/humhub.content.js b/js/humhub.content.js
index d88b6bd1f1..330f71f3e5 100644
--- a/js/humhub.content.js
+++ b/js/humhub.content.js
@@ -9,67 +9,28 @@ humhub.initModule('content', function(module, require, $) {
var object = require('util').object;
var actions = require('actions');
- module.init = function() {
- actions.registerHandler('humhub.modules.content.actiontHandler', function(event) {
- return module.handleAction(event);
- });
- };
-
- /**
- * Handles the given contentAction event. The event should provide the following properties:
- *
- * $trigger (required) : the trigger node of the event
- * handler (required) : the handler functionn name to be executed on the content
- * type (optoinal) : the event type 'click', 'change',...
- *
- * @param {object} event - event object
- * @returns {Boolean} true if the contentAction could be executed else false
- */
- module.handleAction = function(event) {
- var $contentBase = this.getContentBase(event.$trigger);
- if($contentBase.length) {
- //Initialize a content instance by means of the content-base type and execute the handler
- var ContentType = require($contentBase.data('content-base'));
- if(ContentType) {
- var content = new ContentType($contentBase);
- if(event.handler && content[event.handler]) {
- content[event.handler](event);
- return true;
- }
- } else {
- console.error('No ContentType found for '+$contentBase.data('content-base'));
- }
- }
- return false;
- };
-
- module.getContentBase = function($element) {
- return $element.closest('[data-content-base]');
- };
-
- var Content = function(id) {
- if(!id) { //Create content
+ var Content = function(container) {
+ if(!container) { //Create content
return;
}
- if (typeof id === 'string') {
- this.id = id;
- this.$ = $('#' + id);
- } else if (id.jquery) {
- this.$ = id;
- this.id = this.$.attr('id');
- }
+ this.$ = (object.isString(container)) ? $('#' + container) : container;
+ this.contentBase = this.$.data('content-base');
+ };
+
+ Content.prototype.getContentActions = function() {
+ return ['create','edit','delete'];
};
Content.prototype.getKey = function () {
- return this.$.data('content-key');
+ return this.$.data('content-pk');
};
- Content.prototype.getEditUrl = function () {
- var result = this.$.data('content-edit-url');
+ Content.prototype.data = function(dataSuffix) {
+ var result = this.$.data(dataSuffix);
if(!result) {
- var parentContent = this.getParentContentBase('[data-content-base]');
+ var parentContent = this.getParentContentBase();
if(parentContent) {
- return parentContent.getEditUrl();
+ return parentContent.data(dataSuffix);
}
}
return result;
@@ -89,11 +50,19 @@ humhub.initModule('content', function(module, require, $) {
Content.prototype.create = function (addContentHandler) {
//Note that this Content won't have an id, so the backend will create an instance
+ if(indexOf(this.getContentActions(), 'create') < 0) {
+ return;
+ }
+
this.edit(addContentHandler);
};
Content.prototype.edit = function (successHandler) {
- var editUrl = this.getEditUrl();
+ if(indexOf(this.getContentActions(), 'edit') < 0) {
+ return;
+ }
+
+ var editUrl = this.data('content-edit-url');
var contentId = this.getKey();
var modal = require('ui.modal').global;
@@ -146,20 +115,103 @@ humhub.initModule('content', function(module, require, $) {
});
};
- Content.prototype.replaceContent = function(content) {
- try {
- this.$.html($(content).children());
- } catch(e) {
- console.error('Error occured while replacing content: '+this.id , e);
+ Content.prototype.delete = function () {
+ if(this.getContentActions().indexOf('delete') < 0) {
+ return;
+ }
+
+ var that = this;
+ var url = this.data('content-delete-url');
+ if(url) {
+ client.post(url, {
+ data: {
+ id: that.getKey()
+ },
+ success: function(json) {
+ json.success;
+ that.remove();
+ },
+ error: function(json) {
+ console.error(json);
+ }
+ })
+ } else {
+ console.error('Content delete was called, but no url could be determined for '+this.contentBase);
}
};
- Content.prototype.delete = function () {
- //Search for data-content-delte-url on root.
- //if(this.deleteModal) {open modal bla}
- //Call this url with data-content-pk
- //Trigger delete event
+ Content.prototype.replaceContent = function(content) {
+ try {
+ var that = this;
+ this.$.animate({ opacity: 0 }, 'fast', function() {
+ that.$.html($(content).children());
+ that.$.stop().animate({ opacity: 1 }, 'fast');
+ if(that.highlight) {
+ that.highlight();
+ }
+ });
+ } catch(e) {
+ console.error('Error occured while replacing content: '+this.$.attr('id') , e);
+ }
};
- module.Content = Content;
+ Content.prototype.remove = function() {
+ var that = this;
+ this.$.animate({ height: 'toggle', opacity: 'toggle' }, 'fast', function() {
+ that.$.remove();
+ //TODO: fire global event
+ });
+ };
+
+ Content.getContentBase = function($element) {
+ return $element.closest('[data-content-base]');
+ };
+
+ Content.getInstance = function($contentBase) {
+ $contentBase = (object.isString($contentBase)) ? $('#'+$contentBase) : $contentBase;
+ var ContentType = require($contentBase.data('content-base'));
+ if(ContentType) {
+ return new ContentType($contentBase);
+ }
+ };
+
+ var init = function() {
+ actions.registerHandler('humhub.modules.content.actiontHandler', function(event) {
+ return module.handleAction(event);
+ });
+ };
+
+ /**
+ * Handles the given contentAction event. The event should provide the following properties:
+ *
+ * $trigger (required) : the trigger node of the event
+ * handler (required) : the handler functionn name to be executed on the content
+ * type (optoinal) : the event type 'click', 'change',...
+ *
+ * @param {object} event - event object
+ * @returns {Boolean} true if the contentAction could be executed else false
+ */
+ handleAction = function(event) {
+ var $contentBase = Content.getContentBase(event.$trigger);
+ if($contentBase.length) {
+ //Initialize a content instance by means of the content-base type and execute the handler
+ var content = Content.getInstance($contentBase);
+ if(content) {
+ //Check if the content instance provides this actionhandler
+ if(event.handler && content[event.handler]) {
+ content[event.handler](event);
+ return true;
+ }
+ } else {
+ console.error('No ContentType found for '+$contentBase.data('content-base'));
+ }
+ }
+ return false;
+ };
+
+ module.export({
+ Content : Content,
+ init : init,
+ handleAction: handleAction
+ });
});
\ No newline at end of file
diff --git a/js/humhub.core.js b/js/humhub.core.js
index 0400b2ea54..972aa965e2 100644
--- a/js/humhub.core.js
+++ b/js/humhub.core.js
@@ -149,6 +149,38 @@ var humhub = humhub || (function($) {
}
};
+ /**
+ * Config implementation
+ */
+
+ var config = {
+ get : function(module, key, defaultVal) {
+ if(_isDefined(key)) {
+ var result = this.getModuleConfig(module)[key];
+ return (_isDefined(result)) ? result : defaultVal;
+ }
+ },
+ getModuleConfig: function(module) {
+ if(!this.module) {
+ this.module = {};
+ }
+ return this.module;
+ },
+
+ is : function(module, key, defaultVal) {
+ return this.get(module, key,defaultVal) === true;
+ },
+
+ set : function(module, key, value) {
+ //Moduleid with multiple values
+ if(arguments.length === 2) {
+ $.extend(this.getModuleConfig(module), key);
+ } else if(arguments.length === 3) {
+ this.getModuleConfig(module)[key] = value;
+ }
+ }
+ };
+
/**
* Cuts the prefix humub.modules or modules. from the given value.
* @param {type} value
@@ -186,6 +218,10 @@ var humhub = humhub || (function($) {
return val.indexOf(prefix) === 0;
};
+ var _isDefined = function(obj) {
+ return typeof obj !== 'undefined';
+ };
+
//Initialize all initial modules
$(document).ready(function() {
$.each(initialModules, function(i, module) {
@@ -197,7 +233,11 @@ var humhub = humhub || (function($) {
});
});
+
+
return {
- initModule: initModule
+ initModule: initModule,
+ modules: modules,
+ config: config
};
})($);
\ No newline at end of file
diff --git a/js/humhub.stream.js b/js/humhub.stream.js
index 19b2a930cf..1ee4d70dff 100644
--- a/js/humhub.stream.js
+++ b/js/humhub.stream.js
@@ -2,183 +2,480 @@
* Core module for managing Streams and StreamItems
* @type Function
*/
-humhub.initModule('stream', function(module, require, $) {
+humhub.initModule('stream', function (module, require, $) {
- var ENTRY_ID_SELECTOR_PREFIX = '#wallEntry_';
- var WALLSTREAM_ID = 'wallStream';
+ var util = require('util');
+ var object = util.object;
+ var string = util.string;
+ var client = require('client');
+ var modal = require('modal');
+ var Content = require('content').Content;
+
+ var STREAM_INIT_COUNT = 8;
+ var STREAM_LOAD_COUNT = 4;
+
+ //TODO: load streamUrl from config
+ //TODO: readonly
/**
- * Base class for all StreamItems
+ * Base class for all StreamContent
* @param {type} id
* @returns {undefined}
*/
- var StreamItem = function (id) {
- if (typeof id === 'string') {
- this.id = id;
- this.$ = $('#' + id);
- } else if (id.jquery) {
- this.$ = id;
- this.id = this.$.attr('id');
+ var StreamEntry = function (id) {
+ this.$ = object.isString(id) ? this.$ = $('#' + id) : id;
+ Content.call(this);
+ };
+
+ object.inherits(StreamEntry, Content);
+
+ StreamEntry.prototype.getContentActions = function() {
+ return ['delete', 'edit'];
+ };
+
+ StreamEntry.prototype.delete = function () {
+ var content = this.getContentInstance();
+ if(content && content.delete) {
+ //TODO: modalconfirm
+ content.delete();
+ } else {
+ StreamEntry._super.delete.call(this);
}
};
-
- /**
- * Removes the stream item from stream
- */
- StreamItem.prototype.remove = function () {
- this.$.remove();
+
+ StreamEntry.prototype.reload = function () {
+ getStream().reload(this);
};
-
- StreamItem.prototype.getContentKey = function () {}
-
- StreamItem.prototype.edit = function () {
+
+ StreamEntry.prototype.edit = function () {
//Search for data-content-edit-url on root.
//Call this url with data-content-pk
//Trigger delete event
};
+
+ StreamEntry.prototype.getContentInstance = function () {
+ return Content.getInstance(this.$.find('[data-content-base]'));
+ };
- StreamItem.prototype.delete = function () {
- //Search for data-content-delte-url on root.
- //Call this url with data-content-pk
- //Trigger delete event
- };
-
- StreamItem.prototype.getContent = function () {
- return this.$.find('.content');
- };
-
-/*
- module.StreamItem.prototype.highlightContent = function () {
- var $content = this.getContent();
- $content.addClass('highlight');
- $content.delay(200).animate({backgroundColor: 'transparent'}, 1000, function () {
- $content.removeClass('highlight');
- $content.css('backgroundColor', '');
- });
- };
-*/
/**
- * Stream implementation
- * @param {type} id
+ * Stream implementation.
+ *
+ * @param {type} container id or jQuery object of the stream container
* @returns {undefined}
*/
- var Stream = function (id) {
- this.id = id;
- this.$ = $('#' + id);
- };
-
- Stream.prototype.getEntry = function (id) {
- //Search for data-content-base and try to initiate the Item class
+ var Stream = function (container) {
+ this.$ = (object.isString(container)) ? $('#' + container) : container;
- return new module.Entry(this.$.find(ENTRY_ID_SELECTOR_PREFIX + id));
- };
+ if (!this.$.length) {
+ console.error('Could not initialize stream, invalid container given'+ container);
+ return;
+ }
+
+ //If a contentId is set on the stream root we will only show the single content
+ if(this.$.data('stream-contentid')) {
+ this.contentId = parseInt(this.$.data('stream-contentid'));
+ }
+
+ this.$stream = this.$.find(".s2_stream");
+
+ //Cache some stream relevant data/nodes
+ this.url = this.$.data('stream'); //TODO: set this in config instead of data field
+ this.$loader = this.$stream.find(".streamLoader");
+ this.$content = this.$stream.find('.s2_streamContent');
+ this.$filter = $('.wallFilterPanel');
- Stream.prototype.wallStick = function (url) {
- $.ajax({
- dataType: "json",
- type: 'post',
- url: url
- }).done(function (data) {
- if (data.success) {
- if (currentStream) {
- $.each(data.wallEntryIds, function (k, wallEntryId) {
- currentStream.deleteEntry(wallEntryId);
- currentStream.prependEntry(wallEntryId);
- });
- $('html, body').animate({scrollTop: 0}, 'slow');
- }
- } else {
- alert(data.errorMessage);
- }
- });
+ //TODO: make this configurable
+ this.filters = [];
+ this.sort = "c";
+
+ Content.call(this);
};
-
- Stream.prototype.wallUnstick = function (url) {
- $.ajax({
- dataType: "json",
- type: 'post',
- url: url
- }).done(function (data) {
- if (data.success) {
- //Reload the whole stream, since we have to reorder the entries
- currentStream.showStream();
- }
- });
+
+ object.inherits(Stream, Content);
+
+ Stream.prototype.getContentActions = function() {
+ return [];
};
-
+
/**
- * Click Handler for Archive Link of Wall Posts
- * (archiveLink.php)
+ * Initializes the stream, by clearing the stream and reloading initial stream entries,
+ * this should be called if any filter/sort settings are changed or the stream
+ * needs an reload.
*
- * @param {type} className
- * @param {type} id
+ * @returns {humhub.stream_L5.Stream.prototype}
*/
- Stream.prototype.wallArchive = function (id) {
-
- url = wallArchiveLinkUrl.replace('-id-', id);
-
- $.ajax({
- dataType: "json",
- type: 'post',
- url: url
- }).done(function (data) {
- if (data.success) {
- if (currentStream) {
- $.each(data.wallEntryIds, function (k, wallEntryId) {
- //currentStream.reloadWallEntry(wallEntryId);
- // fade out post
- setInterval(fadeOut(), 1000);
-
- function fadeOut() {
- // fade out current archived post
- $('#wallEntry_' + wallEntryId).fadeOut('slow');
- }
- });
- }
- }
- });
+ Stream.prototype.init = function () {
+ this.clear();
+ this.$stream.show();
+ if (this.isShowSingleEntry()) {
+ this.loadSingleEntry(this.contentId);
+ } else {
+ this.loadEntries(STREAM_INIT_COUNT);
+ }
+ return this;
+ };
+
+ Stream.prototype.clear = function() {
+ this.lastEntryLoaded = false;
+ this.readOnly = false;
+ this.loading = false;
+ this.$.find(".s2_streamContent").empty();
+ this.$.find(".s2_stream").hide();
+ this.$.find(".s2_single").hide();
+ this.$.find(".streamLoader").hide();
+ this.$.find(".emptyStreamMessage").hide();
+ this.$.find(".emptyFilterStreamMessage").hide();
+ this.$.find('.back_button_holder').hide();
+ this.$filter.hide();
};
-
- /**
- * Click Handler for Un Archive Link of Wall Posts
- * (archiveLink.php)
- *
- * @param {type} className
- * @param {type} id
- */
- Stream.prototype.wallUnarchive = function (id) {
- url = wallUnarchiveLinkUrl.replace('-id-', id);
-
- $.ajax({
- dataType: "json",
- type: 'post',
- url: url
- }).done(function (data) {
- if (data.success) {
- if (currentStream) {
- $.each(data.wallEntryIds, function (k, wallEntryId) {
- currentStream.reloadWallEntry(wallEntryId);
- });
-
- }
+ Stream.prototype.loadSingleEntry = function(contentId) {
+ this.$.find('.back_button_holder').show();
+ this.loadEntries(1, (contentId + 1), '');
+ };
+
+ Stream.prototype.reloadEntry = function(entry) {
+ var that = this;
+ return new Promise(function(resolve, reject) {
+ entry = (entry instanceof StreamEntry) ? entry : that.getEntry(entry);
+
+ if(!entry) {
+ console.warn('Attempt to reload of non existent entry: '+entry);
+ reject();
+ return;
}
+
+ var contentId = entry.getKey();
+ return that._load(1, (contentId + 1), '').then(function(response) {
+ if(response.content[contentId]) {
+ entry.replaceContent(response.content[contentId].output);
+ resolve(entry);
+ } else {
+ console.warn('Reload failed: ContentId not found in response: '+contentId);
+ reject();
+ }
+ }, reject);
+ });
+ };
+
+ Stream.prototype.loadEntries = function (limit, from, filter, sort) {
+ if (this.loading || this.lastEntryLoaded) {
+ return;
+ }
+
+ //Initialize loading process
+ this.$loader.show();
+ this.loading = true;
+
+ //Overwrite the stream settings if provided
+ limit = limit || STREAM_LOAD_COUNT;
+ from = from || this.getLastContentId();
+ filter = filter || this.getFilterString();
+ sort = sort || this.sort;
+
+ var that = this;
+ return new Promise(function(resolve, reject) {
+ that._load(limit, from, filter,sort).then(function(response) {
+ that.$loader.hide();
+ if (object.isEmpty(response.content)) {
+ that.lastEntryLoaded = true;
+ $('#btn-load-more').hide();
+ } else {
+ that.lastEntryLoaded = response.is_last;
+ that.appendEntries(response);
+ }
+
+ that.loading = false;
+ that.onChange();
+ resolve();
+ }).catch(function(err) {
+ //TODO: handle error
+ that.loading = false;
+ that.$loader.hide();
+ reject();
+ });
});
};
- var getStream = function () {
- if (!module.mainStream) {
- module.mainStream = new module.Stream(WALLSTREAM_ID);
+ Stream.prototype._load = function (limit, from, filter, sort) {
+ return client.ajax(this.url, {
+ data: {
+ filters: filter,
+ sort: sort,
+ from: from,
+ limit: limit
+ }
+ });
+ };
+
+ Stream.prototype.getLastContentId = function () {
+ var $lastEntry = this.$stream.find('[data-content-pk]').last();
+ if ($lastEntry.length) {
+ return $lastEntry.data('stream-contentid');
}
- return module.mainStream;
+ };
+
+ Stream.prototype.appendEntries = function (response) {
+ var that = this;
+ var result = '';
+ $.each(response.contentIds, function (i, key) {
+ var $entry = that.$.find('[data-content-pk="' + key + '"]');
+ if ($entry.length) {
+ $entry.remove();
+ }
+ result += response.content[key].output;
+ });
+ return this.$content.append(result);
+ };
+
+ /**
+ * Fired when new entries are shown
+ */
+ Stream.prototype.onChange = function () {
+ if (this.readOnly) {
+ $('.wallReadOnlyHide').hide();
+ $('.wallReadOnlyShow').show();
+ } else {
+ $('.wallReadOnlyShow').hide();
+ }
+
+ var hasEntries = this.hasEntries();
+ if (!hasEntries && !this.hasFilter()) {
+ this.$.find('.emptyStreamMessage').show();
+ this.$filter.hide();
+ } else if (!hasEntries) {
+ this.$.find('.emptyFilterStreamMessage').hide();
+ } else if(!this.isShowSingleEntry()) {
+ this.$filter.show();
+ this.$.find('.emptyStreamMessage').hide();
+ this.$.find('.emptyFilterStreamMessage').hide();
+ }
+
+ //TODO: fire global event
+ };
+
+ Stream.prototype.isShowSingleEntry = function () {
+ return object.isDefined(this.contentId);
+ };
+
+ Stream.prototype.hasEntries = function () {
+ return this.getEntryCount() > 0;
+ };
+
+ Stream.prototype.getEntryCount = function () {
+ return this.$.find('[data-content-pk]').length;
+ };
+
+ Stream.prototype.hasFilter = function () {
+ return this.filters.length > 0;
+ };
+
+ Stream.prototype.getFilterString = function () {
+ var result = '';
+ $.each(this.filters, function(i, filter) {
+ result += filter+',';
+ });
+
+ return string.cutsuffix(result, ',');
+ };
+
+ Stream.prototype.setFilter = function (filterId) {
+ if(this.filters.indexOf(filterId) < 0) {
+ this.filters.push(filterId);
+ }
+ };
+
+ Stream.prototype.unsetFilter = function (filterId) {
+ var index = this.filters.indexOf(filterId);
+ if(index > -1) {
+ this.filters.splice(index, 1);
+ }
+ };
+
+ Stream.prototype.getEntry = function(key) {
+ return new StreamEntry(this.$.find('[data-content-pk="' + key + '"]'));
+ };
+
+ Stream.prototype.getEntryByNode = function($childNode) {
+ return new StreamEntry($childNode.closest('[data-content-pk]'));
+ };
+
+ var getStream = function () {
+ if (!module.instance) {
+ module.instance = new Stream($('[data-stream]'));
+ }
+ return module.instance;
};
var getEntry = function (id) {
return module.getStream().getEntry(id);
};
-
+
+ var init = function () {
+ var stream = getStream().init();
+ $(window).scroll(function () {
+ if ($(window).scrollTop() == $(document).height() - $(window).height()) {
+ if (stream && !stream.loading && !stream.isShowSingleEntry() && !stream.lastEntryLoaded) {
+ stream.loadEntries();
+ }
+ }
+ });
+
+ stream.$.on('click', '.singleBackLink', function() {
+ stream.contentId = undefined;
+ stream.init();
+ $(this).hide();
+ });
+
+ initFilterNav();
+ };
+
+ var initFilterNav = function() {
+ $(".wallFilter").click(function () {
+ var $filter = $(this);
+ var checkboxi = $filter.children("i");
+ checkboxi.toggleClass('fa-square-o').toggleClass('fa-check-square-o');
+ if(checkboxi.hasClass('fa-check-square-o')) {
+ getStream().setFilter($filter.attr('id').replace('filter_', ''));
+ } else {
+ getStream().unsetFilter($filter.attr('id').replace('filter_', ''));
+ }
+ getStream().init();
+ });
+
+ $(".wallSorting").click(function () {
+ var newSortingMode = $(this).attr('id');
+
+ // uncheck all sorting
+ $(".wallSorting").find('i')
+ .removeClass('fa-check-square-o')
+ .addClass('fa-square-o');
+
+ // check current sorting mode
+ $("#" + newSortingMode).children("i")
+ .removeClass('fa-square-o')
+ .addClass('fa-check-square-o');
+
+ // remove sorting id append
+ newSortingMode = newSortingMode.replace('sorting_', '');
+
+ // Switch sorting mode and reload stream
+ getStream().sort = newSortingMode;
+ getStream().init();
+ });
+ };
+
module.export({
- getStream : getStream,
- getEntry : getEntry
+ StreamEntry: StreamEntry,
+ Stream: Stream,
+ getStream: getStream,
+ getEntry: getEntry,
+ init: init
});
-});
\ No newline at end of file
+});
+
+/* TODO:
+ Stream.prototype.wallStick = function (url) {
+ $.ajax({
+ dataType: "json",
+ type: 'post',
+ url: url
+ }).done(function (data) {
+ if (data.success) {
+ if (currentStream) {
+ $.each(data.wallEntryIds, function (k, wallEntryId) {
+ currentStream.deleteEntry(wallEntryId);
+ currentStream.prependEntry(wallEntryId);
+ });
+ $('html, body').animate({scrollTop: 0}, 'slow');
+ }
+ } else {
+ alert(data.errorMessage);
+ }
+ });
+ };
+
+ Stream.prototype.wallUnstick = function (url) {
+ $.ajax({
+ dataType: "json",
+ type: 'post',
+ url: url
+ }).done(function (data) {
+ if (data.success) {
+ //Reload the whole stream, since we have to reorder the entries
+ currentStream.showStream();
+ }
+ });
+ };
+
+ /**
+ * Click Handler for Archive Link of Wall Posts
+ * (archiveLink.php)
+ *
+ * @param {type} className
+ * @param {type} id
+
+ Stream.prototype.wallArchive = function (id) {
+
+ url = wallArchiveLinkUrl.replace('-id-', id);
+
+ $.ajax({
+ dataType: "json",
+ type: 'post',
+ url: url
+ }).done(function (data) {
+ if (data.success) {
+ if (currentStream) {
+ $.each(data.wallEntryIds, function (k, wallEntryId) {
+ //currentStream.reloadWallEntry(wallEntryId);
+ // fade out post
+ setInterval(fadeOut(), 1000);
+
+ function fadeOut() {
+ // fade out current archived post
+ $('#wallEntry_' + wallEntryId).fadeOut('slow');
+ }
+ });
+ }
+ }
+ });
+ };
+
+
+ /**
+ * Click Handler for Un Archive Link of Wall Posts
+ * (archiveLink.php)
+ *
+ * @param {type} className
+ * @param {type} id
+
+ Stream.prototype.wallUnarchive = function (id) {
+ url = wallUnarchiveLinkUrl.replace('-id-', id);
+
+ $.ajax({
+ dataType: "json",
+ type: 'post',
+ url: url
+ }).done(function (data) {
+ if (data.success) {
+ if (currentStream) {
+ $.each(data.wallEntryIds, function (k, wallEntryId) {
+ currentStream.reloadWallEntry(wallEntryId);
+ });
+
+ }
+ }
+ });
+ };
+
+
+ /*
+ module.StreamItem.prototype.highlightContent = function () {
+ var $content = this.getContent();
+ $content.addClass('highlight');
+ $content.delay(200).animate({backgroundColor: 'transparent'}, 1000, function () {
+ $content.removeClass('highlight');
+ $content.css('backgroundColor', '');
+ });
+ };
+ */
\ No newline at end of file
diff --git a/js/humhub.ui.js b/js/humhub.ui.js
index 44fe7dce5a..db51a9bc59 100644
--- a/js/humhub.ui.js
+++ b/js/humhub.ui.js
@@ -4,5 +4,5 @@ humhub.initModule('ui', function(module, require, $) {
additions.registerAddition('.autosize', function($match) {
$match.autosize();
});
- }
+ };
});
\ No newline at end of file
diff --git a/js/humhub.ui.modal.js b/js/humhub.ui.modal.js
index cea4c278e1..0c8ef06afa 100644
--- a/js/humhub.ui.modal.js
+++ b/js/humhub.ui.modal.js
@@ -16,8 +16,8 @@
* @param {type} param2
*/
humhub.initModule('ui.modal', function (module, require, $) {
+ var object = require('util').object;
var additions = require('additions');
- var actions = require('actions');
//Keeps track of all initialized modals
var modals = [];
@@ -285,6 +285,18 @@ humhub.initModule('ui.modal', function (module, require, $) {
return this.$modal.find('.modal-body');
};
+ var ConfirmModal = function(id, confirmHandler) {
+ Modal.call(this, id);
+ this.initButtons();
+ };
+
+ ConfirmModal.prototype.initButtons = function(confirmHandler) {
+ this.$confirm = this.$modal.find('[data-modal-submit]');
+ this.$confirm.on('click', confirmHandler);
+ };
+
+ object.inherits(ConfirmModal, Modal);
+
module.export({
init: function () {
module.global = new Modal('global-modal');
diff --git a/js/humhub.util.js b/js/humhub.util.js
index 01dd615b68..7fc5f337c3 100644
--- a/js/humhub.util.js
+++ b/js/humhub.util.js
@@ -15,6 +15,9 @@ humhub.initModule('util', function(module, require, $) {
isArray: function(obj) {
return $.isArray(obj);
},
+ isEmpty: function(obj) {
+ return $.isEmptyObject(obj);
+ },
isString: function (obj) {
return typeof obj === 'string';
},
@@ -46,7 +49,7 @@ humhub.initModule('util', function(module, require, $) {
var string = {
cutprefix : function(val, prefix) {
- if(!this.startsWith(prefix)) {
+ if(!this.startsWith(val, prefix)) {
return val;
}
return val.substring(prefix.length, val.length);
diff --git a/node_modules/.gitignore b/node_modules/.gitignore
deleted file mode 100644
index 86d0cb2726..0000000000
--- a/node_modules/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-# Ignore everything in this directory
-*
-# Except this file
-!.gitignore
\ No newline at end of file
diff --git a/protected/humhub/assets/AppAsset.php b/protected/humhub/assets/AppAsset.php
index fa0acd900d..2c5918fe69 100755
--- a/protected/humhub/assets/AppAsset.php
+++ b/protected/humhub/assets/AppAsset.php
@@ -58,6 +58,7 @@ class AppAsset extends AssetBundle
* https://github.com/inuyaksa/jquery.nicescroll/issues/574
*/
//'humhub\assets\JqueryNiceScrollAsset',
+ 'humhub\assets\BluebirdAsset',
'humhub\assets\JqueryTimeAgoAsset',
'humhub\assets\JqueryKnobAsset',
'humhub\assets\JqueryWidgetAsset',
diff --git a/protected/humhub/assets/BluebirdAsset.php b/protected/humhub/assets/BluebirdAsset.php
new file mode 100644
index 0000000000..ebbb44e8a8
--- /dev/null
+++ b/protected/humhub/assets/BluebirdAsset.php
@@ -0,0 +1,38 @@
+ \yii\web\View::POS_BEGIN];
+
+ /**
+ * @inheritdoc
+ */
+ public $sourcePath = '@bower/bluebird';
+
+ /**
+ * @inheritdoc
+ */
+ public $js = ['js/browser/bluebird.min.js'];
+
+ /**
+ * @inheritdoc
+ */
+ public $css = [];
+
+}
diff --git a/protected/humhub/modules/content/controllers/ContentController.php b/protected/humhub/modules/content/controllers/ContentController.php
index 15ae3e90f2..06fcf336e3 100644
--- a/protected/humhub/modules/content/controllers/ContentController.php
+++ b/protected/humhub/modules/content/controllers/ContentController.php
@@ -34,6 +34,13 @@ class ContentController extends Controller
];
}
+ public function actionDeleteById()
+ {
+ Yii::$app->response->format = 'json';
+ $id = (int) Yii::$app->request->get('id');
+ Content::findOne($id);
+ }
+
/**
* Deletes a content object
*
@@ -44,22 +51,27 @@ class ContentController extends Controller
Yii::$app->response->format = 'json';
$this->forcePostRequest();
- $json = [
- 'success' => 'false'
- ];
$model = Yii::$app->request->get('model');
- $id = (int) Yii::$app->request->get('id');
+
+ //Due to backward compatibility we use the old delte mechanism in case a model parameter is provided
+ $id = (int) ($model != null) ? Yii::$app->request->get('id') : Yii::$app->request->post('id');
- $contentObj = Content::get($model, $id);
+ $contentObj = ($model != null) ? Content::get($model, $id) : Content::findOne($id);
- if ($contentObj !== null && $contentObj->content->canDelete() && $contentObj->delete()) {
+ if(!$contentObj->canDelete()) {
+ throw new HttpException(400, Yii::t('ContentModule.controllers_ContentController', 'Could not delete content: Access denied!'));
+ }
+
+ if ($contentObj !== null && $contentObj->delete()) {
$json = [
'success' => true,
'uniqueId' => $contentObj->getUniqueId(),
'model' => $model,
'pk' => $id
];
+ } else {
+ throw new HttpException(500, Yii::t('ContentModule.controllers_ContentController', 'Could not delete content!'));
}
return $json;
diff --git a/protected/humhub/modules/content/views/layouts/wallEntry.php b/protected/humhub/modules/content/views/layouts/wallEntry.php
index 9399dc6406..465c86b36e 100644
--- a/protected/humhub/modules/content/views/layouts/wallEntry.php
+++ b/protected/humhub/modules/content/views/layouts/wallEntry.php
@@ -14,7 +14,8 @@
$cssClass = ($entry->sticked) ? 'wall-entry sticked-entry' : 'wall-entry';
if ($mode != "activity") : ?>
-
+
diff --git a/protected/humhub/modules/content/widgets/views/deleteLink.php b/protected/humhub/modules/content/widgets/views/deleteLink.php
index b0a9febaa1..721d90140e 100644
--- a/protected/humhub/modules/content/widgets/views/deleteLink.php
+++ b/protected/humhub/modules/content/widgets/views/deleteLink.php
@@ -6,8 +6,11 @@ use yii\helpers\Url;
?>
+
+ = Yii::t('ContentModule.widgets_views_deleteLink', 'Delete') ?>
+
'modal_postdelete_' . $id,
'linkOutput' => 'a',
'title' => Yii::t('ContentModule.widgets_views_deleteLink', 'Confirm post deleting'),
@@ -17,6 +20,6 @@ use yii\helpers\Url;
'linkContent' => ' ' . Yii::t('ContentModule.widgets_views_deleteLink', 'Delete'),
'linkHref' => Url::to(['/content/content/delete', 'model' => $model, 'id' => $id]),
'confirmJS' => 'function(json) { $(".wall_"+json.uniqueId).remove(); }'
- ));
+ ));*/
?>
\ No newline at end of file
diff --git a/protected/humhub/modules/stream/actions/Stream.php b/protected/humhub/modules/stream/actions/Stream.php
index 9c29977192..69eb934097 100644
--- a/protected/humhub/modules/stream/actions/Stream.php
+++ b/protected/humhub/modules/stream/actions/Stream.php
@@ -20,7 +20,7 @@ use humhub\modules\user\models\User;
* @author luke
* @since 0.11
*/
-abstract class Stream extends \yii\base\Action
+abstract class Stream extends Action
{
/**
@@ -97,7 +97,6 @@ abstract class Stream extends \yii\base\Action
$this->user = Yii::$app->user->getIdentity();
}
-
// Read parameters
if (!Yii::$app->request->isConsoleRequest) {
$from = Yii::$app->getRequest()->get('from', 0);
@@ -191,16 +190,19 @@ abstract class Stream extends \yii\base\Action
$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 involed
if (in_array('entry_userinvoled', $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()]);
}
@@ -223,22 +225,21 @@ abstract class Stream extends \yii\base\Action
$this->init();
- $output['contents'] = [];
+ $output['content'] = [];
foreach ($this->activeQuery->all() as $content) {
- $output['contents'][$content->id] = $this->getContentResultEntry($content);
+ $output['content'][$content->id] = $this->getContentResultEntry($content);
}
- $output['total'] = count($output['contents']);
+ $output['total'] = count($output['content']);
$output['is_last'] = ($output['total'] < $this->activeQuery->limit);
// BEGIN: TEMPORARY until JS Rewrite
$output['output'] = '';
- foreach ($output['contents'] as $i => $c) {
+ foreach ($output['content'] as $i => $c) {
$output['output'] .= $c['output'];
- $output['lastEntryId'] = $i;
- $output['entryIds'][] = $i;
+ $output['lastContentId'] = $i;
+ $output['contentIds'][] = $i;
}
$output['counter'] = $output['total'];
- $output['counter'] = $output['total'];
// END: Temporary
return $output;
diff --git a/protected/humhub/modules/stream/widgets/StreamViewer.php b/protected/humhub/modules/stream/widgets/StreamViewer.php
index 9a9c318fb6..b0307739b9 100644
--- a/protected/humhub/modules/stream/widgets/StreamViewer.php
+++ b/protected/humhub/modules/stream/widgets/StreamViewer.php
@@ -112,10 +112,6 @@ class StreamViewer extends \yii\base\Widget
{
$params = [
$this->streamAction,
- 'limit' => '-limit-',
- 'filters' => '-filter-',
- 'sort' => '-sort-',
- 'from' => '-from-',
'mode' => \humhub\modules\stream\actions\Stream::MODE_NORMAL
];
diff --git a/protected/humhub/modules/stream/widgets/views/stream.php b/protected/humhub/modules/stream/widgets/views/stream.php
index d5bae10949..d81a3b8cb1 100644
--- a/protected/humhub/modules/stream/widgets/views/stream.php
+++ b/protected/humhub/modules/stream/widgets/views/stream.php
@@ -1,24 +1,11 @@
registerJs('var streamUrl="' . $streamUrl . '"', View::POS_BEGIN);
-
-$jsLoadWall = "s = new Stream('#wallStream');\n";
-$wallEntryId = (int) Yii::$app->request->getQueryParam('wallEntryId');
-if ($wallEntryId != "") {
- $jsLoadWall .= "s.showItem(" . $wallEntryId . ");\n";
-} else {
- $jsLoadWall .= "s.showStream();\n";
-}
-$jsLoadWall .= "currentStream = s;\n";
-$jsLoadWall .= "mainStream = s;\n";
-$jsLoadWall .= "$('#btn-load-more').click(function() { currentStream.loadMore(); })\n";
-$this->registerJs($jsLoadWall, View::POS_READY);
+$contentId = (int) Yii::$app->request->getQueryParam('wallEntryId');
+$contentIdData = ($contentId != "") ? 'data-stream-contentid="'.$contentId.'"' : '' ;
?>
+
context->showFilters) { ?>
-
@@ -26,11 +13,12 @@ $this->registerJs($jsLoadWall, View::POS_READY);
class="caret">
-
@@ -46,16 +34,20 @@ $this->registerJs($jsLoadWall, View::POS_READY);
-
-
+
+
+ data-content-base="humhub.modules.stream.Stream"
+ data-content-delete-url="= Url::to(['/content/content/delete']) ?>">
+
-
+
'streamLoader']); ?>
-
-
+
@@ -63,9 +55,8 @@ $this->registerJs($jsLoadWall, View::POS_READY);
-
-
+
@@ -77,23 +68,10 @@ $this->registerJs($jsLoadWall, View::POS_READY);
-
-
-
-
+
\ No newline at end of file