diff --git a/seahub/api2/endpoints/dir_shared_items.py b/seahub/api2/endpoints/dir_shared_items.py index f456e2e3ff..3c71cc58de 100644 --- a/seahub/api2/endpoints/dir_shared_items.py +++ b/seahub/api2/endpoints/dir_shared_items.py @@ -17,7 +17,8 @@ from seahub.api2.throttling import UserRateThrottle from seahub.api2.utils import api_error from seahub.base.templatetags.seahub_tags import email2nickname from seahub.base.accounts import User -from seahub.share.signals import share_repo_to_user_successful +from seahub.share.signals import share_repo_to_user_successful, \ + share_repo_to_group_successful from seahub.utils import (is_org_context, is_valid_username, send_perm_audit_msg) @@ -345,6 +346,9 @@ class DirSharedItemsEndpoint(APIView): seafile_api.set_group_repo(shared_repo.repo_id, gid, username, permission) + share_repo_to_group_successful.send(sender=None, + from_user=username, group_id=gid, repo=shared_repo) + result['success'].append({ "share_type": "group", "group_info": { diff --git a/seahub/api2/views.py b/seahub/api2/views.py index 86fca1815f..712ff7506b 100644 --- a/seahub/api2/views.py +++ b/seahub/api2/views.py @@ -22,7 +22,7 @@ from django.contrib.auth.hashers import check_password from django.contrib.sites.models import RequestSite from django.db import IntegrityError from django.db.models import F, Q -from django.http import HttpResponse, Http404 +from django.http import HttpResponse from django.template import RequestContext from django.template.loader import render_to_string from django.template.defaultfilters import filesizeformat @@ -40,31 +40,24 @@ from .utils import get_diff_details, \ api_repo_user_folder_perm_check, api_repo_setting_permission_check, \ api_repo_group_folder_perm_check -from seahub.avatar.settings import AVATAR_DEFAULT_SIZE from seahub.avatar.templatetags.avatar_tags import api_avatar_url, avatar from seahub.avatar.templatetags.group_avatar_tags import api_grp_avatar_url, \ grp_avatar from seahub.base.accounts import User -from seahub.base.models import FileDiscuss, UserStarredFiles, DeviceToken +from seahub.base.models import UserStarredFiles, DeviceToken from seahub.base.templatetags.seahub_tags import email2nickname, \ - translate_commit_desc, translate_seahub_time, translate_commit_desc_escape -from seahub.group.models import GroupMessage, MessageReply, MessageAttachment -from seahub.group.signals import grpmsg_added -from seahub.group.views import group_check, remove_group_common, \ + translate_seahub_time, translate_commit_desc_escape +from seahub.group.views import remove_group_common, \ rename_group_with_new_name, is_group_staff from seahub.group.utils import BadGroupNameError, ConflictGroupNameError, \ validate_group_name from seahub.thumbnail.utils import generate_thumbnail -from seahub.message.models import UserMessage from seahub.notifications.models import UserNotification from seahub.options.models import UserOptions from seahub.contacts.models import Contact from seahub.profile.models import Profile, DetailedProfile -from seahub.shortcuts import get_first_object_or_none from seahub.signals import (repo_created, repo_deleted) from seahub.share.models import FileShare, OrgFileShare, UploadLinkShare -from seahub.share.signals import share_repo_to_user_successful -from seahub.share.views import list_shared_repos from seahub.utils import gen_file_get_url, gen_token, gen_file_upload_url, \ check_filename_with_rename, is_valid_username, EVENTS_ENABLED, \ get_user_events, EMPTY_SHA1, get_ccnet_server_addr_port, is_pro_version, \ @@ -75,11 +68,11 @@ from seahub.utils import gen_file_get_url, gen_token, gen_file_upload_url, \ from seahub.utils.devices import get_user_devices, do_unlink_device from seahub.utils.repo import get_sub_repo_abbrev_origin_path from seahub.utils.star import star_file, unstar_file -from seahub.utils.file_types import IMAGE, DOCUMENT +from seahub.utils.file_types import DOCUMENT from seahub.utils.file_size import get_file_size_unit from seahub.utils.timeutils import utc_to_local, datetime_to_isoformat_timestr -from seahub.views import validate_owner, is_registered_user, check_file_lock, \ - group_events_data, get_diff, create_default_library, get_owned_repo_list, \ +from seahub.views import is_registered_user, check_file_lock, \ + group_events_data, get_diff, create_default_library, \ list_inner_pub_repos, get_virtual_repos_by_owner, \ check_folder_permission from seahub.views.ajax import get_share_in_repo_list, get_groups_by_user, \ @@ -93,7 +86,7 @@ if HAS_OFFICE_CONVERTER: import seahub.settings as settings from seahub.settings import THUMBNAIL_EXTENSION, THUMBNAIL_ROOT, \ ENABLE_GLOBAL_ADDRESSBOOK, FILE_LOCK_EXPIRATION_DAYS, \ - ENABLE_THUMBNAIL, ENABLE_SUB_LIBRARY, ENABLE_FOLDER_PERM + ENABLE_THUMBNAIL, ENABLE_FOLDER_PERM try: from seahub.settings import CLOUD_MODE except ImportError: @@ -119,15 +112,14 @@ except ImportError: from pysearpc import SearpcError, SearpcObjEncoder import seaserv -from seaserv import seafserv_rpc, seafserv_threaded_rpc, \ +from seaserv import seafserv_threaded_rpc, \ get_personal_groups_by_user, get_session_info, is_personal_repo, \ get_repo, check_permission, get_commits, is_passwd_set,\ - list_personal_repos_by_owner, check_quota, \ - list_share_repos, get_group_repos_by_owner, get_group_repoids, \ + check_quota, list_share_repos, get_group_repos_by_owner, get_group_repoids, \ list_inner_pub_repos_by_owner, is_group_user, \ - remove_share, unshare_group_repo, unset_inner_pub_repo, get_group, \ + remove_share, unset_inner_pub_repo, get_group, \ get_commit, get_file_id_by_path, MAX_DOWNLOAD_DIR_SIZE, edit_repo, \ - ccnet_threaded_rpc, get_personal_groups, seafile_api, check_group_staff, \ + ccnet_threaded_rpc, get_personal_groups, seafile_api, \ create_org, ccnet_api from constance import config diff --git a/seahub/notifications/management/commands/send_notices.py b/seahub/notifications/management/commands/send_notices.py index 6b013fd3c4..6dd3f549f3 100644 --- a/seahub/notifications/management/commands/send_notices.py +++ b/seahub/notifications/management/commands/send_notices.py @@ -13,7 +13,7 @@ from django.utils import translation from django.utils.translation import ugettext as _ import seaserv -from seaserv import seafile_api +from seaserv import seafile_api, ccnet_api from seahub.base.models import CommandsLastCheck from seahub.notifications.models import UserNotification from seahub.utils import send_html_email, get_service_url, \ @@ -83,9 +83,7 @@ class Command(BaseCommand): d = notice.group_message_detail_to_dict() group_id = d['group_id'] message = d['message'] - group = seaserv.get_group(int(group_id)) - if group is None: - notice.delete() + group = ccnet_api.get_group(int(group_id)) notice.group_url = reverse('group_discuss', args=[group.id]) notice.notice_from = escape(email2nickname(d['msg_from'])) @@ -96,16 +94,31 @@ class Command(BaseCommand): def format_repo_share_msg(self, notice): d = json.loads(notice.detail) - repo_id = d['repo_id'] repo = seafile_api.get_repo(repo_id) - if repo is None: - notice.delete() notice.repo_url = reverse("view_common_lib_dir", args=[repo_id, '']) notice.notice_from = escape(email2nickname(d['share_from'])) notice.repo_name = repo.name notice.avatar_src = self.get_avatar_src(d['share_from']) + + return notice + + def format_repo_share_to_group_msg(self, notice): + d = json.loads(notice.detail) + + repo_id = d['repo_id'] + repo = seafile_api.get_repo(repo_id) + group_id = d['group_id'] + group = ccnet_api.get_group(group_id) + + notice.repo_url = reverse("view_common_lib_dir", args=[repo_id, '']) + notice.notice_from = escape(email2nickname(d['share_from'])) + notice.repo_name = repo.name + notice.avatar_src = self.get_avatar_src(d['share_from']) + notice.group_url = reverse("group_info", args=[group.id]) + notice.group_name = group.group_name + return notice def format_file_uploaded_msg(self, notice): @@ -132,9 +145,7 @@ class Command(BaseCommand): group_id = d['group_id'] join_request_msg = d['join_request_msg'] - group = seaserv.get_group(group_id) - if group is None: - notice.delete() + group = ccnet_api.get_group(group_id) notice.grpjoin_user_profile_url = reverse('user_profile', args=[username]) @@ -150,9 +161,7 @@ class Command(BaseCommand): group_staff = d['group_staff'] group_id = d['group_id'] - group = seaserv.get_group(group_id) - if group is None: - notice.delete() + group = ccnet_api.get_group(group_id) notice.notice_from = group_staff notice.avatar_src = self.get_avatar_src(group_staff) @@ -208,6 +217,22 @@ class Command(BaseCommand): for notice in unseen_notices: logger.info('Processing unseen notice: [%s]' % (notice)) + d = json.loads(notice.detail) + + repo_id = d.get('repo_id', None) + group_id = d.get('group_id', None) + try: + if repo_id and not seafile_api.get_repo(repo_id): + notice.delete() + continue + + if group_id and not ccnet_api.get_group(group_id): + notice.delete() + continue + except Exception as e: + logger.error(e) + continue + if notice.to_user != to_user: continue @@ -220,6 +245,9 @@ class Command(BaseCommand): elif notice.is_repo_share_msg(): notice = self.format_repo_share_msg(notice) + elif notice.is_repo_share_to_group_msg(): + notice = self.format_repo_share_to_group_msg(notice) + elif notice.is_file_uploaded_msg(): notice = self.format_file_uploaded_msg(notice) diff --git a/seahub/notifications/models.py b/seahub/notifications/models.py index 679f38c975..2f535a8945 100644 --- a/seahub/notifications/models.py +++ b/seahub/notifications/models.py @@ -5,14 +5,12 @@ import json import logging from django.db import models -from django.db.models.signals import post_save from django.forms import ModelForm, Textarea -from django.utils.http import urlquote from django.utils.html import escape from django.utils.translation import ugettext as _ import seaserv -from seaserv import seafile_api +from seaserv import seafile_api, ccnet_api from seahub.base.fields import LowerCaseCharField from seahub.base.templatetags.seahub_tags import email2nickname @@ -42,6 +40,7 @@ MSG_TYPE_GROUP_JOIN_REQUEST = 'group_join_request' MSG_TYPE_ADD_USER_TO_GROUP = 'add_user_to_group' MSG_TYPE_FILE_UPLOADED = 'file_uploaded' MSG_TYPE_REPO_SHARE = 'repo_share' +MSG_TYPE_REPO_SHARE_TO_GROUP = 'repo_share_to_group' MSG_TYPE_USER_MESSAGE = 'user_message' def file_uploaded_msg_to_json(file_name, repo_id, uploaded_to): @@ -53,6 +52,9 @@ def file_uploaded_msg_to_json(file_name, repo_id, uploaded_to): def repo_share_msg_to_json(share_from, repo_id): return json.dumps({'share_from': share_from, 'repo_id': repo_id}) +def repo_share_to_group_msg_to_json(share_from, repo_id, group_id): + return json.dumps({'share_from': share_from, 'repo_id': repo_id, 'group_id': group_id}) + def group_msg_to_json(group_id, msg_from, message): return json.dumps({'group_id': group_id, 'msg_from': msg_from, 'message': message}) @@ -72,7 +74,7 @@ def add_user_to_group_to_json(group_staff, group_id): class UserNotificationManager(models.Manager): def _add_user_notification(self, to_user, msg_type, detail): """Add generic user notification. - + Arguments: - `self`: - `username`: @@ -85,11 +87,11 @@ class UserNotificationManager(models.Manager): def get_all_notifications(self, seen=None, time_since=None): """Get all notifications of all users. - + Arguments: - `self`: - `seen`: - - `time_since`: + - `time_since`: """ qs = super(UserNotificationManager, self).all() if seen is not None: @@ -97,10 +99,10 @@ class UserNotificationManager(models.Manager): if time_since is not None: qs = qs.filter(timestamp__gt=time_since) return qs - + def get_user_notifications(self, username, seen=None): """Get all notifications(group_msg, grpmsg_reply, etc) of a user. - + Arguments: - `self`: - `username`: @@ -112,23 +114,23 @@ class UserNotificationManager(models.Manager): def remove_user_notifications(self, username): """Remove all user notifications. - + Arguments: - `self`: - `username`: """ self.get_user_notifications(username).delete() - + def count_unseen_user_notifications(self, username): """ - + Arguments: - `self`: - `username`: """ return super(UserNotificationManager, self).filter( to_user=username, seen=False).count() - + def bulk_add_group_msg_notices(self, to_users, detail): """Efficiently add group message notices. @@ -179,10 +181,10 @@ class UserNotificationManager(models.Manager): super(UserNotificationManager, self).filter( to_user=to_user, msg_type=MSG_TYPE_GROUP_MSG, detail=str(group_id)).delete() - + def add_group_join_request_notice(self, to_user, detail): """ - + Arguments: - `self`: - `to_user`: @@ -205,7 +207,7 @@ class UserNotificationManager(models.Manager): def add_file_uploaded_msg(self, to_user, detail): """ - + Arguments: - `self`: - `to_user`: @@ -217,7 +219,7 @@ class UserNotificationManager(models.Manager): def add_repo_share_msg(self, to_user, detail): """Notify ``to_user`` that others shared a repo to him/her. - + Arguments: - `self`: - `to_user`: @@ -226,9 +228,20 @@ class UserNotificationManager(models.Manager): return self._add_user_notification(to_user, MSG_TYPE_REPO_SHARE, detail) + def add_repo_share_to_group_msg(self, to_user, detail): + """Notify ``to_user`` that others shared a repo to group. + + Arguments: + - `self`: + - `to_user`: + - `detail`: + """ + return self._add_user_notification(to_user, + MSG_TYPE_REPO_SHARE_TO_GROUP, detail) + def add_user_message(self, to_user, detail): """Notify ``to_user`` that others sent a message to him/her. - + Arguments: - `self`: - `to_user`: @@ -236,7 +249,7 @@ class UserNotificationManager(models.Manager): """ return self._add_user_notification(to_user, MSG_TYPE_USER_MESSAGE, detail) - + class UserNotification(models.Model): to_user = LowerCaseCharField(db_index=True, max_length=255) msg_type = models.CharField(db_index=True, max_length=30) @@ -250,7 +263,7 @@ class UserNotification(models.Model): class Meta: ordering = ["-timestamp"] - + def __unicode__(self): return '%s|%s|%s' % (self.to_user, self.msg_type, self.detail) @@ -259,7 +272,7 @@ class UserNotification(models.Model): Use this in a template to mark an unseen notice differently the first time it is shown. - + Arguments: - `self`: """ @@ -268,10 +281,10 @@ class UserNotification(models.Model): self.seen = True self.save() return seen - + def is_group_msg(self): """Check whether is a group message notification. - + Arguments: - `self`: """ @@ -279,7 +292,7 @@ class UserNotification(models.Model): def is_file_uploaded_msg(self): """ - + Arguments: - `self`: """ @@ -287,15 +300,23 @@ class UserNotification(models.Model): def is_repo_share_msg(self): """ - + Arguments: - `self`: """ return self.msg_type == MSG_TYPE_REPO_SHARE + def is_repo_share_to_group_msg(self): + """ + + Arguments: + - `self`: + """ + return self.msg_type == MSG_TYPE_REPO_SHARE_TO_GROUP + def is_user_message(self): """ - + Arguments: - `self`: """ @@ -303,7 +324,7 @@ class UserNotification(models.Model): def is_group_join_request(self): """ - + Arguments: - `self`: """ @@ -322,7 +343,7 @@ class UserNotification(models.Model): ``msg_from``. NOTE: ``msg_from`` may be ``None``. - + Arguments: - `self`: @@ -374,7 +395,7 @@ class UserNotification(models.Model): ########## functions used in templates def format_file_uploaded_msg(self): """ - + Arguments: - `self`: """ @@ -416,7 +437,7 @@ class UserNotification(models.Model): def format_repo_share_msg(self): """ - + Arguments: - `self`: """ @@ -439,6 +460,44 @@ class UserNotification(models.Model): 'href': reverse('view_common_lib_dir', args=[repo.id, '']), 'repo_name': escape(repo.name), } + + return msg + + def format_repo_share_to_group_msg(self): + """ + + Arguments: + - `self`: + """ + try: + d = json.loads(self.detail) + except Exception as e: + logger.error(e) + return _(u"Internal error") + + share_from = email2nickname(d['share_from']) + repo_id = d['repo_id'] + group_id = d['group_id'] + + try: + repo = seafile_api.get_repo(repo_id) + group = ccnet_api.get_group(group_id) + except Exception as e: + logger.error(e) + return None + + if not repo or not group: + self.delete() + return None + + msg = _(u"%(user)s has shared a library named %(repo_name)s to group %(group_name)s.") % { + 'user': escape(share_from), + 'repo_href': reverse('view_common_lib_dir', args=[repo.id, '']), + 'repo_name': escape(repo.name), + 'group_href': reverse('group_info', args=[group.id]), + 'group_name': escape(group.group_name), + } + return msg def format_user_message_title(self): @@ -450,6 +509,7 @@ class UserNotification(models.Model): try: d = self.user_message_detail_to_dict() except self.InvalidDetailError as e: + logger.error(e) return _(u"Internal error") msg_from = d.get('msg_from') @@ -470,6 +530,7 @@ class UserNotification(models.Model): try: d = self.user_message_detail_to_dict() except self.InvalidDetailError as e: + logger.error(e) return _(u"Internal error") message = d.get('message') @@ -480,17 +541,18 @@ class UserNotification(models.Model): def format_group_message_title(self): """ - + Arguments: - `self`: """ try: d = self.group_message_detail_to_dict() except self.InvalidDetailError as e: + logger.error(e) return _(u"Internal error") group_id = d.get('group_id') - group = seaserv.get_group(group_id) + group = ccnet_api.get_group(group_id) if group is None: self.delete() return None @@ -517,6 +579,7 @@ class UserNotification(models.Model): try: d = self.group_message_detail_to_dict() except self.InvalidDetailError as e: + logger.error(e) return _(u"Internal error") message = d.get('message') @@ -527,7 +590,7 @@ class UserNotification(models.Model): def format_group_join_request(self): """ - + Arguments: - `self`: """ @@ -541,7 +604,7 @@ class UserNotification(models.Model): group_id = d['group_id'] join_request_msg = d['join_request_msg'] - group = seaserv.get_group(group_id) + group = ccnet_api.get_group(group_id) if group is None: self.delete() return None @@ -570,7 +633,7 @@ class UserNotification(models.Model): group_staff = d['group_staff'] group_id = d['group_id'] - group = seaserv.get_group(group_id) + group = ccnet_api.get_group(group_id) if group is None: self.delete() return None @@ -588,10 +651,9 @@ from django.core.urlresolvers import reverse from django.dispatch import receiver from seahub.signals import upload_file_successful -from seahub.group.models import GroupMessage, MessageReply from seahub.group.signals import grpmsg_added, group_join_request, add_user_to_group -from seahub.share.signals import share_repo_to_user_successful -from seahub.message.models import UserMessage +from seahub.share.signals import share_repo_to_user_successful, \ + share_repo_to_group_successful from seahub.message.signals import user_message_sent @receiver(upload_file_successful) @@ -606,24 +668,6 @@ def add_upload_file_msg_cb(sender, **kwargs): filename = os.path.basename(file_path) folder_path = os.path.dirname(file_path) - folder_name = os.path.basename(folder_path) - - detail = file_uploaded_msg_to_json(filename, repo_id, folder_path) - UserNotification.objects.add_file_uploaded_msg(owner, detail) - -@receiver(upload_file_successful) -def add_upload_file_msg_cb(sender, **kwargs): - """Notify repo owner when others upload files to his/her folder from shared link. - """ - repo_id = kwargs.get('repo_id', None) - file_path = kwargs.get('file_path', None) - owner = kwargs.get('owner', None) - - assert repo_id and file_path and owner is not None, 'Arguments error' - - filename = os.path.basename(file_path) - folder_path = os.path.dirname(file_path) - folder_name = os.path.basename(folder_path) detail = file_uploaded_msg_to_json(filename, repo_id, folder_path) UserNotification.objects.add_file_uploaded_msg(owner, detail) @@ -635,12 +679,30 @@ def add_share_repo_msg_cb(sender, **kwargs): from_user = kwargs.get('from_user', None) to_user = kwargs.get('to_user', None) repo = kwargs.get('repo', None) - + assert from_user and to_user and repo is not None, 'Arguments error' detail = repo_share_msg_to_json(from_user, repo.id) UserNotification.objects.add_repo_share_msg(to_user, detail) +@receiver(share_repo_to_group_successful) +def add_share_repo_to_group_msg_cb(sender, **kwargs): + """Notify group member when others share repos to group. + """ + from_user = kwargs.get('from_user', None) + group_id = kwargs.get('group_id', None) + repo = kwargs.get('repo', None) + + assert from_user and group_id and repo is not None, 'Arguments error' + + members = ccnet_api.get_group_members(int(group_id)) + for member in members: + to_user = member.user_name + if to_user == from_user: + continue + detail = repo_share_to_group_msg_to_json(from_user, repo.id, group_id) + UserNotification.objects.add_repo_share_to_group_msg(to_user, detail) + @receiver(user_message_sent) def add_user_message_cb(sender, **kwargs): """Notify user when he/she got a new mesaage. diff --git a/seahub/notifications/templates/notifications/notice_email.html b/seahub/notifications/templates/notifications/notice_email.html index 56a5ddbbc7..a5bb0a78ad 100644 --- a/seahub/notifications/templates/notifications/notice_email.html +++ b/seahub/notifications/templates/notifications/notice_email.html @@ -32,6 +32,9 @@ You've got {{num}} new notices on {{ site_name }}: {% elif notice.is_repo_share_msg %}

{% blocktrans with user=notice.notice_from lib_url=notice.repo_url lib_name=notice.repo_name%}{{user}} has shared a library named {{lib_name}} to you.{% endblocktrans %}

+ {% elif notice.is_repo_share_to_group_msg %} +

{% blocktrans with user=notice.notice_from lib_url=notice.repo_url lib_name=notice.repo_name group_url=notice.group_url group_name=notice.group_name %}{{user}} has shared a library named {{lib_name}} to {{group_name}}.{% endblocktrans %}

+ {% elif notice.is_user_message %}

{% blocktrans with msg_url=notice.user_msg_url user=notice.notice_from %}You have received a new message from {{user}}.{% endblocktrans %}

{% if notice.user_msg %} diff --git a/seahub/notifications/templates/notifications/user_notification_tr.html b/seahub/notifications/templates/notifications/user_notification_tr.html index cff94bcbc1..b162e96ff7 100644 --- a/seahub/notifications/templates/notifications/user_notification_tr.html +++ b/seahub/notifications/templates/notifications/user_notification_tr.html @@ -32,6 +32,9 @@ {% elif notice.is_repo_share_msg %}

{{ notice.format_repo_share_msg|safe }}

+ {% elif notice.is_repo_share_to_group_msg %} +

{{ notice.format_repo_share_to_group_msg|safe }}

+ {% elif notice.is_group_join_request %}

{{ notice.format_group_join_request|safe }}

{% endif %} diff --git a/seahub/notifications/views.py b/seahub/notifications/views.py index d841961b7c..68abe6b3db 100644 --- a/seahub/notifications/views.py +++ b/seahub/notifications/views.py @@ -163,6 +163,14 @@ def add_notice_from_info(notices): logger.error(e) notice.default_avatar_url = default_avatar_url + elif notice.is_repo_share_to_group_msg(): + try: + d = json.loads(notice.detail) + notice.msg_from = d['share_from'] + except Exception as e: + logger.error(e) + notice.default_avatar_url = default_avatar_url + elif notice.is_group_join_request(): try: d = json.loads(notice.detail) diff --git a/seahub/share/signals.py b/seahub/share/signals.py index 0935163a8d..f9dbddaa6e 100644 --- a/seahub/share/signals.py +++ b/seahub/share/signals.py @@ -1,3 +1,4 @@ import django.dispatch share_repo_to_user_successful = django.dispatch.Signal(providing_args=["from_user", "to_user", "repo"]) +share_repo_to_group_successful = django.dispatch.Signal(providing_args=["from_user", "group_id", "repo"]) diff --git a/seahub/templates/snippets/notice_html.html b/seahub/templates/snippets/notice_html.html index 9d7991a8f5..4953cf934b 100644 --- a/seahub/templates/snippets/notice_html.html +++ b/seahub/templates/snippets/notice_html.html @@ -30,6 +30,9 @@ {% elif notice.is_repo_share_msg %}

{{ notice.format_repo_share_msg|safe }}

+ {% elif notice.is_repo_share_to_group_msg %} +

{{ notice.format_repo_share_to_group_msg|safe }}

+ {% elif notice.is_group_join_request %}

{{ notice.format_group_join_request|safe }}