summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorThomas Müller <thomas.mueller@tmit.eu>2016-02-03 09:03:43 +0100
committerThomas Müller <thomas.mueller@tmit.eu>2016-02-03 09:03:43 +0100
commitd4b356e0e9e1de237c9a86c8a576291a3508aec4 (patch)
tree08abafedf15a509a68f3b12c279f5746cb78e0fe /apps
parent77942ad38afb982c3b7efa841a9bb0b46a0c039a (diff)
parent5e08f1df78652e7a16de00041d376f55a4d2009e (diff)
downloadnextcloud-server-d4b356e0e9e1de237c9a86c8a576291a3508aec4.tar.gz
nextcloud-server-d4b356e0e9e1de237c9a86c8a576291a3508aec4.zip
Merge pull request #21961 from owncloud/comments-gui
Comments GUI in files sidebar
Diffstat (limited to 'apps')
-rw-r--r--apps/comments/appinfo/app.php34
-rw-r--r--apps/comments/appinfo/info.xml16
-rw-r--r--apps/comments/css/comments.css51
-rw-r--r--apps/comments/js/app.js20
-rw-r--r--apps/comments/js/commentcollection.js110
-rw-r--r--apps/comments/js/commentmodel.js48
-rw-r--r--apps/comments/js/commentstabview.js236
-rw-r--r--apps/comments/js/filesplugin.js41
-rw-r--r--apps/comments/tests/js/commentscollectionSpec.js104
-rw-r--r--apps/comments/tests/js/commentstabviewSpec.js198
10 files changed, 858 insertions, 0 deletions
diff --git a/apps/comments/appinfo/app.php b/apps/comments/appinfo/app.php
new file mode 100644
index 00000000000..c6f36567c51
--- /dev/null
+++ b/apps/comments/appinfo/app.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+$eventDispatcher = \OC::$server->getEventDispatcher();
+$eventDispatcher->addListener(
+ 'OCA\Files::loadAdditionalScripts',
+ function() {
+ \OCP\Util::addScript('oc-backbone-webdav');
+ \OCP\Util::addScript('comments', 'app');
+ \OCP\Util::addScript('comments', 'commentmodel');
+ \OCP\Util::addScript('comments', 'commentcollection');
+ \OCP\Util::addScript('comments', 'commentstabview');
+ \OCP\Util::addScript('comments', 'filesplugin');
+ \OCP\Util::addStyle('comments', 'comments');
+ }
+);
diff --git a/apps/comments/appinfo/info.xml b/apps/comments/appinfo/info.xml
new file mode 100644
index 00000000000..550c79448cf
--- /dev/null
+++ b/apps/comments/appinfo/info.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<info>
+ <id>comments</id>
+ <name>Comments</name>
+ <description>Files app plugin to add comments to files</description>
+ <licence>AGPL</licence>
+ <author>Arthur Shiwon, Vincent Petry</author>
+ <default_enable/>
+ <version>0.1</version>
+ <dependencies>
+ <owncloud min-version="9.0" max-version="9.0" />
+ </dependencies>
+ <documentation>
+ <user>user-comments</user>
+ </documentation>
+</info>
diff --git a/apps/comments/css/comments.css b/apps/comments/css/comments.css
new file mode 100644
index 00000000000..c1624dcc57b
--- /dev/null
+++ b/apps/comments/css/comments.css
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+#commentsTabView .newCommentForm {
+ margin-bottom: 20px;
+}
+
+#commentsTabView .newCommentForm .message {
+ width: 90%;
+ resize: none;
+}
+
+#commentsTabView .newCommentForm .submitLoading {
+ background-position: left;
+}
+
+#commentsTabView .comment {
+ margin-bottom: 30px;
+}
+
+#commentsTabView .comment .avatar {
+ width: 28px;
+ height: 28px;
+ line-height: 28px;
+}
+
+#commentsTabView .authorRow>div {
+ display: inline-block;
+ vertical-align: middle;
+}
+
+#commentsTabView .comment .authorRow {
+ margin-bottom: 5px;
+ position: relative;
+}
+
+#commentsTabView .comment .author {
+ font-weight: bold;
+}
+
+#commentsTabView .comment .date {
+ position: absolute;
+ right: 0;
+}
diff --git a/apps/comments/js/app.js b/apps/comments/js/app.js
new file mode 100644
index 00000000000..547059393a5
--- /dev/null
+++ b/apps/comments/js/app.js
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2016 Vincent Petry <pvince81@owncloud.com>
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function() {
+ if (!OCA.Comments) {
+ /**
+ * @namespace
+ */
+ OCA.Comments = {};
+ }
+
+})();
+
diff --git a/apps/comments/js/commentcollection.js b/apps/comments/js/commentcollection.js
new file mode 100644
index 00000000000..d10e5e00865
--- /dev/null
+++ b/apps/comments/js/commentcollection.js
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2016
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function(OC, OCA) {
+
+ var NS_OWNCLOUD = 'http://owncloud.org/ns';
+
+ /**
+ * @class OCA.Comments.CommentCollection
+ * @classdesc
+ *
+ * Collection of comments assigned to a file
+ *
+ */
+ var CommentCollection = OC.Backbone.Collection.extend(
+ /** @lends OCA.Comments.CommentCollection.prototype */ {
+
+ sync: OC.Backbone.davSync,
+
+ model: OCA.Comments.CommentModel,
+
+ _objectType: 'files',
+ _objectId: null,
+
+ _endReached: false,
+ _limit : 20,
+
+ initialize: function(models, options) {
+ options = options || {};
+ if (options.objectType) {
+ this._objectType = options.objectType;
+ }
+ if (options.objectId) {
+ this._objectId = options.objectId;
+ }
+ },
+
+ url: function() {
+ return OC.linkToRemote('dav') + '/comments/' +
+ encodeURIComponent(this._objectType) + '/' +
+ encodeURIComponent(this._objectId) + '/';
+ },
+
+ setObjectId: function(objectId) {
+ this._objectId = objectId;
+ },
+
+ hasMoreResults: function() {
+ return !this._endReached;
+ },
+
+ reset: function() {
+ this._endReached = false;
+ return OC.Backbone.Collection.prototype.reset.apply(this, arguments);
+ },
+
+ /**
+ * Fetch the next set of results
+ */
+ fetchNext: function(options) {
+ var self = this;
+ if (!this.hasMoreResults()) {
+ return null;
+ }
+
+ var body = '<?xml version="1.0" encoding="utf-8" ?>\n' +
+ '<oc:filter-comments xmlns:D="DAV:" xmlns:oc="http://owncloud.org/ns">\n' +
+ // load one more so we know there is more
+ ' <oc:limit>' + (this._limit + 1) + '</oc:limit>\n' +
+ ' <oc:offset>' + this.length + '</oc:offset>\n' +
+ '</oc:filter-comments>\n';
+
+ options = options || {};
+ var success = options.success;
+ options = _.extend({
+ remove: false,
+ data: body,
+ davProperties: CommentCollection.prototype.model.prototype.davProperties,
+ success: function(resp) {
+ if (resp.length <= self._limit) {
+ // no new entries, end reached
+ self._endReached = true;
+ } else {
+ // remove last entry, for next page load
+ resp = _.initial(resp);
+ }
+ if (!self.set(resp, options)) {
+ return false;
+ }
+ if (success) {
+ success.apply(null, arguments);
+ }
+ self.trigger('sync', 'REPORT', self, options);
+ }
+ }, options);
+
+ return this.sync('REPORT', this, options);
+ }
+ });
+
+ OCA.Comments.CommentCollection = CommentCollection;
+})(OC, OCA);
+
diff --git a/apps/comments/js/commentmodel.js b/apps/comments/js/commentmodel.js
new file mode 100644
index 00000000000..b945f71fdd2
--- /dev/null
+++ b/apps/comments/js/commentmodel.js
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function(OC, OCA) {
+ var NS_OWNCLOUD = 'http://owncloud.org/ns';
+ /**
+ * @class OCA.Comments.CommentModel
+ * @classdesc
+ *
+ * Comment
+ *
+ */
+ var CommentModel = OC.Backbone.Model.extend(
+ /** @lends OCA.Comments.CommentModel.prototype */ {
+ sync: OC.Backbone.davSync,
+
+ defaults: {
+ actorType: 'users',
+ objectType: 'files'
+ },
+
+ davProperties: {
+ 'id': '{' + NS_OWNCLOUD + '}id',
+ 'message': '{' + NS_OWNCLOUD + '}message',
+ 'actorType': '{' + NS_OWNCLOUD + '}actorType',
+ 'actorId': '{' + NS_OWNCLOUD + '}actorId',
+ 'actorDisplayName': '{' + NS_OWNCLOUD + '}actorDisplayName',
+ 'creationDateTime': '{' + NS_OWNCLOUD + '}creationDateTime',
+ 'objectType': '{' + NS_OWNCLOUD + '}objectType',
+ 'objectId': '{' + NS_OWNCLOUD + '}objectId'
+ },
+
+ parse: function(data) {
+ // TODO: parse non-string values
+ return data;
+ }
+ });
+
+ OCA.Comments.CommentModel = CommentModel;
+})(OC, OCA);
+
diff --git a/apps/comments/js/commentstabview.js b/apps/comments/js/commentstabview.js
new file mode 100644
index 00000000000..463ac2d76ef
--- /dev/null
+++ b/apps/comments/js/commentstabview.js
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2016
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function(OC, OCA) {
+ var TEMPLATE =
+ '<div class="newCommentRow comment">' +
+ ' <div class="authorRow">' +
+ ' {{#if avatarEnabled}}' +
+ ' <div class="avatar" data-username="{{userId}}"></div>' +
+ ' {{/if}}' +
+ ' <div class="author">{{userDisplayName}}</div>' +
+ ' </div>' +
+ ' <form class="newCommentForm">' +
+ ' <textarea class="message" placeholder="{{newMessagePlaceholder}}"></textarea>' +
+ ' <input class="submit" type="submit" value="{{submitText}}" />' +
+ ' <div class="submitLoading icon-loading-small hidden"></div>'+
+ ' </form>' +
+ ' <ul class="comments">' +
+ ' </ul>' +
+ '</div>' +
+ '<div class="empty hidden">{{emptyResultLabel}}</div>' +
+ '<input type="button" class="showMore hidden" value="{{moreLabel}}"' +
+ ' name="show-more" id="show-more" />' +
+ '<div class="loading hidden" style="height: 50px"></div>';
+
+ var COMMENT_TEMPLATE =
+ '<li class="comment">' +
+ ' <div class="authorRow">' +
+ ' {{#if avatarEnabled}}' +
+ ' <div class="avatar" data-username="{{actorId}}"> </div>' +
+ ' {{/if}}' +
+ ' <div class="author">{{actorDisplayName}}</div>' +
+ ' <div class="date has-tooltip" title="{{altDate}}">{{date}}</div>' +
+ ' </div>' +
+ ' <div class="message">{{{formattedMessage}}}</div>' +
+ '</li>';
+
+ /**
+ * @memberof OCA.Comments
+ */
+ var CommentsTabView = OCA.Files.DetailTabView.extend(
+ /** @lends OCA.Comments.CommentsTabView.prototype */ {
+ id: 'commentsTabView',
+ className: 'tab commentsTabView',
+
+ events: {
+ 'submit .newCommentForm': '_onSubmitComment',
+ 'click .showMore': '_onClickShowMore'
+ },
+
+ initialize: function() {
+ OCA.Files.DetailTabView.prototype.initialize.apply(this, arguments);
+ this.collection = new OCA.Comments.CommentCollection();
+ this.collection.on('request', this._onRequest, this);
+ this.collection.on('sync', this._onEndRequest, this);
+ this.collection.on('add', this._onAddModel, this);
+
+ this._avatarsEnabled = !!OC.config.enable_avatars;
+
+ // TODO: error handling
+ _.bindAll(this, '_onSubmitComment');
+ },
+
+ template: function(params) {
+ if (!this._template) {
+ this._template = Handlebars.compile(TEMPLATE);
+ }
+ var currentUser = OC.getCurrentUser();
+ return this._template(_.extend({
+ avatarEnabled: this._avatarsEnabled,
+ userId: currentUser.uid,
+ userDisplayName: currentUser.displayName,
+ newMessagePlaceholder: t('comments', 'Type in a new comment...'),
+ submitText: t('comments', 'Post')
+ }, params));
+ },
+
+ commentTemplate: function(params) {
+ if (!this._commentTemplate) {
+ this._commentTemplate = Handlebars.compile(COMMENT_TEMPLATE);
+ }
+ return this._commentTemplate(_.extend({
+ avatarEnabled: this._avatarsEnabled
+ }, params));
+ },
+
+ getLabel: function() {
+ return t('comments', 'Comments');
+ },
+
+ setFileInfo: function(fileInfo) {
+ if (fileInfo) {
+ this.render();
+ this.collection.setObjectId(fileInfo.id);
+ // reset to first page
+ this.collection.reset([], {silent: true});
+ this.nextPage();
+ } else {
+ this.render();
+ this.collection.reset();
+ }
+ },
+
+ render: function() {
+ this.$el.html(this.template({
+ emptyResultLabel: t('comments', 'No other comments available'),
+ moreLabel: t('comments', 'More comments...')
+ }));
+ this.$el.find('.has-tooltip').tooltip();
+ this.$container = this.$el.find('ul.comments');
+ this.$el.find('.avatar').avatar(OC.getCurrentUser().uid, 28);
+ this.delegateEvents();
+ },
+
+ _formatItem: function(commentModel) {
+ var timestamp = new Date(commentModel.get('creationDateTime')).getTime();
+ var data = _.extend({
+ date: OC.Util.relativeModifiedDate(timestamp),
+ altDate: OC.Util.formatDate(timestamp),
+ formattedMessage: this._formatMessage(commentModel.get('message'))
+ }, commentModel.attributes);
+ return data;
+ },
+
+ _toggleLoading: function(state) {
+ this._loading = state;
+ this.$el.find('.loading').toggleClass('hidden', !state);
+ },
+
+ _onRequest: function() {
+ this._toggleLoading(true);
+ this.$el.find('.showMore').addClass('hidden');
+ },
+
+ _onEndRequest: function() {
+ this._toggleLoading(false);
+ this.$el.find('.empty').toggleClass('hidden', !!this.collection.length);
+ this.$el.find('.showMore').toggleClass('hidden', !this.collection.hasMoreResults());
+ },
+
+ _onAddModel: function(model, collection, options) {
+ var $el = $(this.commentTemplate(this._formatItem(model)));
+ if (!_.isUndefined(options.at) && collection.length > 1) {
+ this.$container.find('li').eq(options.at).before($el);
+ } else {
+ this.$container.append($el);
+ }
+
+ this._postRenderItem($el);
+ },
+
+ _postRenderItem: function($el) {
+ $el.find('.has-tooltip').tooltip();
+ if(this._avatarsEnabled) {
+ $el.find('.avatar').each(function() {
+ var $this = $(this);
+ $this.avatar($this.attr('data-username'), 28);
+ });
+ }
+ },
+
+ /**
+ * Convert a message to be displayed in HTML,
+ * converts newlines to <br> tags.
+ */
+ _formatMessage: function(message) {
+ return escapeHTML(message).replace(/\n/g, '<br/>');
+ },
+
+ nextPage: function() {
+ if (this._loading || !this.collection.hasMoreResults()) {
+ return;
+ }
+
+ this.collection.fetchNext();
+ },
+
+ _onClickShowMore: function(ev) {
+ ev.preventDefault();
+ this.nextPage();
+ },
+
+ _onSubmitComment: function(e) {
+ var $form = $(e.target);
+ var currentUser = OC.getCurrentUser();
+ var $submit = $form.find('.submit');
+ var $loading = $form.find('.submitLoading');
+ var $textArea = $form.find('textarea');
+ var message = $textArea.val().trim();
+ e.preventDefault();
+
+ if (!message.length) {
+ return;
+ }
+
+ $textArea.prop('disabled', true);
+ $submit.addClass('hidden');
+ $loading.removeClass('hidden');
+
+ this.collection.create({
+ actorId: currentUser.uid,
+ actorDisplayName: currentUser.displayName,
+ actorType: 'users',
+ verb: 'comment',
+ message: $textArea.val(),
+ creationDateTime: (new Date()).getTime()
+ }, {
+ at: 0,
+ success: function() {
+ $submit.removeClass('hidden');
+ $loading.addClass('hidden');
+ $textArea.val('').prop('disabled', false);
+ },
+ error: function(msg) {
+ $submit.removeClass('hidden');
+ $loading.addClass('hidden');
+ $textArea.prop('disabled', false);
+
+ OC.Notification.showTemporary(msg);
+ }
+ });
+
+ return false;
+ }
+ });
+
+ OCA.Comments.CommentsTabView = CommentsTabView;
+})(OC, OCA);
+
diff --git a/apps/comments/js/filesplugin.js b/apps/comments/js/filesplugin.js
new file mode 100644
index 00000000000..c8d91e0ede3
--- /dev/null
+++ b/apps/comments/js/filesplugin.js
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016 Vincent Petry <pvince81@owncloud.com>
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function() {
+ OCA.Comments = _.extend({}, OCA.Comments);
+ if (!OCA.Comments) {
+ /**
+ * @namespace
+ */
+ OCA.Comments = {};
+ }
+
+ /**
+ * @namespace
+ */
+ OCA.Comments.FilesPlugin = {
+ allowedLists: [
+ 'files',
+ 'favorites'
+ ],
+
+ attach: function(fileList) {
+ if (this.allowedLists.indexOf(fileList.id) < 0) {
+ return;
+ }
+
+ fileList.registerTabView(new OCA.Comments.CommentsTabView('commentsTabView'));
+ }
+ };
+
+})();
+
+OC.Plugins.register('OCA.Files.FileList', OCA.Comments.FilesPlugin);
+
diff --git a/apps/comments/tests/js/commentscollectionSpec.js b/apps/comments/tests/js/commentscollectionSpec.js
new file mode 100644
index 00000000000..0dc68cc167c
--- /dev/null
+++ b/apps/comments/tests/js/commentscollectionSpec.js
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2016
+ *
+ * This file is licensed under the Affero General Public License comment 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+describe('OCA.Comments.CommentCollection', function() {
+ var CommentCollection = OCA.Comments.CommentCollection;
+ var collection, syncStub;
+ var comment1, comment2, comment3;
+
+ beforeEach(function() {
+ syncStub = sinon.stub(CommentCollection.prototype, 'sync');
+ collection = new CommentCollection();
+ collection.setObjectId(5);
+
+ comment1 = {
+ id: 1,
+ actorType: 'users',
+ actorId: 'user1',
+ actorDisplayName: 'User One',
+ objectType: 'files',
+ objectId: 5,
+ message: 'First',
+ creationDateTime: Date.UTC(2016, 1, 3, 10, 5, 0)
+ };
+ comment2 = {
+ id: 2,
+ actorType: 'users',
+ actorId: 'user2',
+ actorDisplayName: 'User Two',
+ objectType: 'files',
+ objectId: 5,
+ message: 'Second\nNewline',
+ creationDateTime: Date.UTC(2016, 1, 3, 10, 0, 0)
+ };
+ comment3 = {
+ id: 3,
+ actorType: 'users',
+ actorId: 'user3',
+ actorDisplayName: 'User Three',
+ objectType: 'files',
+ objectId: 5,
+ message: 'Third',
+ creationDateTime: Date.UTC(2016, 1, 3, 5, 0, 0)
+ };
+ });
+ afterEach(function() {
+ syncStub.restore();
+ });
+
+ it('fetches the next page', function() {
+ collection._limit = 2;
+ collection.fetchNext();
+
+ expect(syncStub.calledOnce).toEqual(true);
+ expect(syncStub.lastCall.args[0]).toEqual('REPORT');
+ var options = syncStub.lastCall.args[2];
+ expect(options.remove).toEqual(false);
+
+ var parser = new DOMParser();
+ var doc = parser.parseFromString(options.data, "application/xml");
+ expect(doc.getElementsByTagNameNS('http://owncloud.org/ns', 'limit')[0].textContent).toEqual('3');
+ expect(doc.getElementsByTagNameNS('http://owncloud.org/ns', 'offset')[0].textContent).toEqual('0');
+
+ syncStub.yieldTo('success', [comment1, comment2, comment3]);
+
+ expect(collection.length).toEqual(2);
+ expect(collection.hasMoreResults()).toEqual(true);
+
+ collection.fetchNext();
+
+ expect(syncStub.calledTwice).toEqual(true);
+ options = syncStub.lastCall.args[2];
+ doc = parser.parseFromString(options.data, "application/xml");
+ expect(doc.getElementsByTagNameNS('http://owncloud.org/ns', 'limit')[0].textContent).toEqual('3');
+ expect(doc.getElementsByTagNameNS('http://owncloud.org/ns', 'offset')[0].textContent).toEqual('2');
+
+ syncStub.yieldTo('success', [comment3]);
+
+ expect(collection.length).toEqual(3);
+ expect(collection.hasMoreResults()).toEqual(false);
+
+ collection.fetchNext();
+
+ // no further requests
+ expect(syncStub.calledTwice).toEqual(true);
+ });
+ it('resets page counted when calling reset', function() {
+ collection.fetchNext();
+
+ syncStub.yieldTo('success', [comment1]);
+
+ expect(collection.hasMoreResults()).toEqual(false);
+
+ collection.reset();
+
+ expect(collection.hasMoreResults()).toEqual(true);
+ });
+});
+
diff --git a/apps/comments/tests/js/commentstabviewSpec.js b/apps/comments/tests/js/commentstabviewSpec.js
new file mode 100644
index 00000000000..0fb5eec0653
--- /dev/null
+++ b/apps/comments/tests/js/commentstabviewSpec.js
@@ -0,0 +1,198 @@
+/**
+* ownCloud
+*
+* @author Vincent Petry
+* @copyright 2016 Vincent Petry <pvince81@owncloud.com>
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* comment 3 of the License, or any later comment.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Affero General Public
+* License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+describe('OCA.Comments.CommentsTabView tests', function() {
+ var view, fileInfoModel;
+ var fetchStub;
+ var testComments;
+ var clock;
+
+ beforeEach(function() {
+ clock = sinon.useFakeTimers(Date.UTC(2016, 1, 3, 10, 5, 9));
+ fetchStub = sinon.stub(OCA.Comments.CommentCollection.prototype, 'fetchNext');
+ view = new OCA.Comments.CommentsTabView();
+ fileInfoModel = new OCA.Files.FileInfoModel({
+ id: 5,
+ name: 'One.txt',
+ mimetype: 'text/plain',
+ permissions: 31,
+ path: '/subdir',
+ size: 123456789,
+ etag: 'abcdefg',
+ mtime: Date.UTC(2016, 1, 0, 0, 0, 0)
+ });
+ view.render();
+ var comment1 = new OCA.Comments.CommentModel({
+ id: 1,
+ actorType: 'users',
+ actorId: 'user1',
+ actorDisplayName: 'User One',
+ objectType: 'files',
+ objectId: 5,
+ message: 'First',
+ creationDateTime: Date.UTC(2016, 1, 3, 10, 5, 0)
+ });
+ var comment2 = new OCA.Comments.CommentModel({
+ id: 2,
+ actorType: 'users',
+ actorId: 'user2',
+ actorDisplayName: 'User Two',
+ objectType: 'files',
+ objectId: 5,
+ message: 'Second\nNewline',
+ creationDateTime: Date.UTC(2016, 1, 3, 10, 0, 0)
+ });
+
+ testComments = [comment1, comment2];
+ });
+ afterEach(function() {
+ view.remove();
+ view = undefined;
+ fetchStub.restore();
+ clock.restore();
+ });
+ describe('rendering', function() {
+ it('reloads matching comments when setting file info model', function() {
+ view.setFileInfo(fileInfoModel);
+ expect(fetchStub.calledOnce).toEqual(true);
+ });
+
+ it('renders loading icon while fetching comments', function() {
+ view.setFileInfo(fileInfoModel);
+ view.collection.trigger('request');
+
+ expect(view.$el.find('.loading').length).toEqual(1);
+ expect(view.$el.find('.comments li').length).toEqual(0);
+ });
+
+ it('renders comments', function() {
+
+ view.setFileInfo(fileInfoModel);
+ view.collection.set(testComments);
+
+ var $comments = view.$el.find('.comments>li');
+ expect($comments.length).toEqual(2);
+ var $item = $comments.eq(0);
+ expect($item.find('.author').text()).toEqual('User One');
+ expect($item.find('.date').text()).toEqual('seconds ago');
+ expect($item.find('.message').text()).toEqual('First');
+
+ $item = $comments.eq(1);
+ expect($item.find('.author').text()).toEqual('User Two');
+ expect($item.find('.date').text()).toEqual('5 minutes ago');
+ expect($item.find('.message').html()).toEqual('Second<br>Newline');
+ });
+ });
+ describe('more comments', function() {
+ var hasMoreResultsStub;
+
+ beforeEach(function() {
+ view.collection.set(testComments);
+ hasMoreResultsStub = sinon.stub(OCA.Comments.CommentCollection.prototype, 'hasMoreResults');
+ });
+ afterEach(function() {
+ hasMoreResultsStub.restore();
+ });
+
+ it('shows "More comments" button when more comments are available', function() {
+ hasMoreResultsStub.returns(true);
+ view.collection.trigger('sync');
+
+ expect(view.$el.find('.showMore').hasClass('hidden')).toEqual(false);
+ });
+ it('does not show "More comments" button when more comments are available', function() {
+ hasMoreResultsStub.returns(false);
+ view.collection.trigger('sync');
+
+ expect(view.$el.find('.showMore').hasClass('hidden')).toEqual(true);
+ });
+ it('fetches and appends the next page when clicking the "More" button', function() {
+ hasMoreResultsStub.returns(true);
+
+ expect(fetchStub.notCalled).toEqual(true);
+
+ view.$el.find('.showMore').click();
+
+ expect(fetchStub.calledOnce).toEqual(true);
+ });
+ it('appends comment to the list when added to collection', function() {
+ var comment3 = new OCA.Comments.CommentModel({
+ id: 3,
+ actorType: 'users',
+ actorId: 'user3',
+ actorDisplayName: 'User Three',
+ objectType: 'files',
+ objectId: 5,
+ message: 'Third',
+ creationDateTime: Date.UTC(2016, 1, 3, 5, 0, 0)
+ });
+
+ view.collection.add(comment3);
+
+ expect(view.$el.find('.comments>li').length).toEqual(3);
+
+ var $item = view.$el.find('.comments>li').eq(2);
+ expect($item.find('.author').text()).toEqual('User Three');
+ expect($item.find('.date').text()).toEqual('5 hours ago');
+ expect($item.find('.message').html()).toEqual('Third');
+ });
+ });
+ describe('posting comments', function() {
+ var createStub;
+ var currentUserStub;
+
+ beforeEach(function() {
+ view.collection.set(testComments);
+ createStub = sinon.stub(OCA.Comments.CommentCollection.prototype, 'create');
+ currentUserStub = sinon.stub(OC, 'getCurrentUser');
+ currentUserStub.returns({
+ uid: 'testuser',
+ displayName: 'Test User'
+ });
+ });
+ afterEach(function() {
+ createStub.restore();
+ currentUserStub.restore();
+ });
+
+ it('creates a new comment when clicking post button', function() {
+ view.$el.find('.message').val('New message');
+ view.$el.find('form').submit();
+
+ expect(createStub.calledOnce).toEqual(true);
+ expect(createStub.lastCall.args[0]).toEqual({
+ actorId: 'testuser',
+ actorDisplayName: 'Test User',
+ actorType: 'users',
+ verb: 'comment',
+ message: 'New message',
+ creationDateTime: Date.UTC(2016, 1, 3, 10, 5, 9)
+ });
+ });
+ it('does not create a comment if the field is empty', function() {
+ view.$el.find('.message').val(' ');
+ view.$el.find('form').submit();
+
+ expect(createStub.notCalled).toEqual(true);
+ });
+
+ });
+});