1# Copyright (c) 2012-2016 Seafile Ltd. 2import logging 3 4from django.conf import settings 5from django.db import models, IntegrityError 6from django.core.cache import cache 7from django.dispatch import receiver 8from django.core.exceptions import MultipleObjectsReturned 9 10from seahub.base.fields import LowerCaseCharField 11from seahub.profile.settings import EMAIL_ID_CACHE_PREFIX, EMAIL_ID_CACHE_TIMEOUT 12from seahub.institutions.models import Institution 13from registration.signals import user_registered 14from seahub.signals import institution_deleted 15from seahub.institutions.models import InstitutionAdmin 16 17# Get an instance of a logger 18logger = logging.getLogger(__name__) 19 20class DuplicatedContactEmailError(Exception): 21 pass 22 23 24class ProfileManager(models.Manager): 25 def add_or_update(self, username, nickname=None, intro=None, lang_code=None, 26 login_id=None, contact_email=None, institution=None, list_in_address_book=None): 27 """Add or update user profile. 28 """ 29 try: 30 profile = self.get(user=username) 31 except Profile.DoesNotExist: 32 profile = self.model(user=username) 33 34 if nickname is not None: 35 nickname = nickname.strip() 36 profile.nickname = nickname 37 if intro is not None: 38 profile.intro = intro 39 if lang_code is not None: 40 profile.lang_code = lang_code 41 if login_id is not None: 42 login_id = login_id.strip() 43 profile.login_id = login_id 44 if contact_email is not None: 45 contact_email = contact_email.strip() 46 profile.contact_email = contact_email 47 if institution is not None: 48 institution = institution.strip() 49 profile.institution = institution 50 if list_in_address_book is not None: 51 profile.list_in_address_book = list_in_address_book.lower() == 'true' 52 53 try: 54 profile.save(using=self._db) 55 return profile 56 except IntegrityError: 57 raise DuplicatedContactEmailError 58 59 def update_contact_email(self, username, contact_email): 60 """ 61 update contact_email of profile 62 """ 63 try: 64 profile = self.get(user=username) 65 profile.contact_email = contact_email 66 except Profile.DoesNotExist: 67 logger.warn('%s profile does not exists' % username) 68 return None 69 70 try: 71 profile.save(using=self._db) 72 return profile 73 except IntegrityError: 74 raise DuplicatedContactEmailError 75 76 def get_profile_by_user(self, username): 77 """Get a user's profile. 78 """ 79 try: 80 return super(ProfileManager, self).get(user=username) 81 except Profile.DoesNotExist: 82 return None 83 84 def get_profile_by_contact_email(self, contact_email): 85 res = super(ProfileManager, self).filter(contact_email=contact_email) 86 if len(res) > 0: 87 if len(res) > 1: 88 logger.warning('Repeated contact email %s' % contact_email) 89 return res[0] 90 else: 91 return None 92 93 def get_contact_email_by_user(self, username): 94 """Get a user's contact email, use username(login email) if not found. 95 """ 96 p = self.get_profile_by_user(username) 97 if p and p.contact_email: 98 return p.contact_email 99 100 return username 101 102 def get_username_by_login_id(self, login_id): 103 """Convert a user's login id to username(login email). 104 """ 105 if not login_id: 106 return None 107 108 try: 109 return super(ProfileManager, self).get(login_id=login_id).user 110 except Profile.DoesNotExist: 111 return None 112 113 def get_username_by_contact_email(self, contact_email): 114 """Convert a user's contact_email to username(login email). 115 """ 116 if not contact_email: 117 return None 118 119 try: 120 return super(ProfileManager, self).get(contact_email=contact_email).user 121 except Profile.DoesNotExist: 122 return None 123 124 def convert_login_str_to_username(self, login_str): 125 """ 126 Convert login id or contact email to username(login email). 127 Use login_str if both login id and contact email are not set. 128 """ 129 username = self.get_username_by_login_id(login_str) 130 if username is None: 131 username = self.get_username_by_contact_email(login_str) 132 if username is None: 133 return login_str 134 else: 135 return username 136 else: 137 return username 138 139 def get_user_language(self, username): 140 """Get user's language from profile. Return default language code if 141 user has no preferred language. 142 143 Arguments: 144 - `self`: 145 - `username`: 146 """ 147 try: 148 profile = self.get(user=username) 149 if profile.lang_code is not None: 150 return profile.lang_code 151 else: 152 return settings.LANGUAGE_CODE 153 except Profile.DoesNotExist: 154 return settings.LANGUAGE_CODE 155 156 def delete_profile_by_user(self, username): 157 self.filter(user=username).delete() 158 159class Profile(models.Model): 160 user = models.EmailField(unique=True) 161 nickname = models.CharField(max_length=64, blank=True) 162 intro = models.TextField(max_length=256, blank=True) 163 lang_code = models.TextField(max_length=50, null=True, blank=True) 164 # Login id can be email or anything else used to login. 165 login_id = models.CharField(max_length=225, unique=True, null=True, blank=True) 166 # Contact email is used to receive emails. 167 contact_email = models.EmailField(max_length=225, unique=True, null=True, blank=True) 168 institution = models.CharField(max_length=225, db_index=True, null=True, blank=True, default='') 169 list_in_address_book = models.BooleanField(default=False, db_index=True) 170 objects = ProfileManager() 171 172 def set_lang_code(self, lang_code): 173 self.lang_code = lang_code 174 self.save() 175 176class DetailedProfileManager(models.Manager): 177 def add_detailed_profile(self, username, department, telephone): 178 d_profile = self.model(user=username, department=department, 179 telephone=telephone) 180 d_profile.save(using=self._db) 181 return d_profile 182 183 def add_or_update(self, username, department, telephone): 184 try: 185 d_profile = self.get(user=username) 186 187 if department is not None: 188 d_profile.department = department 189 if telephone is not None: 190 d_profile.telephone = telephone 191 192 except DetailedProfile.DoesNotExist: 193 d_profile = self.model(user=username, department=department, 194 telephone=telephone) 195 d_profile.save(using=self._db) 196 return d_profile 197 198 def get_detailed_profile_by_user(self, username): 199 """Get a user's profile. 200 """ 201 ret = list(super(DetailedProfileManager, self).filter(user=username)) 202 if len(ret) == 0: 203 return None 204 elif len(ret) == 1: 205 return ret[0] 206 else: 207 # XXX: got multiple records, delete them all. 208 super(DetailedProfileManager, self).filter(user=username).delete() 209 logger.warn('Remove multiple detailed profile records for user %s' % username) 210 return None 211 212class DetailedProfile(models.Model): 213 user = LowerCaseCharField(max_length=255, db_index=True) 214 department = models.CharField(max_length=512) 215 telephone = models.CharField(max_length=100) 216 objects = DetailedProfileManager() 217 218 219########## signal handlers 220from django.db.models.signals import post_save 221from .utils import refresh_cache 222 223@receiver(user_registered) 224def clean_email_id_cache(sender, **kwargs): 225 from seahub.utils import normalize_cache_key 226 227 user = kwargs['user'] 228 key = normalize_cache_key(user.email, EMAIL_ID_CACHE_PREFIX) 229 cache.set(key, user.id, EMAIL_ID_CACHE_TIMEOUT) 230 231@receiver(post_save, sender=Profile, dispatch_uid="update_profile_cache") 232def update_profile_cache(sender, instance, **kwargs): 233 """ 234 Set profile data to cache when profile data change. 235 """ 236 refresh_cache(instance.user) 237 238@receiver(institution_deleted) 239def remove_user_for_inst_deleted(sender, **kwargs): 240 inst_name = kwargs.get("inst_name", "") 241 Profile.objects.filter(institution=inst_name).update(institution="") 242 InstitutionAdmin.objects.filter(institution__name=inst_name).delete() 243 244