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