1# Copyright (c) 2012-2016 Seafile Ltd. 2# -*- coding: utf-8 -*- 3import datetime 4import os 5import json 6import logging 7 8from django.urls import reverse 9from django.db import models 10from django.conf import settings 11from django.forms import ModelForm, Textarea 12from django.utils.html import escape 13from django.utils.translation import ugettext as _ 14from django.core.cache import cache 15from django.template.loader import render_to_string 16 17import seaserv 18from seaserv import seafile_api, ccnet_api 19 20from seahub.base.fields import LowerCaseCharField 21from seahub.base.templatetags.seahub_tags import email2nickname 22from seahub.invitations.models import Invitation 23from seahub.utils.repo import get_repo_shared_users 24from seahub.utils import normalize_cache_key 25from seahub.utils.timeutils import datetime_to_isoformat_timestr 26from seahub.constants import HASH_URLS 27from seahub.drafts.models import DraftReviewer 28from seahub.file_participants.utils import list_file_participants 29 30# Get an instance of a logger 31logger = logging.getLogger(__name__) 32 33 34class NotificationManager(models.Manager): 35 def create_sys_notification(self, message, is_primary=False): 36 """ 37 Creates and saves a system notification. 38 """ 39 notification = Notification() 40 notification.message = message 41 notification.primary = is_primary 42 notification.save() 43 44 return notification 45 46 47########## system notification 48class Notification(models.Model): 49 message = models.CharField(max_length=512) 50 primary = models.BooleanField(default=False, db_index=True) 51 objects = NotificationManager() 52 53 def update_notification_to_current(self): 54 self.primary = 1 55 self.save() 56 57class NotificationForm(ModelForm): 58 """ 59 Form for adding notification. 60 """ 61 class Meta: 62 model = Notification 63 fields = ('message', 'primary') 64 widgets = { 65 'message': Textarea(), 66 } 67 68########## user notification 69MSG_TYPE_GROUP_JOIN_REQUEST = 'group_join_request' 70MSG_TYPE_ADD_USER_TO_GROUP = 'add_user_to_group' 71MSG_TYPE_FILE_UPLOADED = 'file_uploaded' 72MSG_TYPE_REPO_SHARE = 'repo_share' 73MSG_TYPE_REPO_SHARE_TO_GROUP = 'repo_share_to_group' 74MSG_TYPE_USER_MESSAGE = 'user_message' 75MSG_TYPE_FILE_COMMENT = 'file_comment' 76MSG_TYPE_DRAFT_COMMENT = 'draft_comment' 77MSG_TYPE_DRAFT_REVIEWER = 'draft_reviewer' 78MSG_TYPE_GUEST_INVITATION_ACCEPTED = 'guest_invitation_accepted' 79MSG_TYPE_REPO_TRANSFER = 'repo_transfer' 80 81USER_NOTIFICATION_COUNT_CACHE_PREFIX = 'USER_NOTIFICATION_COUNT_' 82 83def file_uploaded_msg_to_json(file_name, repo_id, uploaded_to): 84 """Encode file uploaded message to json string. 85 """ 86 return json.dumps({'file_name': file_name, 'repo_id': repo_id, 87 'uploaded_to': uploaded_to}) 88 89def repo_share_msg_to_json(share_from, repo_id, path, org_id): 90 return json.dumps({'share_from': share_from, 'repo_id': repo_id, 91 'path': path, 'org_id': org_id}) 92 93def repo_share_to_group_msg_to_json(share_from, repo_id, group_id, path, org_id): 94 return json.dumps({'share_from': share_from, 'repo_id': repo_id, 95 'group_id': group_id, 'path': path, 'org_id': org_id}) 96 97def group_msg_to_json(group_id, msg_from, message): 98 return json.dumps({'group_id': group_id, 'msg_from': msg_from, 99 'message': message}) 100 101def user_msg_to_json(message, msg_from): 102 return json.dumps({'message': message, 'msg_from': msg_from}) 103 104def group_join_request_to_json(username, group_id, join_request_msg): 105 return json.dumps({'username': username, 'group_id': group_id, 106 'join_request_msg': join_request_msg}) 107 108def add_user_to_group_to_json(group_staff, group_id): 109 return json.dumps({'group_staff': group_staff, 110 'group_id': group_id}) 111 112def file_comment_msg_to_json(repo_id, file_path, author, comment): 113 return json.dumps({'repo_id': repo_id, 114 'file_path': file_path, 115 'author': author, 116 'comment': comment}) 117 118def draft_comment_msg_to_json(draft_id, author, comment): 119 return json.dumps({'draft_id': draft_id, 120 'author': author, 121 'comment': comment}) 122 123def request_reviewer_msg_to_json(draft_id, from_user, to_user): 124 return json.dumps({'draft_id': draft_id, 125 'from_user': from_user, 126 'to_user': to_user}) 127 128def guest_invitation_accepted_msg_to_json(invitation_id): 129 return json.dumps({'invitation_id': invitation_id}) 130 131def repo_transfer_msg_to_json(org_id, repo_owner, repo_id, repo_name): 132 """Encode repo transfer message to json string. 133 """ 134 return json.dumps({'org_id': org_id, 'repo_owner': repo_owner, 135 'repo_id': repo_id, 'repo_name': repo_name}) 136 137def get_cache_key_of_unseen_notifications(username): 138 return normalize_cache_key(username, 139 USER_NOTIFICATION_COUNT_CACHE_PREFIX) 140 141 142class UserNotificationManager(models.Manager): 143 def _add_user_notification(self, to_user, msg_type, detail): 144 """Add generic user notification. 145 146 Arguments: 147 - `self`: 148 - `username`: 149 - `detail`: 150 """ 151 n = super(UserNotificationManager, self).create( 152 to_user=to_user, msg_type=msg_type, detail=detail) 153 n.save() 154 155 cache_key = get_cache_key_of_unseen_notifications(to_user) 156 cache.delete(cache_key) 157 158 return n 159 160 def get_all_notifications(self, seen=None, time_since=None): 161 """Get all notifications of all users. 162 163 Arguments: 164 - `self`: 165 - `seen`: 166 - `time_since`: 167 """ 168 qs = super(UserNotificationManager, self).all() 169 if seen is not None: 170 qs = qs.filter(seen=seen) 171 if time_since is not None: 172 qs = qs.filter(timestamp__gt=time_since) 173 return qs 174 175 def get_user_notifications(self, username, seen=None): 176 """Get all notifications(group_msg, grpmsg_reply, etc) of a user. 177 178 Arguments: 179 - `self`: 180 - `username`: 181 """ 182 qs = super(UserNotificationManager, self).filter(to_user=username) 183 if seen is not None: 184 qs = qs.filter(seen=seen) 185 return qs 186 187 def remove_user_notifications(self, username): 188 """Remove all user notifications. 189 190 Arguments: 191 - `self`: 192 - `username`: 193 """ 194 self.get_user_notifications(username).delete() 195 196 def count_unseen_user_notifications(self, username): 197 """ 198 199 Arguments: 200 - `self`: 201 - `username`: 202 """ 203 return super(UserNotificationManager, self).filter( 204 to_user=username, seen=False).count() 205 206 def seen_user_msg_notices(self, to_user, from_user): 207 """Mark priv message notices of a user as seen. 208 """ 209 user_notices = super(UserNotificationManager, self).filter( 210 to_user=to_user, seen=False, msg_type=MSG_TYPE_USER_MESSAGE) 211 for notice in user_notices: 212 notice_from_user = notice.user_message_detail_to_dict().get('msg_from') 213 if from_user == notice_from_user: 214 if notice.seen is False: 215 notice.seen = True 216 notice.save() 217 218 def add_group_join_request_notice(self, to_user, detail): 219 """ 220 221 Arguments: 222 - `self`: 223 - `to_user`: 224 - `detail`: 225 """ 226 return self._add_user_notification(to_user, 227 MSG_TYPE_GROUP_JOIN_REQUEST, detail) 228 229 def set_add_user_to_group_notice(self, to_user, detail): 230 """ 231 232 Arguments: 233 - `self`: 234 - `to_user`: 235 - `detail`: 236 """ 237 return self._add_user_notification(to_user, 238 MSG_TYPE_ADD_USER_TO_GROUP, 239 detail) 240 241 def add_file_uploaded_msg(self, to_user, detail): 242 """ 243 244 Arguments: 245 - `self`: 246 - `to_user`: 247 - `file_name`: 248 - `upload_to`: 249 """ 250 return self._add_user_notification(to_user, 251 MSG_TYPE_FILE_UPLOADED, detail) 252 253 def add_repo_share_msg(self, to_user, detail): 254 """Notify ``to_user`` that others shared a repo to him/her. 255 256 Arguments: 257 - `self`: 258 - `to_user`: 259 - `repo_id`: 260 """ 261 return self._add_user_notification(to_user, 262 MSG_TYPE_REPO_SHARE, detail) 263 264 def add_repo_share_to_group_msg(self, to_user, detail): 265 """Notify ``to_user`` that others shared a repo to group. 266 267 Arguments: 268 - `self`: 269 - `to_user`: 270 - `detail`: 271 """ 272 return self._add_user_notification(to_user, 273 MSG_TYPE_REPO_SHARE_TO_GROUP, detail) 274 275 def add_user_message(self, to_user, detail): 276 """Notify ``to_user`` that others sent a message to him/her. 277 278 Arguments: 279 - `self`: 280 - `to_user`: 281 - `detail`: 282 """ 283 return self._add_user_notification(to_user, 284 MSG_TYPE_USER_MESSAGE, detail) 285 286 def add_file_comment_msg(self, to_user, detail): 287 """Notify ``to_user`` that others comment a file he can access. 288 """ 289 return self._add_user_notification(to_user, MSG_TYPE_FILE_COMMENT, detail) 290 291 def add_draft_comment_msg(self, to_user, detail): 292 """Notify ``to_user`` that review creator 293 """ 294 return self._add_user_notification(to_user, MSG_TYPE_DRAFT_COMMENT, detail) 295 296 def add_request_reviewer_msg(self, to_user, detail): 297 """Notify ``to_user`` that reviewer 298 """ 299 return self._add_user_notification(to_user, MSG_TYPE_DRAFT_REVIEWER, detail) 300 301 def add_guest_invitation_accepted_msg(self, to_user, detail): 302 """Nofity ``to_user`` that a guest has accpeted an invitation. 303 """ 304 return self._add_user_notification( 305 to_user, MSG_TYPE_GUEST_INVITATION_ACCEPTED, detail) 306 307 def add_repo_transfer_msg(self, to_user, detail): 308 """Nofity ``to_user`` that a library has been transfered to him/her. 309 """ 310 return self._add_user_notification( 311 to_user, MSG_TYPE_REPO_TRANSFER, detail) 312 313 314class UserNotification(models.Model): 315 to_user = LowerCaseCharField(db_index=True, max_length=255) 316 msg_type = models.CharField(db_index=True, max_length=30) 317 detail = models.TextField() 318 timestamp = models.DateTimeField(db_index=True, default=datetime.datetime.now) 319 seen = models.BooleanField('seen', default=False) 320 objects = UserNotificationManager() 321 322 class InvalidDetailError(Exception): 323 pass 324 325 class Meta: 326 ordering = ["-timestamp"] 327 328 def __unicode__(self): 329 return '%s|%s|%s' % (self.to_user, self.msg_type, self.detail) 330 331 def is_seen(self): 332 """Returns value of ``self.seen`` but also changes it to ``True``. 333 334 Use this in a template to mark an unseen notice differently the first 335 time it is shown. 336 337 Arguments: 338 - `self`: 339 """ 340 seen = self.seen 341 if seen is False: 342 self.seen = True 343 self.save() 344 return seen 345 346 def is_file_uploaded_msg(self): 347 """ 348 349 Arguments: 350 - `self`: 351 """ 352 return self.msg_type == MSG_TYPE_FILE_UPLOADED 353 354 def is_repo_share_msg(self): 355 """ 356 357 Arguments: 358 - `self`: 359 """ 360 return self.msg_type == MSG_TYPE_REPO_SHARE 361 362 def is_repo_share_to_group_msg(self): 363 """ 364 365 Arguments: 366 - `self`: 367 """ 368 return self.msg_type == MSG_TYPE_REPO_SHARE_TO_GROUP 369 370 def is_user_message(self): 371 """ 372 373 Arguments: 374 - `self`: 375 """ 376 return self.msg_type == MSG_TYPE_USER_MESSAGE 377 378 def is_group_join_request(self): 379 """ 380 381 Arguments: 382 - `self`: 383 """ 384 return self.msg_type == MSG_TYPE_GROUP_JOIN_REQUEST 385 386 def is_add_user_to_group(self): 387 """ 388 389 Arguments: 390 - `self`: 391 """ 392 return self.msg_type == MSG_TYPE_ADD_USER_TO_GROUP 393 394 def is_file_comment_msg(self): 395 return self.msg_type == MSG_TYPE_FILE_COMMENT 396 397 def is_draft_comment_msg(self): 398 return self.msg_type == MSG_TYPE_DRAFT_COMMENT 399 400 def is_draft_reviewer_msg(self): 401 return self.msg_type == MSG_TYPE_DRAFT_REVIEWER 402 403 def is_guest_invitation_accepted_msg(self): 404 return self.msg_type == MSG_TYPE_GUEST_INVITATION_ACCEPTED 405 406 def is_repo_transfer_msg(self): 407 return self.msg_type == MSG_TYPE_REPO_TRANSFER 408 409 def user_message_detail_to_dict(self): 410 """Parse user message detail, returns dict contains ``message`` and 411 ``msg_from``. 412 413 Arguments: 414 - `self`: 415 416 """ 417 assert self.is_user_message() 418 419 try: 420 detail = json.loads(self.detail) 421 except ValueError: 422 msg_from = self.detail 423 message = None 424 return {'message': message, 'msg_from': msg_from} 425 else: 426 message = detail['message'] 427 msg_from = detail['msg_from'] 428 return {'message': message, 'msg_from': msg_from} 429 430 ########## functions used in templates 431 def format_msg(self): 432 if self.is_file_uploaded_msg(): 433 return self.format_file_uploaded_msg() 434 elif self.is_repo_share_msg(): 435 return self.format_repo_share_msg() 436 elif self.is_repo_share_to_group_msg(): 437 return self.format_repo_share_to_group_msg() 438 elif self.is_group_join_request(): 439 return self.format_group_join_request() 440 elif self.is_file_comment_msg(): 441 return self.format_file_comment_msg() 442 elif self.is_draft_comment_msg(): 443 return self.format_draft_comment_msg() 444 elif self.is_draft_reviewer_msg(): 445 return self.format_draft_reviewer_msg() 446 elif self.is_guest_invitation_accepted_msg(): 447 return self.format_guest_invitation_accepted_msg() 448 elif self.is_add_user_to_group(): 449 return self.format_add_user_to_group() 450 elif self.is_repo_transfer_msg(): 451 return self.format_repo_transfer_msg() 452 else: 453 return '' 454 455 def format_file_uploaded_msg(self): 456 """ 457 458 Arguments: 459 - `self`: 460 """ 461 try: 462 d = json.loads(self.detail) 463 except Exception as e: 464 logger.error(e) 465 return _("Internal Server Error") 466 467 filename = d['file_name'] 468 repo_id = d['repo_id'] 469 repo = seafile_api.get_repo(repo_id) 470 if repo: 471 if d['uploaded_to'] == '/': 472 # current upload path is '/' 473 file_path = '/' + filename 474 link = reverse('lib_view', args=[repo_id, repo.name, '']) 475 name = repo.name 476 else: 477 uploaded_to = d['uploaded_to'].rstrip('/') 478 file_path = uploaded_to + '/' + filename 479 link = reverse('lib_view', args=[repo_id, repo.name, uploaded_to.lstrip('/')]) 480 name = os.path.basename(uploaded_to) 481 file_link = reverse('view_lib_file', args=[repo_id, file_path]) 482 483 msg = _("A file named <a href='%(file_link)s'>%(file_name)s</a> is uploaded to <a href='%(link)s'>%(name)s</a>") % { 484 'file_link': file_link, 485 'file_name': escape(filename), 486 'link': link, 487 'name': escape(name), 488 } 489 else: 490 msg = _("A file named <strong>%(file_name)s</strong> is uploaded to <strong>Deleted Library</strong>") % { 491 'file_name': escape(filename), 492 } 493 494 return msg 495 496 def format_repo_share_msg(self): 497 """ 498 499 Arguments: 500 - `self`: 501 """ 502 try: 503 d = json.loads(self.detail) 504 except Exception as e: 505 logger.error(e) 506 return _("Internal Server Error") 507 508 share_from = email2nickname(d['share_from']) 509 repo_id = d['repo_id'] 510 path = d.get('path', '/') 511 org_id = d.get('org_id', None) 512 repo = None 513 try: 514 if path == '/': 515 repo = seafile_api.get_repo(repo_id) 516 else: 517 if org_id: 518 owner = seafile_api.get_org_repo_owner(repo_id) 519 repo = seafile_api.get_org_virtual_repo( 520 org_id, repo_id, path, owner) 521 else: 522 owner = seafile_api.get_repo_owner(repo_id) 523 repo = seafile_api.get_virtual_repo(repo_id, path, owner) 524 525 except Exception as e: 526 logger.error(e) 527 return None 528 529 if repo is None: 530 self.delete() 531 return None 532 533 if path == '/': 534 tmpl = 'notifications/notice_msg/repo_share_msg.html' 535 else: 536 tmpl = 'notifications/notice_msg/folder_share_msg.html' 537 538 lib_url = reverse('lib_view', args=[repo.id, repo.name, '']) 539 msg = render_to_string(tmpl, { 540 'user': share_from, 541 'lib_url': lib_url, 542 'lib_name': repo.name, 543 }) 544 545 return msg 546 547 def format_repo_share_to_group_msg(self): 548 """ 549 550 Arguments: 551 - `self`: 552 """ 553 try: 554 d = json.loads(self.detail) 555 except Exception as e: 556 logger.error(e) 557 return _("Internal Server Error") 558 559 share_from = email2nickname(d['share_from']) 560 repo_id = d['repo_id'] 561 group_id = d['group_id'] 562 path = d.get('path', '/') 563 org_id = d.get('org_id', None) 564 565 repo = None 566 try: 567 group = ccnet_api.get_group(group_id) 568 if path == '/': 569 repo = seafile_api.get_repo(repo_id) 570 else: 571 if org_id: 572 owner = seafile_api.get_org_repo_owner(repo_id) 573 repo = seafile_api.get_org_virtual_repo( 574 org_id, repo_id, path, owner) 575 else: 576 owner = seafile_api.get_repo_owner(repo_id) 577 repo = seafile_api.get_virtual_repo(repo_id, path, owner) 578 except Exception as e: 579 logger.error(e) 580 return None 581 582 if not repo or not group: 583 self.delete() 584 return None 585 586 if path == '/': 587 tmpl = 'notifications/notice_msg/repo_share_to_group_msg.html' 588 else: 589 tmpl = 'notifications/notice_msg/folder_share_to_group_msg.html' 590 591 lib_url = reverse('lib_view', args=[repo.id, repo.name, '']) 592 group_url = reverse('group', args=[group.id]) 593 msg = render_to_string(tmpl, { 594 'user': share_from, 595 'lib_url': lib_url, 596 'lib_name': repo.name, 597 'group_url': group_url, 598 'group_name': group.group_name, 599 }) 600 601 return msg 602 603 def format_group_join_request(self): 604 """ 605 606 Arguments: 607 - `self`: 608 """ 609 try: 610 d = json.loads(self.detail) 611 except Exception as e: 612 logger.error(e) 613 return _("Internal Server Error") 614 615 username = d['username'] 616 group_id = d['group_id'] 617 join_request_msg = d['join_request_msg'] 618 619 group = ccnet_api.get_group(group_id) 620 if group is None: 621 self.delete() 622 return None 623 624 msg = _("User <a href='%(user_profile)s'>%(username)s</a> has asked to join group <a href='%(href)s'>%(group_name)s</a>, verification message: %(join_request_msg)s") % { 625 'user_profile': reverse('user_profile', args=[username]), 626 'username': username, 627 'href': HASH_URLS['GROUP_MEMBERS'] % {'group_id': group_id}, 628 'group_name': escape(group.group_name), 629 'join_request_msg': escape(join_request_msg), 630 } 631 return msg 632 633 def format_add_user_to_group(self): 634 """ 635 636 Arguments: 637 - `self`: 638 """ 639 try: 640 d = json.loads(self.detail) 641 except Exception as e: 642 logger.error(e) 643 return _("Internal Server Error") 644 645 group_staff = d['group_staff'] 646 group_id = d['group_id'] 647 648 group = ccnet_api.get_group(group_id) 649 if group is None: 650 self.delete() 651 return None 652 653 msg = _("User <a href='%(user_profile)s'>%(group_staff)s</a> has added you to group <a href='%(href)s'>%(group_name)s</a>") % { 654 'user_profile': reverse('user_profile', args=[group_staff]), 655 'group_staff': escape(email2nickname(group_staff)), 656 'href': reverse('group', args=[group_id]), 657 'group_name': escape(group.group_name)} 658 return msg 659 660 def format_file_comment_msg(self): 661 try: 662 d = json.loads(self.detail) 663 except Exception as e: 664 logger.error(e) 665 return _("Internal Server Error") 666 667 repo_id = d['repo_id'] 668 file_path = d['file_path'] 669 author = d['author'] 670 comment = d['comment'] 671 672 repo = seafile_api.get_repo(repo_id) 673 if repo is None or not seafile_api.get_file_id_by_path(repo.id, 674 file_path): 675 self.delete() 676 return None 677 678 file_name = os.path.basename(file_path) 679 msg = _("File <a href='%(file_url)s'>%(file_name)s</a> has a new comment from user %(author)s") % { 680 'file_url': reverse('view_lib_file', args=[repo_id, file_path]), 681 'file_name': escape(file_name), 682 'author': escape(email2nickname(author)), 683 } 684 return msg 685 686 def format_draft_comment_msg(self): 687 try: 688 d = json.loads(self.detail) 689 except Exception as e: 690 logger.error(e) 691 return _("Internal Server Error") 692 693 draft_id = d['draft_id'] 694 author = d['author'] 695 696 msg = _("<a href='%(file_url)s'>Draft #%(draft_id)s</a> has a new comment from user %(author)s") % { 697 'draft_id': draft_id, 698 'file_url': reverse('drafts:draft', args=[draft_id]), 699 'author': escape(email2nickname(author)), 700 } 701 return msg 702 703 def format_draft_reviewer_msg(self): 704 try: 705 d = json.loads(self.detail) 706 except Exception as e: 707 logger.error(e) 708 return _("Internal Server Error") 709 710 draft_id = d['draft_id'] 711 from_user = d['from_user'] 712 713 msg = _("%(from_user)s has sent you a request for <a href='%(file_url)s'>draft #%(draft_id)s</a>") % { 714 'draft_id': draft_id, 715 'file_url': reverse('drafts:draft', args=[draft_id]), 716 'from_user': escape(email2nickname(from_user)) 717 } 718 return msg 719 720 def format_guest_invitation_accepted_msg(self): 721 try: 722 d = json.loads(self.detail) 723 except Exception as e: 724 logger.error(e) 725 return _("Internal Server Error") 726 727 inv_id = d['invitation_id'] 728 try: 729 inv = Invitation.objects.get(pk=inv_id) 730 except Invitation.DoesNotExist: 731 self.delete() 732 return 733 734 # Use same msg as in notice_email.html, so there will be only one msg 735 # in django.po. 736 msg = _('Guest %(user)s accepted your <a href="%(url_base)s%(inv_url)s">invitation</a> at %(time)s.') % { 737 'user': inv.accepter, 738 'url_base': '', 739 'inv_url': settings.SITE_ROOT + '#invitations/', 740 'time': inv.accept_time.strftime("%Y-%m-%d %H:%M:%S"), 741 } 742 return msg 743 744 def format_repo_transfer_msg(self): 745 """ 746 747 Arguments: 748 - `self`: 749 """ 750 try: 751 d = json.loads(self.detail) 752 except Exception as e: 753 logger.error(e) 754 return _("Internal Server Error") 755 756 repo_owner_name = email2nickname(d['repo_owner']) 757 repo_id = d['repo_id'] 758 repo_name = d['repo_name'] 759 repo_url = reverse('lib_view', args=[repo_id, repo_name, '']) 760 msg = _('%(user)s has transfered a library named <a href="%(repo_url)s">%(repo_name)s</a> to you.') % { 761 'user': repo_owner_name, 762 'repo_url': repo_url, 763 'repo_name': repo_name, 764 } 765 return msg 766 767 768########## handle signals 769from django.dispatch import receiver 770 771from seahub.signals import upload_file_successful, comment_file_successful, repo_transfer 772from seahub.group.signals import group_join_request, add_user_to_group 773from seahub.share.signals import share_repo_to_user_successful, \ 774 share_repo_to_group_successful 775from seahub.invitations.signals import accept_guest_invitation_successful 776from seahub.drafts.signals import comment_draft_successful, \ 777 request_reviewer_successful 778 779@receiver(upload_file_successful) 780def add_upload_file_msg_cb(sender, **kwargs): 781 """Notify repo owner when others upload files to his/her folder from shared link. 782 """ 783 repo_id = kwargs.get('repo_id', None) 784 file_path = kwargs.get('file_path', None) 785 owner = kwargs.get('owner', None) 786 787 assert repo_id and file_path and owner is not None, 'Arguments error' 788 789 filename = os.path.basename(file_path) 790 folder_path = os.path.dirname(file_path) 791 792 detail = file_uploaded_msg_to_json(filename, repo_id, folder_path) 793 UserNotification.objects.add_file_uploaded_msg(owner, detail) 794 795@receiver(share_repo_to_user_successful) 796def add_share_repo_msg_cb(sender, **kwargs): 797 """Notify user when others share repos to him/her. 798 """ 799 from_user = kwargs.get('from_user', None) 800 to_user = kwargs.get('to_user', None) 801 repo = kwargs.get('repo', None) 802 path = kwargs.get('path', None) 803 org_id = kwargs.get('org_id', None) 804 805 assert from_user and to_user and repo and path is not None, 'Arguments error' 806 807 detail = repo_share_msg_to_json(from_user, repo.id, path, org_id) 808 UserNotification.objects.add_repo_share_msg(to_user, detail) 809 810@receiver(share_repo_to_group_successful) 811def add_share_repo_to_group_msg_cb(sender, **kwargs): 812 """Notify group member when others share repos to group. 813 """ 814 from_user = kwargs.get('from_user', None) 815 group_id = kwargs.get('group_id', None) 816 repo = kwargs.get('repo', None) 817 path = kwargs.get('path', None) 818 org_id = kwargs.get('org_id', None) 819 820 assert from_user and group_id and repo and path is not None, 'Arguments error' 821 822 members = ccnet_api.get_group_members(int(group_id)) 823 for member in members: 824 to_user = member.user_name 825 if to_user == from_user: 826 continue 827 detail = repo_share_to_group_msg_to_json(from_user, repo.id, group_id, path, org_id) 828 UserNotification.objects.add_repo_share_to_group_msg(to_user, detail) 829 830@receiver(group_join_request) 831def group_join_request_cb(sender, **kwargs): 832 staffs = kwargs['staffs'] 833 username = kwargs['username'] 834 group_id = kwargs['group'].id 835 join_request_msg = kwargs['join_request_msg'] 836 837 detail = group_join_request_to_json(username, group_id, 838 join_request_msg) 839 for staff in staffs: 840 UserNotification.objects.add_group_join_request_notice(to_user=staff, 841 detail=detail) 842 843@receiver(add_user_to_group) 844def add_user_to_group_cb(sender, **kwargs): 845 group_staff = kwargs['group_staff'] 846 group_id = kwargs['group_id'] 847 added_user = kwargs['added_user'] 848 849 detail = add_user_to_group_to_json(group_staff, 850 group_id) 851 852 UserNotification.objects.set_add_user_to_group_notice(to_user=added_user, 853 detail=detail) 854 855@receiver(comment_file_successful) 856def comment_file_successful_cb(sender, **kwargs): 857 """ send notification to file participants 858 """ 859 repo = kwargs['repo'] 860 repo_owner = kwargs['repo_owner'] 861 file_path = kwargs['file_path'] 862 comment = kwargs['comment'] 863 author = kwargs['author'] 864 865 notify_users = list_file_participants(repo.id, file_path) 866 notify_users = [x for x in notify_users if x != author] 867 for u in notify_users: 868 detail = file_comment_msg_to_json(repo.id, file_path, author, comment) 869 UserNotification.objects.add_file_comment_msg(u, detail) 870 871@receiver(comment_draft_successful) 872def comment_draft_successful_cb(sender, **kwargs): 873 draft = kwargs['draft'] 874 comment = kwargs['comment'] 875 author = kwargs['author'] 876 877 detail = draft_comment_msg_to_json(draft.id, author, comment) 878 879 if draft.username != author: 880 UserNotification.objects.add_draft_comment_msg(draft.username, detail) 881 882 reviewers = DraftReviewer.objects.filter(draft=draft) 883 for r in reviewers: 884 if r.reviewer != author: 885 UserNotification.objects.add_draft_comment_msg(r.reviewer, detail) 886 887@receiver(request_reviewer_successful) 888def requeset_reviewer_successful_cb(sender, **kwargs): 889 from_user = kwargs['from_user'] 890 draft_id = kwargs['draft_id'] 891 to_user = kwargs['to_user'] 892 893 detail = request_reviewer_msg_to_json(draft_id, from_user, to_user) 894 895 UserNotification.objects.add_request_reviewer_msg(to_user, detail) 896 897@receiver(accept_guest_invitation_successful) 898def accept_guest_invitation_successful_cb(sender, **kwargs): 899 inv_obj = kwargs['invitation_obj'] 900 901 detail = guest_invitation_accepted_msg_to_json(inv_obj.pk) 902 UserNotification.objects.add_guest_invitation_accepted_msg( 903 inv_obj.inviter, detail) 904 905@receiver(repo_transfer) 906def repo_transfer_cb(sender, **kwargs): 907 908 org_id = kwargs['org_id'] 909 repo_owner = kwargs['repo_owner'] 910 to_user = kwargs['to_user'] 911 repo_id = kwargs['repo_id'] 912 repo_name = kwargs['repo_name'] 913 914 detail = repo_transfer_msg_to_json(org_id, repo_owner, repo_id, repo_name) 915 UserNotification.objects.add_repo_transfer_msg(to_user, detail) 916