From a47759e8d02425050759d51fa13228fec5e22ab2 Mon Sep 17 00:00:00 2001 From: swpd Date: Sun, 2 Mar 2014 00:29:52 -0500 Subject: [PATCH 1/2] [message] integrate group discussion into personal discussion listing page --- media/css/seahub.css | 2 +- seahub/message/message.py | 52 ++++++++++--- seahub/message/templates/message/all_msg.html | 11 ++- .../templates/message/all_msg_list.html | 60 +++++++++++---- seahub/message/views.py | 74 ++++++++++++++----- seahub/urls.py | 2 +- seahub/views/ajax.py | 11 ++- 7 files changed, 163 insertions(+), 49 deletions(-) diff --git a/media/css/seahub.css b/media/css/seahub.css index 69f37d8f52..29d6a1761f 100644 --- a/media/css/seahub.css +++ b/media/css/seahub.css @@ -2703,7 +2703,7 @@ textarea:-moz-placeholder {/* for FF */ padding:2px; margin-bottom:3px; } -#mass-email { +#mass-receiver { width:432px; } /* payments */ diff --git a/seahub/message/message.py b/seahub/message/message.py index 97ed3cf1b0..01fd823fc6 100644 --- a/seahub/message/message.py +++ b/seahub/message/message.py @@ -1,24 +1,27 @@ # -*- coding: utf-8 -*- +from seahub.group.models import GroupMessage +from seahub.notifications.models import UserNotification, MSG_TYPE_GROUP_MSG -def msg_info_list(msgs, user): +def user_msg_info_list(user_msgs, user): """Group message list by ``from_email`` or ``to_email`` and order by - message time. + message time. **Returns** - [ - (u'foo@foo.com', {'last_msg': u'test', 'not_read': 0, 'last_time': datetime.datetime(2013, 5, 27, 13, 26, 7, 423777)}), - (u'bar@bar.com', {'last_msg': u'hello', 'not_read': 0, 'last_time': datetime.datetime(2013, 5, 27, 13, 10, 31, 811318)}), + { + u'foo@foo.com': {'type': u'user', 'last_msg': u'test', 'not_read': 0, 'last_time': datetime.datetime(2013, 5, 27, 13, 26, 7, 423777)}, + u'bar@bar.com', {'type': u'user', 'last_msg': u'hello', 'not_read': 0, 'last_time': datetime.datetime(2013, 5, 27, 13, 10, 31, 811318)}, ... - ] + } """ message = {} - for msg in msgs: + for msg in user_msgs: if (msg.from_email == user): # message user send to others if not message.has_key(msg.to_email): message.setdefault(msg.to_email,{}) + message[msg.to_email].setdefault('type', 'user') message[msg.to_email].setdefault('not_read', 0) message[msg.to_email].setdefault('last_msg',msg.message) message[msg.to_email].setdefault('last_time',msg.timestamp) @@ -30,6 +33,7 @@ def msg_info_list(msgs, user): else: # message others send to the user if not message.has_key(msg.from_email): message.setdefault(msg.from_email,{}) + message[msg.from_email].setdefault('type', 'user') message[msg.from_email].setdefault('not_read',0) message[msg.from_email].setdefault('last_msg',msg.message) message[msg.from_email].setdefault('last_time',msg.timestamp) @@ -41,7 +45,35 @@ def msg_info_list(msgs, user): if not msg.ifread: message[msg.from_email]['not_read'] += 1 - sorted_msg = sorted(message.items(), key=lambda x: x[1]['last_time'], - reverse=True) - return sorted_msg + return message + +def group_msg_info_list(joined_groups, username): + """ + Group message of groups that user joined. + + **Returns** + { + u'test_group': {'type': u'group', 'id': 1, 'not_read': 32, 'last_msg': u'hey', 'last_time': datetime.datetime(2013, 5, 27, 13, 12, 31, 343452)}, + ... + } + """ + message = {} + user_notices = UserNotification.objects.filter(to_user=username, + msg_type=MSG_TYPE_GROUP_MSG) + for g in joined_groups: + group_msg = GroupMessage.objects.filter(group_id=g.id).order_by('-timestamp')[:1] + if len(group_msg) > 0: + message.setdefault(g.group_name,{}) + message[g.group_name].setdefault('type', 'group') + message[g.group_name].setdefault('id', g.id) + message[g.group_name].setdefault('not_read', 0) + message[g.group_name].setdefault('last_msg', group_msg[0].message) + message[g.group_name].setdefault('last_time', group_msg[0].timestamp) + for notice in user_notices: + gid = notice.group_message_detail_to_dict().get('group_id') + if gid == g.id: + if notice.seen is False: + message[g.group_name]['not_read'] += 1 + + return message diff --git a/seahub/message/templates/message/all_msg.html b/seahub/message/templates/message/all_msg.html index 81575f9735..7948c06d89 100644 --- a/seahub/message/templates/message/all_msg.html +++ b/seahub/message/templates/message/all_msg.html @@ -1,12 +1,21 @@ -{% load avatar_tags i18n seahub_tags %} +{% load avatar_tags group_avatar_tags i18n seahub_tags %} {% for key,value in msgs %} {% with not_read=value.not_read %} +{% if value.type == 'user' %} {% avatar key 20 %} {{ key|email2nickname }}{% if not_read > 0%}({{not_read}}){% endif %} {{ value.last_msg|seahub_urlize|truncatewords_html:12 }} {{ value.last_time|translate_seahub_time }} +{% else %} + + {% grp_avatar value.id 20 %} + {{ key }}{% if not_read > 0 %}({{not_read}}){% endif %} + {{ value.last_msg|seahub_urlize|truncatewords_html:12 }} + {{ value.last_time|translate_seahub_time }} + +{% endif %} {% endwith %} {% endfor %} diff --git a/seahub/message/templates/message/all_msg_list.html b/seahub/message/templates/message/all_msg_list.html index 1b26d6d207..d7e21577ed 100644 --- a/seahub/message/templates/message/all_msg_list.html +++ b/seahub/message/templates/message/all_msg_list.html @@ -1,5 +1,5 @@ {% extends "home_base.html" %} -{% load avatar_tags i18n seahub_tags %} +{% load avatar_tags group_avatar_tags i18n seahub_tags %} {% block sub_title %}{% trans "Messages" %}{% if total_unread > 0%}({{total_unread}}){%endif%} - {% endblock %} {% block extra_style %} @@ -25,12 +25,21 @@ {% if msgs %} {% for key,value in msgs %} {% with not_read=value.not_read %} + {% if value.type == 'user' %} {% avatar key 20 %} {{ key|email2nickname }}{% if not_read > 0%}({{not_read}}){% endif %} {{ value.last_msg|seahub_urlize|truncatewords_html:12 }} {{ value.last_time|translate_seahub_time }} + {% else %} + + {% grp_avatar value.id 20 %} + {{ key }}{% if not_read > 0 %}({{not_read}}){% endif %} + {{ value.last_msg|seahub_urlize|truncatewords_html:12 }} + {{ value.last_time|translate_seahub_time }} + + {% endif %} {% endwith %} {% endfor %} {% endif %} @@ -40,7 +49,7 @@
{% csrf_token %}
-
+
@@ -73,26 +82,38 @@ $('#add-msg').click(function() { } popup.removeClass('hide'); $.ajax({ - url:'{% url 'get_contacts' %}', + url:'{% url 'get_contacts_and_groups' %}', cache: false, dataType: 'json', success: function(data) { var contacts = data['contacts'], + groups = data['groups'], opts = '', - email; + email, + name; if (contacts.length > 0) { popup.find('.loading-tip').remove(); $('#send-msg-form').removeClass('hide'); - for(var i = 0, len = contacts.length; i < len; i++) { + var contact_len = contacts.length, group_len = groups.length; + for(var i = 0; i < contact_len; i++) { email = contacts[i].email; - opts += ''; + opts += ''; + } + for (var i = 0; i < group_len; i++) { + name = groups[i].name; + opts += ''; } var format = function(item) { - return contacts[$(item.element).data('index')].avatar + '' + item.text + ''; + var idx = $(item.element).data('index'); + if (idx < contact_len) { + return contacts[idx].avatar + '' + item.text + ''; + } else { + return groups[idx - contact_len].avatar + '' + item.text + ''; + } } - $('#mass-email').html(opts).select2({ - placeholder: "{% trans "send to: click to select contacts" %}", + $('#mass-receiver').html(opts).select2({ + placeholder: "{% trans "send to: click to select contacts/groups" %}", formatResult: format, formatSelection: format, escapeMarkup: function(m) { return m; } @@ -102,7 +123,7 @@ $('#add-msg').click(function() { } }, error: function() { - popup.html('

' + "{% trans "Failed to get your contacts for sending a message." %}" + '

'); + popup.html('

' + "{% trans "Failed to get your contacts/groups for sending a message." %}" + '

'); } }); }); @@ -158,14 +179,25 @@ $('#send-msg-form').submit(function() { form_id = form.attr('id'), msg_input = $('[name="mass_msg"]', form), msg = $.trim(msg_input.val()), - emails = $('[name="mass_email"]', form).val(); + receivers = $('[name="mass_receiver"] option:selected', form); + + var emails = [], + groups = []; + receivers.each(function() { + var type = $(this).data('type'); + if (type == "email") { + emails.push($(this).val()); + } else if (type == "group") { + groups.push($(this).val()); + } + }); if (!msg) { apply_form_error(form_id, "{% trans "message is required" %}"); return false; } - if (!emails) { // val is null or ['xx',...] - apply_form_error(form_id, "{% trans "contact is required" %}"); + if (!emails && !groups) { // val is null or ['xx',...] + apply_form_error(form_id, "{% trans "contact/group is required" %}"); return false; } @@ -185,7 +217,7 @@ $('#send-msg-form').submit(function() { type: 'POST', dataType: 'json', beforeSend: prepareCSRFToken, - data: { 'mass_msg': msg, 'mass_email': emails, 'selected': selected }, + data: { 'mass_msg': msg, 'mass_email': emails, 'mass_group': groups, 'selected': selected }, traditional: true, success: function(data) { msg_input.val(''); diff --git a/seahub/message/views.py b/seahub/message/views.py index e63b096d1f..1dff44c165 100755 --- a/seahub/message/views.py +++ b/seahub/message/views.py @@ -14,8 +14,10 @@ from django.utils.translation import ugettext as _ from django.views.decorators.http import require_POST from models import UserMessage, UserMsgAttachment -from message import msg_info_list -from seaserv import get_repo +from seahub.group.models import GroupMessage, MessageAttachment +from seahub.group.signals import grpmsg_added +from message import user_msg_info_list, group_msg_info_list +from seaserv import get_repo, get_group, is_group_user, get_personal_groups_by_user from seahub.auth.decorators import login_required from seahub.base.accounts import User from seahub.views import is_registered_user @@ -32,13 +34,16 @@ def message_list(request): """ username = request.user.username - messages = UserMessage.objects.get_messages_related_to_user(username) - msgs = msg_info_list(messages, username) - + related_user_msgs = UserMessage.objects.get_messages_related_to_user(username) + user_msgs = user_msg_info_list(related_user_msgs, username) total_unread = 0 - for msg in msgs: + for msg in user_msgs.items(): total_unread += msg[1]['not_read'] + joined_groups = get_personal_groups_by_user(username) + group_msgs = group_msg_info_list(joined_groups, username) + + msgs = sorted(user_msgs.items() + group_msgs.items(), key=lambda x: x[1]['last_time'], reverse=True) return render_to_response('message/all_msg_list.html', { 'msgs': msgs, 'total_unread': total_unread, @@ -111,7 +116,7 @@ def user_msg_list(request, id_or_email): @login_required @require_POST def message_send(request): - """Handle POST request to send message to user(s). + """Handle POST request to send message to user(s)/group(s). """ if not request.is_ajax() or request.method != 'POST': @@ -129,12 +134,13 @@ def message_send(request): mass_msg = request.POST.get('mass_msg') mass_emails = request.POST.getlist('mass_email') # e.g: [u'1@1.com, u'2@1.com'] + mass_group_ids = [int(x) for x in request.POST.getlist('mass_group')] if not mass_msg: result['error'] = [_(u'message is required')] return HttpResponse(json.dumps(result), content_type=content_type) - if not mass_emails: - result['error'] = [_(u'contact is required')] + if not mass_emails and not mass_group_ids: + result['error'] = [_(u'contact/group is required')] return HttpResponse(json.dumps(result), content_type=content_type) # attachment @@ -150,9 +156,11 @@ def message_send(request): att['path'] = item[36:] attached_items.append(att) - email_sended = [] + email_sent = [] + group_sent = [] errors = [] - msgs = [] + user_msgs = [] + group_msgs = [] for to_email in mass_emails: to_email = to_email.strip() if not to_email or not is_valid_username(to_email): @@ -166,26 +174,54 @@ def message_send(request): errors.append(_(u'Failed to send message to %s, user not found.') % to_email) continue - usermsg = UserMessage.objects.add_unread_message(username, to_email, mass_msg) - msgs.append(usermsg) + user_msg = UserMessage.objects.add_unread_message(username, to_email, mass_msg) + user_msgs.append(user_msg) if len(attached_items) > 0: for att_item in attached_items: repo_id = att_item['repo_id'] path = att_item['path'] pfds = PrivateFileDirShare.objects.add_read_only_priv_file_share( username, to_email, repo_id, path) - UserMsgAttachment.objects.add_user_msg_attachment(usermsg, pfds) + UserMsgAttachment.objects.add_user_msg_attachment(user_msg, pfds) - email_sended.append(to_email) + email_sent.append(to_email) + + joined_groups = [] + for group_id in mass_group_ids: + group = get_group(group_id) + if not group: + continue + joined_groups.append(group) + + if not is_group_user(group_id, username): + errors.append(_(u'You can not send message to group %s, you didn\'t join in.') % group.name) + continue + + group_msg = GroupMessage(group_id=group_id, from_email=username, message=mass_msg) + group_msg.save() + grpmsg_added.send(sender=GroupMessage, group_id=group_id, from_email=username) + group_msgs.append(group_msg) + if len(attached_items) > 0: + for att_item in attached_items: + repo_id = att_item['repo_id'] + path = att_item['path'] + ma = MessageAttachment(group_message=group_msg, repo_id=repo_id, + attach_type='file', path=path, + src='recommend') + ma.save() + + group_sent.append(group_id) html = '' - if email_sended: + if email_sent or group_sent: ctx = {} if fr == 'all': # from 'all_msg_list' page - ctx['msgs'] = msg_info_list(msgs, username) - html = render_to_string('message/all_msg.html', ctx) + user_msgs = user_msg_info_list(user_msgs, username) + group_msgs = group_msg_info_list(joined_groups, username) + ctx['msgs'] = sorted(user_msgs.items() + group_msgs.items(), key=lambda x: x[1]['last_time'], reverse=True) + html = render_to_string('message/all_msg.html', ctx, context_instance=RequestContext(request)) else: - ctx['msg'] = msgs[0] + ctx['msg'] = user_msgs[0] html = render_to_string('message/user_msg.html', ctx) return HttpResponse(json.dumps({"html": html, "error": errors}), content_type=content_type) else: diff --git a/seahub/urls.py b/seahub/urls.py index f90203dcc7..494f8d279a 100644 --- a/seahub/urls.py +++ b/seahub/urls.py @@ -144,7 +144,7 @@ urlpatterns = patterns('', url(r'^ajax/my-unenc-repos/$', get_my_unenc_repos, name='get_my_unenc_repos'), url(r'^ajax/unenc-rw-repos/$', unenc_rw_repos, name='unenc_rw_repos'), - url(r'^ajax/contacts/$', get_contacts, name='get_contacts'), + url(r'^ajax/contacts_and_groups/$', get_contacts_and_groups, name='get_contacts_and_groups'), url(r'^ajax/upload-file-done/$', upload_file_done, name='upload_file_done'), url(r'^ajax/unseen-notices-count/$', unseen_notices_count, name='unseen_notices_count'), url(r'^ajax/space_and_traffic/$', space_and_traffic, name='space_and_traffic'), diff --git a/seahub/views/ajax.py b/seahub/views/ajax.py index 40854bd0b1..b6f96e897f 100644 --- a/seahub/views/ajax.py +++ b/seahub/views/ajax.py @@ -15,7 +15,7 @@ import seaserv from seaserv import seafile_api, seafserv_rpc, \ get_related_users_by_repo, get_related_users_by_org_repo, \ is_org_repo_owner, CALC_SHARE_USAGE, seafserv_threaded_rpc, \ - get_user_quota_usage, get_user_share_usage + get_user_quota_usage, get_user_share_usage, get_personal_groups_by_user from pysearpc import SearpcError from seahub.auth.decorators import login_required @@ -899,7 +899,7 @@ def repo_unstar_file(request, repo_id): ########## contacts related @login_required -def get_contacts(request): +def get_contacts_and_groups(request): if not request.is_ajax(): raise Http404 @@ -912,7 +912,12 @@ def get_contacts(request): for c in contacts: contact_list.append({"email": c.contact_email, "avatar": avatar(c.contact_email, 16)}) - return HttpResponse(json.dumps({"contacts":contact_list}), content_type=content_type) + joined_groups = get_personal_groups_by_user(username) + group_list = [] + from seahub.avatar.templatetags.group_avatar_tags import grp_avatar + for g in joined_groups: + group_list.append({"id": g.id, "name": g.group_name, "avatar": grp_avatar(g.id, 16)}) + return HttpResponse(json.dumps({"contacts":contact_list, "groups":group_list}), content_type=content_type) @login_required def get_current_commit(request, repo_id): From 707a35462e5b702fe94d9beaf9536928011b8d9f Mon Sep 17 00:00:00 2001 From: swpd Date: Sun, 2 Mar 2014 10:47:02 -0500 Subject: [PATCH 2/2] [message] allow user to remove personal discussion --- .../templates/message/user_msg_list.html | 61 ++++++++++++++++++- seahub/message/views.py | 19 ++++++ seahub/urls.py | 3 +- 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/seahub/message/templates/message/user_msg_list.html b/seahub/message/templates/message/user_msg_list.html index dc9b009cb7..7a3c254c36 100644 --- a/seahub/message/templates/message/user_msg_list.html +++ b/seahub/message/templates/message/user_msg_list.html @@ -27,15 +27,18 @@
    {% for msg in person_msgs.object_list %} {% if msg.to_email == request.user.email %} -
  • +
  • {% avatar msg.from_email 48 %} {% else %} -
  • +
  • {% avatar msg.from_email 48 %} {% endif %}
    + {% if request.user.is_staff or msg.from_email == request.user.username %} + {% trans "Delete" %} + {% endif %} {{ msg.from_email|email2nickname }} {{ msg.timestamp|translate_seahub_time }}
    @@ -160,6 +163,60 @@ $('#personal-message-form').submit(function() { }); return false; }); + + +$('.msg-del').click(function() { + var msg = $(this).parents('.msg'); + var cfm; + if (msg.find('.msg-del-confirm').length > 0) { + cfm = msg.find('.msg-del-confirm'); + } else { + cfm = '

    {% trans "Really want to delete this discussion?" %}

    '; + var msg_main = msg.find('.msg-main'); + var msg_hd = msg.find('.msg-hd'); + cfm = msg_main.append(cfm).children(':last'); + cfm.css({'right':msg_main.css('padding-right'), 'top':msg_hd.position().top + msg_hd.height()}); + } + cfm.removeClass('hide'); + cfm.children('.yes').click(function() { + cfm.addClass('hide'); + $.ajax({ + url: '{{SITE_ROOT}}user/' + msg.attr('data-id') + '/msgdel/', + dataType:'json', + success: function(data) { + if (data['success']) { + msg.remove(); + feedback('{% trans "Successfully deleted" %}', 'success'); + } else { + feedback('{% trans "Failed to delete: " %}' + data['err_msg'], 'error'); + } + }, + error: function() { + feedback('{% trans "Failed." %}', 'error'); + } + }); + }); + cfm.children('.no').click(function() { + cfm.addClass('hide'); + }); +}); + +$('.msg-main').hover( + function(){ + $(this).find('.op').removeClass('vh'); + }, + function(){ + $(this).find('.op').addClass('vh'); + } +); +$('.msg .op').hover( + function() { + $(this).css({'text-decoration':'underline'}); + }, + function() { + $(this).css({'text-decoration':'none'}); + } +); {% include 'snippets/add_to_contacts_js.html' %} diff --git a/seahub/message/views.py b/seahub/message/views.py index 1dff44c165..bb5df193e1 100755 --- a/seahub/message/views.py +++ b/seahub/message/views.py @@ -113,6 +113,25 @@ def user_msg_list(request, id_or_email): "to_email": to_email, }, context_instance=RequestContext(request)) +@login_required +def user_msg_remove(request, msg_id): + """Remove message related to a certain person. + """ + try: + msg = UserMessage.objects.get(message_id=msg_id) + except UserMessage.DoesNotExist: + return HttpResponse(json.dumps({'success': False, 'err_msg':_(u"The message doesn't exist")}), + content_type='application/json; charset=utf-8') + else: + # Test whether user is admin or message owner. + if request.user.is_staff or msg.from_email == request.user.username: + msg.delete() + return HttpResponse(json.dumps({'success': True}), + content_type='application/json; charset=utf-8') + else: + return HttpResponse(json.dumps({'success': False, 'err_msg': _(u"You don't have the permission.")}), + content_type='application/json; charset=utf-8') + @login_required @require_POST def message_send(request): diff --git a/seahub/urls.py b/seahub/urls.py index 494f8d279a..e7f7aae170 100644 --- a/seahub/urls.py +++ b/seahub/urls.py @@ -10,7 +10,7 @@ from seahub.views.file import view_file, view_history_file, view_trash_file,\ from seahub.views.repo import repo, repo_history_view from notifications.views import notification_list from group.views import group_list -from message.views import user_msg_list +from message.views import user_msg_list, user_msg_remove from share.views import user_share_list, gen_private_file_share, \ rm_private_file_share, save_private_file_share from seahub.views.wiki import personal_wiki, personal_wiki_pages, \ @@ -108,6 +108,7 @@ urlpatterns = patterns('', (r'^seafile_access_check/$', seafile_access_check), url(r'^convert_cmmt_desc_link/$', convert_cmmt_desc_link, name='convert_cmmt_desc_link'), url(r'^user/(?P[^/]+)/msgs/$', user_msg_list, name='user_msg_list'), + url(r'^user/(?P\d+)/msgdel/$', user_msg_remove, name='user_msg_remove'), url(r'^user/(?P[^/]+)/shares/$', user_share_list, name='user_share_list'), url(r'^modules/toggle/$', toggle_modules, name="toggle_modules"), url(r'^download_client_program/$', TemplateView.as_view(template_name="download.html"), name="download_client"),