diff --git a/media/css/seahub.css b/media/css/seahub.css index 1b68377f9b..d9deefa0ea 100644 --- a/media/css/seahub.css +++ b/media/css/seahub.css @@ -9,7 +9,7 @@ * tags ..................reset styles of tags * * Helper - * common class .......... common class + * common class ..........common class * * Widget * @@ -1226,28 +1226,19 @@ textarea:-moz-placeholder {/* for FF */ } /**** discussions/comments ****/ .msg { - padding:10px 0; -} -.msg-avatar { - float:left; - margin-left:10px; + padding:10px; } .msg-body { - margin-left:54px; -} -.msg-header { -} -.msg-ops { - float:right; - margin-right:10px; + margin-left:44px; } .msg-op { - cursor:pointer; + color:#aaa; + font-size:18px; } .msg-username { -} -.msg-content p:first-child { - margin-top:0px; + display:inline-block; + vertical-align:bottom; + max-width:30%; } .msg-time { font-size:11px; @@ -1255,9 +1246,8 @@ textarea:-moz-placeholder {/* for FF */ margin-left:5px; } .msg-form { + padding:10px; background:#f2f2f2; - position:relative; - padding-top:10px; border-top: 1px solid #c9c9c9; } .msg-input { @@ -1268,7 +1258,6 @@ textarea:-moz-placeholder {/* for FF */ color:#fff; background:#4abb49; border:1px solid #5cb25b; - margin:0px 0 8px; } .msg-form .submit:hover { background:#55ab22; diff --git a/seahub/api2/endpoints/group_discussions.py b/seahub/api2/endpoints/group_discussions.py index 8f841e452a..23b18ad4b6 100644 --- a/seahub/api2/endpoints/group_discussions.py +++ b/seahub/api2/endpoints/group_discussions.py @@ -80,7 +80,7 @@ class GroupDiscussions(APIView): @api_check_group def post(self, request, group_id, format=None): - """Post a group discussions. Only group members can perform this op. + """Post a group discussion. Only group members can perform this op. """ content = request.data.get('content', '') if not content: @@ -93,19 +93,19 @@ class GroupDiscussions(APIView): avatar_size = AVATAR_DEFAULT_SIZE username = request.user.username - discuss = GroupMessage.objects.create(group_id=group_id, + msg = GroupMessage.objects.create(group_id=group_id, from_email=username, message=content) info = get_user_common_info(username, avatar_size) - isoformat_timestr = datetime_to_isoformat_timestr(discuss.timestamp) + isoformat_timestr = datetime_to_isoformat_timestr(msg.timestamp) return Response({ - "id": discuss.pk, + "id": msg.pk, "group_id": group_id, "user_name": info["name"], "user_email": info["email"], "user_login_id": info["login_id"], "avatar_url": request.build_absolute_uri(info["avatar_url"]), - "content": discuss.message, + "content": msg.message, "created_at": isoformat_timestr }, status=201) diff --git a/seahub/group/utils.py b/seahub/group/utils.py index 9f218c30de..1c1cb5a9bc 100644 --- a/seahub/group/utils.py +++ b/seahub/group/utils.py @@ -5,6 +5,7 @@ import logging import seaserv from seahub.utils import is_org_context +from seahub.profile.models import Profile from seahub.base.templatetags.seahub_tags import email2nickname from seahub.avatar.settings import AVATAR_DEFAULT_SIZE from seahub.avatar.templatetags.avatar_tags import api_avatar_url, \ diff --git a/seahub/templates/js/templates.html b/seahub/templates/js/templates.html index e9c17b6436..d3f4392945 100644 --- a/seahub/templates/js/templates.html +++ b/seahub/templates/js/templates.html @@ -982,17 +982,19 @@ diff --git a/seahub/templates/libraries.html b/seahub/templates/libraries.html index 0dc0a8843e..0c367b5df7 100644 --- a/seahub/templates/libraries.html +++ b/seahub/templates/libraries.html @@ -160,19 +160,15 @@
- +

{% trans "No discussion in this group yet." %}

diff --git a/static/scripts/app/views/group-discussion.js b/static/scripts/app/views/group-discussion.js index c8936ec669..ccd86febc5 100644 --- a/static/scripts/app/views/group-discussion.js +++ b/static/scripts/app/views/group-discussion.js @@ -24,20 +24,33 @@ define([ initialize: function(options) { this.listenTo(this.model, 'destroy', this.remove); this.parentView = options.parentView; + this.is_group_owner = options.is_group_owner; + this.is_group_admin = options.is_group_admin; }, render: function() { var obj = this.model.attributes; var m = Moment(obj['created_at']); + var can_delete_msg = false; + if (this.is_group_owner || + this.is_group_admin || + this.model.get('user_email') == app.pageOptions.username) { + can_delete_msg = true; + } + var user_profile_url = Common.getUrl({ 'name': 'user_profile', 'username': encodeURIComponent(obj.user_email) }); _.extend(obj, { - 'content_marked': Marked(obj.content, { breaks: true }), + 'content_marked': Marked(obj.content, { + breaks: true, + sanitize: true + }), 'time': m.format('LLLL'), 'time_from_now': Common.getRelativeTimeStr(m), + 'can_delete_msg': can_delete_msg, 'user_profile_url': user_profile_url }); this.$el.html(this.template(obj)); @@ -55,7 +68,7 @@ define([ }, reply: function() { - this.parentView.beginReply(this.model.get("user_name")); + this.parentView.replyTo(this.model.get("user_name")); }, delMessage: function() { diff --git a/static/scripts/app/views/group-discussions.js b/static/scripts/app/views/group-discussions.js index c6d1157bdc..f68628c38f 100644 --- a/static/scripts/app/views/group-discussions.js +++ b/static/scripts/app/views/group-discussions.js @@ -12,13 +12,14 @@ define([ el: '#group-discussions', initialize: function(options) { + this.groupView = options.groupView; + this.collection = new GroupDiscussions(); this.listenTo(this.collection, 'add', this.addOne); this.listenTo(this.collection, 'reset', this.reset); this.$loadingTip = this.$('.loading-tip'); this.$listContainer = this.$('#group-discussion-list'); - this.$content = this.$('.popover-con'); this.$emptyTip = this.$('.no-discussion-tip'); this.$error = this.$('.error'); @@ -49,9 +50,11 @@ define([ addOne: function(item, collection, options) { var view = new ItemView({ model: item, + is_group_owner: this.is_group_owner, + is_group_admin: this.is_group_admin, parentView: this }); - if (options.prepend == true) { + if (options.prepend) { this.$listContainer.append(view.render().el); } else { this.$listContainer.prepend(view.render().el); @@ -65,9 +68,8 @@ define([ if (this.collection.length) { this.$emptyTip.hide(); this.collection.each(this.addOne, this); - this.$listContainer.removeClass('hide'); this.$listContainer.show(); - this.$content.scrollTop(9999); + this.scrollConToBottom(); } else { this.$emptyTip.show(); this.$listContainer.hide(); @@ -79,10 +81,21 @@ define([ this.$loadingTip.show(); var _this = this; + + // the user's role in this group + this.is_group_owner = false; + this.is_group_admin = false; + if (app.pageOptions.username == this.groupView.group.owner) { + this.is_group_owner = true; + } else if ($.inArray(app.pageOptions.username, this.groupView.group.admins) != -1) { + this.is_group_admin = true; + } + this.collection.setGroupId(this.group_id); this.collection.fetch({ cache: false, reset: true, + data: {'avatar_size': 64}, success: function(collection, response, opts) { }, error: function(collection, response, opts) { @@ -108,10 +121,9 @@ define([ 'max-height': $(window).height() - this.$el.offset().top - this.$('.popover-hd').outerHeight(true) - this.$('.popover-footer').outerHeight(true) - - 2 - - 10 - }); // 2: top, bottom border width of $el, - // 10: leave some margin at the bottom + - 2 // 2: top, bottom border width of $el, + - 10 // 10: leave some margin at the bottom + }); }, show: function(options) { @@ -127,8 +139,11 @@ define([ app.router.navigate('group/' + this.group_id + '/'); }, - beginReply: function(to_user) { - this.$('[name="message"]').val("@" + to_user + " "); + replyTo: function(to_user) { + var str = "@" + to_user + " "; + var $input = this.$('[name="message"]').val(str); + Common.setCaretPosition($input[0], str.length); + $input.focus(); }, formSubmit: function() { @@ -139,7 +154,10 @@ define([ return false; } - this.collection.create({ content: content }, { + this.collection.create({ + content: content, + avatar_size: 64 + }, { wait: true, validate: true, prepend: true, @@ -147,6 +165,8 @@ define([ _this.$('[name="message"]').val(''); if (_this.collection.length == 1) { _this.collection.reset(_this.collection.models); + } else { + _this.scrollConToBottom(); } }, error: function(collection, response, options) { @@ -161,6 +181,12 @@ define([ }); return false; + }, + + // scroll '.popover-con' to the bottom + scrollConToBottom: function() { + var $el = this.$('.popover-con'); + $el.scrollTop($el[0].scrollHeight - $el[0].clientHeight); } }); diff --git a/static/scripts/app/views/group-settings.js b/static/scripts/app/views/group-settings.js index 35fe88b92a..694c4bf727 100644 --- a/static/scripts/app/views/group-settings.js +++ b/static/scripts/app/views/group-settings.js @@ -52,7 +52,7 @@ define([ this.$listContainer.hide(); // the user's role in this group - this.is_owner = false, + this.is_owner = false; this.is_admin = false; if (app.pageOptions.username == this.group.owner) { diff --git a/static/scripts/app/views/group.js b/static/scripts/app/views/group.js index 20d842faaf..7550eb4056 100644 --- a/static/scripts/app/views/group.js +++ b/static/scripts/app/views/group.js @@ -49,7 +49,9 @@ define([ this.settingsView = new GroupSettingsView({ groupView: this }); - this.discussionsView = new GroupDiscussionsView(); + this.discussionsView = new GroupDiscussionsView({ + groupView: this + }); }, addOne: function(repo, collection, options) { diff --git a/static/scripts/common.js b/static/scripts/common.js index 4252f6eb7f..d9f684571e 100644 --- a/static/scripts/common.js +++ b/static/scripts/common.js @@ -829,6 +829,17 @@ define([ } } return group_name; + }, + + setCaretPosition:function(input, pos) { + var range; + if (document.selection) { + range = input.createTextRange(); + range.move("character", pos); + return range.select(); + } else { + return input.setSelectionRange(pos, pos); + } } }