diff --git a/.gitignore b/.gitignore index 237f0f44e8..2e42105ad8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ # ignore all apps except core ones /apps*/* +!/apps/comments !/apps/dav !/apps/files !/apps/federation diff --git a/apps/comments/appinfo/app.php b/apps/comments/appinfo/app.php new file mode 100644 index 0000000000..c6f36567c5 --- /dev/null +++ b/apps/comments/appinfo/app.php @@ -0,0 +1,34 @@ + + * + * @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 + * + */ + +$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 0000000000..550c79448c --- /dev/null +++ b/apps/comments/appinfo/info.xml @@ -0,0 +1,16 @@ + + + comments + Comments + Files app plugin to add comments to files + AGPL + Arthur Shiwon, Vincent Petry + + 0.1 + + + + + user-comments + + diff --git a/apps/comments/js/app.js b/apps/comments/js/app.js new file mode 100644 index 0000000000..547059393a --- /dev/null +++ b/apps/comments/js/app.js @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2016 Vincent Petry + * + * 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 0000000000..61b5adb7da --- /dev/null +++ b/apps/comments/js/commentcollection.js @@ -0,0 +1,82 @@ +/* + * 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) { + + function filterFunction(model, term) { + return model.get('name').substr(0, term.length) === term; + } + + /** + * @class OCA.Comments.CommentsCollection + * @classdesc + * + * Collection of comments assigned to a file + * + */ + var CommentsCollection = OC.Backbone.Collection.extend( + /** @lends OCA.Comments.CommentsCollection.prototype */ { + + sync: OC.Backbone.davSync, + + model: OCA.Comments.CommentModel, + + _objectType: 'files', + _objectId: null, + + _endReached: false, + _currentIndex: 0, + + 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; + }, + + /** + * Fetch the next set of results + */ + fetchNext: function() { + if (!this.hasMoreResults()) { + return null; + } + if (this._currentIndex === 0) { + return this.fetch(); + } + return this.fetch({remove: false}); + }, + + reset: function() { + this._currentIndex = 0; + OC.Backbone.Collection.prototype.reset.apply(this, arguments); + } + }); + + OCA.Comments.CommentsCollection = CommentsCollection; +})(OC, OCA); + diff --git a/apps/comments/js/commentmodel.js b/apps/comments/js/commentmodel.js new file mode 100644 index 0000000000..8771bd2d0f --- /dev/null +++ b/apps/comments/js/commentmodel.js @@ -0,0 +1,47 @@ +/* + * 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: { + // TODO + }, + + 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 0000000000..cccb400dd6 --- /dev/null +++ b/apps/comments/js/commentstabview.js @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2016 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +(function() { + var TEMPLATE = + '
' + + '
' + + ' ' + + ' ' + + '
' + + '
    ' + + '
' + + '
' + + '' + + /* + '' + + */ + ''; + + var COMMENT_TEMPLATE = + '
  • ' + + '
    ' + + '
    ' + + ' {{actorDisplayName}}' + + ' {{creationDateTime}}' + + '
    ' + + '
    {{message}}
    ' + + '
  • '; + + /** + * @memberof OCA.Comments + */ + var CommentsTabView = OCA.Files.DetailTabView.extend( + /** @lends OCA.Comments.CommentsTabView.prototype */ { + id: 'commentsTabView', + className: 'tab commentsTabView', + + events: { + 'submit .newCommentForm': '_onSubmitComment' + }, + + initialize: function() { + OCA.Files.DetailTabView.prototype.initialize.apply(this, arguments); + this.collection = new OCA.Comments.CommentsCollection(); + this.collection.on('request', this._onRequest, this); + this.collection.on('sync', this._onEndRequest, this); + this.collection.on('add', this._onAddModel, this); + // TODO: error handling + _.bindAll(this, '_onSubmitComment'); + }, + + template: function(params) { + if (!this._template) { + this._template = Handlebars.compile(TEMPLATE); + } + return this._template(_.extend({ + submitText: t('comments', 'Submit comment') + }, params)); + }, + + commentTemplate: function(params) { + if (!this._commentTemplate) { + this._commentTemplate = Handlebars.compile(COMMENT_TEMPLATE); + } + return this._commentTemplate(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.delegateEvents(); + }, + + _formatItem: function(commentModel) { + // TODO: format + return commentModel.attributes; + }, + + _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); + } + }, + + nextPage: function() { + if (this._loading || !this.collection.hasMoreResults()) { + return; + } + + this.collection.fetchNext(); + }, + + _onClickShowMoreVersions: function(ev) { + ev.preventDefault(); + this.nextPage(); + }, + + _onSubmitComment: function(e) { + var $textArea = $(e.target).find('textarea'); + e.preventDefault(); + this.collection.create({ + actorId: OC.currentUser, + // FIXME: how to get current user's display name ? + actorDisplayName: OC.currentUser, + actorType: 'users', + verb: 'comment', + message: $textArea.val() + }, {at: 0}); + + // TODO: spinner/disable field? + $textArea.val(''); + return false; + } + }); + + OCA.Comments.CommentsTabView = CommentsTabView; +})(); + diff --git a/apps/comments/js/filesplugin.js b/apps/comments/js/filesplugin.js new file mode 100644 index 0000000000..c8d91e0ede --- /dev/null +++ b/apps/comments/js/filesplugin.js @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016 Vincent Petry + * + * 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/core/shipped.json b/core/shipped.json index 5dd8700bf1..5f99532662 100644 --- a/core/shipped.json +++ b/core/shipped.json @@ -3,6 +3,7 @@ "activity", "admin_audit", "encryption", + "comments", "dav", "enterprise_key", "external",