class Role(dd.Model, Addressable): class Meta(object): app_label = 'contacts' # avoid RemovedInDjango19Warning abstract = dd.is_abstract_model(__name__, 'Role') verbose_name = _("Contact person") verbose_name_plural = _("Contact persons") type = dd.ForeignKey('contacts.RoleType', blank=True, null=True) person = dd.ForeignKey("contacts.Person", related_name='rolesbyperson') company = dd.ForeignKey("contacts.Company", related_name='rolesbycompany') if with_roles_history: start_date = models.DateField(verbose_name=_("Start date"), blank=True, null=True) end_date = models.DateField(verbose_name=_("End date"), blank=True, null=True) else: start_date = dd.DummyField() end_date = dd.DummyField() def __str__(self): if self.person_id is None: return super(Role, self).__str__() if self.type is None: return str(self.person) return "%s (%s)" % (self.person, self.type) def address_person_lines(self): if self.company: for ln in self.company.address_person_lines(): yield ln for ln in self.person.address_person_lines(): yield ln def address_location_lines(self): if self.company_id: return self.company.address_location_lines() if self.person_id: return self.person.address_location_lines() return super(Role, self).__str__() def get_print_language(self): if self.company_id: return self.company.language if self.person_id: return self.person.language return super(Role, self).get_print_language() @dd.chooser() def person_choices(cls): # needed to activate create_person_choice return rt.models.contacts.Person.objects.all() def create_person_choice(self, text): return rt.models.contacts.Person.create_from_choice(text)
class Product(mixins.BabelNamed): """A product is something you can sell or buy. .. attribute:: description .. attribute:: cat .. attribute:: delivery_unit """ class Meta: app_label = 'products' verbose_name = _("Product") verbose_name_plural = _("Products") abstract = dd.is_abstract_model(__name__, 'Product') description = dd.BabelTextField(verbose_name=_("Long description"), blank=True, null=True) cat = models.ForeignKey(ProductCat, verbose_name=_("Category"), blank=True, null=True) delivery_unit = DeliveryUnit.field( default=DeliveryUnit.piece.as_callable()) if vat: vat_class = vat.VatClasses.field(blank=True) else: vat_class = dd.DummyField()
class Change(dd.Model): """A registered change in the database. Each database change of a watched object will generate one Change record. .. attribute:: master The database object which acts as "master". .. attribute:: object The database object which has been modified. """ class Meta(object): verbose_name = _("Change") verbose_name_plural = _("Changes") # allow_cascaded_delete = 'master' quick_search_fields = 'changed_fields diff' show_in_site_search = False time = models.DateTimeField() type = ChangeTypes.field() if settings.SITE.user_model: user = dd.ForeignKey(settings.SITE.user_model) else: user = dd.DummyField() object_type = dd.ForeignKey('contenttypes.ContentType', blank=True, null=True, verbose_name=_("Object type"), related_name='changes_by_object') object_id = GenericForeignKeyIdField(object_type, blank=True, null=True) object = GenericForeignKey('object_type', 'object_id', _("Object")) master_type = dd.ForeignKey('contenttypes.ContentType', blank=True, null=True, verbose_name=_("Master type"), related_name='changes_by_master') master_id = GenericForeignKeyIdField(master_type, blank=True, null=True) master = GenericForeignKey('master_type', 'master_id', _("Master")) diff = dd.RichTextField(_("Changes"), format='plain', blank=True, editable=False) changed_fields = dd.CharField(_("Fields"), max_length=250, blank=True) def __str__(self): # ~ return "#%s - %s" % (self.id,self.time) return "#%s" % self.id
class Product(mixins.BabelNamed, mixins.Referrable): class Meta: verbose_name = _("Product") verbose_name_plural = _("Products") abstract = dd.is_abstract_model(__name__, 'Product') description = dd.BabelTextField(verbose_name=_("Long description"), blank=True, null=True) cat = models.ForeignKey(ProductCat, verbose_name=_("Category"), blank=True, null=True) if vat: vat_class = vat.VatClasses.field(blank=True) else: vat_class = dd.DummyField()
def inject_partner_field(sender=None, **kwargs): User = sender.models.users.User if dd.is_installed('contacts'): Partner = sender.models.contacts.Partner if not issubclass(User, Partner): dd.inject_field( User, 'partner', dd.ForeignKey('contacts.Partner', blank=True, null=True, related_name='users_by_partner', on_delete=models.PROTECT)) # a related_name is needed so that Avanti can have aClient # who inherits from both Partner and UserAuthored return dd.inject_field(User, 'partner', dd.DummyField())
class LanguageKnowledge(dd.Model): class Meta: abstract = dd.is_abstract_model(__name__, 'LanguageKnowledge') app_label = 'cv' verbose_name = _("Language knowledge") verbose_name_plural = _("Language knowledges") unique_together = ['person', 'language', 'entry_date'] \ if config.with_language_history else ['person', 'language'] ordering = ['-entry_date', '-id'] \ if config.with_language_history else ['id'] allow_cascaded_delete = ['person'] person = dd.ForeignKey(config.person_model) language = dd.ForeignKey("languages.Language") spoken = HowWell.field(_("Spoken"), blank=True) written = HowWell.field(_("Written"), blank=True) spoken_passively = HowWell.field(_("Spoken (passively)"), blank=True) written_passively = HowWell.field(_("Written (passively)"), blank=True) native = models.BooleanField(_("Mother tongue"), default=False) cef_level = CefLevel.field(blank=True) # ,null=True) has_certificate = models.BooleanField(_("Certificate"), default=False) if config.with_language_history: entry_date = models.DateField(_("Entry date"), default=dd.today) else: entry_date = dd.DummyField() def __str__(self): if self.language_id is None: return '' if self.native: return _("%s (MT)") % (self.language) if self.cef_level: return u"%s (%s)" % (self.language, self.cef_level) if self.spoken: if self.written: return _(u"%s (s/w)") % self.language else: return _(u"%s (s)") % self.language elif self.written: return _(u"%s (w)") % self.language else: return str(self.language)
class TimezoneHolder(models.Model): """Mixin for database models which have a :attr:`timezone` field. .. attribute:: timezone The timezone. """ class Meta(object): abstract = True if settings.USE_TZ: timezone = models.CharField(_("Time zone"), max_length=15, blank=True) else: timezone = dd.DummyField() @dd.chooser(simple_values=True) def timezone_choices(cls, partner): import pytz if partner and partner.country: return pytz.country_timezones[partner.country.isocode] return pytz.common_timezones
class User(CreatedModified): """Represents a user of this site. .. attribute:: username The primary key. .. attribute:: profile The profile of a user is what defines her or his permissions. Users with an empty `profile` field are considered inactive and cannot log in. .. attribute:: partner Pointer to the :class:`lino.modlib.contacts.models.Partner` instance related to this user. This is a DummyField when :mod:`lino.modlib.contacts` is not installed. """ class Meta: verbose_name = _('User') verbose_name_plural = _('Users') abstract = dd.is_abstract_model(__name__, 'User') ordering = ['last_name', 'first_name', 'username'] preferred_foreignkey_width = 15 hidden_columns = 'password remarks' authenticated = True """This is always `True`. See also :attr:`lino.modlib.users.utils.AnonymousUser.authenticated`. """ username = models.CharField(_('Username'), max_length=30, unique=True, help_text=_("""Required. Must be unique.""")) password = models.CharField(_('Password'), max_length=128) profile = UserProfiles.field( blank=True, help_text=_("Users with an empty `profile` field are considered " "inactive and cannot log in.")) initials = models.CharField(_('Initials'), max_length=10, blank=True) first_name = models.CharField(_('First name'), max_length=30, blank=True) last_name = models.CharField(_('Last name'), max_length=30, blank=True) email = models.EmailField(_('e-mail address'), blank=True) remarks = models.TextField(_("Remarks"), blank=True) # ,null=True) language = dd.LanguageField(default=models.NOT_PROVIDED, blank=True) if dd.is_installed('contacts'): partner = models.ForeignKey('contacts.Partner', blank=True, null=True, on_delete=models.PROTECT) else: partner = dd.DummyField() def __unicode__(self): return self.get_full_name() def get_full_name(self): "Returns the first_name plus the last_name, with a space in between." if not self.first_name and not self.last_name: return self.username return u'%s %s' % (self.first_name.strip(), self.last_name.strip()) @dd.displayfield(_("Name"), max_length=15) def name_column(self, request): #~ return join_words(self.last_name.upper(),self.first_name) return unicode(self) if dd.is_installed('contacts'): def get_person(self): if self.partner: return self.partner.get_mti_child('person') else: def get_person(self): return None person = property(get_person) def get_row_permission(self, ar, state, ba): """ Only system managers may edit other users. See also :meth:`User.disabled_fields`. """ #~ print 20120621, self, user, state, action if not ba.action.readonly: user = ar.get_user() if user != self: if not isinstance(user.profile.role, dd.SiteAdmin): return False return super(User, self).get_row_permission(ar, state, ba) #~ return False def disabled_fields(self, ar): """ Only System admins may change the `profile` of users. See also :meth:`Users.get_row_permission`. """ rv = super(User, self).disabled_fields(ar) if not isinstance(ar.get_user().profile.role, dd.SiteAdmin): rv.add('profile') return rv def full_clean(self, *args, **kw): p = self.person if p is not None: for n in ('first_name', 'last_name', 'email', 'language'): if not getattr(self, n): setattr(self, n, getattr(p, n)) #~ self.language = p.language if not self.language: #~ self.language = settings.SITE.DEFAULT_LANGUAGE.django_code self.language = settings.SITE.get_default_language() if not self.password: self.set_unusable_password() if not self.initials: if self.first_name and self.last_name: self.initials = self.first_name[0] + self.last_name[0] super(User, self).full_clean(*args, **kw) def get_received_mandates(self): #~ return [ [u.id,_("as %s")%u] for u in self.__class__.objects.all()] return [[u.id, unicode(u)] for u in self.__class__.objects.all()] #~ return self.__class__.objects.all() change_password = ChangePassword() # the following methods are unchanged copies from Django's User # model def set_password(self, raw_password): self.password = make_password(raw_password) def check_password(self, raw_password): """ Returns a boolean of whether the raw_password was correct. Handles hashing formats behind the scenes. """ def setter(raw_password): self.set_password(raw_password) self.save() return check_password(raw_password, self.password, setter) def set_unusable_password(self): # Sets a value that will never be a valid hash self.password = make_password(None) def has_usable_password(self): return is_password_usable(self.password) def as_list_item(self, ar): return E.li(E.strong(self.username), ' : ', unicode(self), ', ', unicode(self.profile), ', ', E.strong(settings.SITE.LANGUAGE_DICT.get(self.language))) @classmethod def get_by_username(cls, username, default=models.NOT_PROVIDED): """ `User.get_by_username(x)` is equivalent to `User.objects.get(username=x)` except that the text of the DoesNotExist exception is more useful. """ try: return cls.objects.get(username=username) except cls.DoesNotExist: if default is models.NOT_PROVIDED: raise cls.DoesNotExist( "No %s with username %r" % (unicode(cls._meta.verbose_name), username)) return default
class Role(dd.Model, Addressable): class Meta(object): app_label = 'contacts' # avoid RemovedInDjango19Warning abstract = dd.is_abstract_model(__name__, 'Role') verbose_name = _("Contact Person") verbose_name_plural = _("Contact Persons") type = dd.ForeignKey('contacts.RoleType', blank=True, null=True) person = dd.ForeignKey("contacts.Person", related_name='rolesbyperson') company = dd.ForeignKey("contacts.Company", related_name='rolesbycompany') if with_roles_history: start_date = models.DateField(verbose_name=_("Start date"), blank=True, null=True) end_date = models.DateField(verbose_name=_("End date"), blank=True, null=True) else: start_date = dd.DummyField() end_date = dd.DummyField() def __str__(self): if self.person_id is None: return super(Role, self).__str__() if self.type is None: return str(self.person) return u"%s (%s)" % (self.person, self.type) def address_person_lines(self): if self.company: for ln in self.company.address_person_lines(): yield ln for ln in self.person.address_person_lines(): yield ln def address_location_lines(self): if self.company_id: return self.company.address_location_lines() if self.person_id: return self.person.address_location_lines() return super(Role, self).__str__() def get_print_language(self): if self.company_id: return self.company.language if self.person_id: return self.person.language return super(Role, self).get_print_language() @dd.chooser() def person_choices(cls): # needed to activate create_person_choice return rt.models.contacts.Person.objects.all() def create_person_choice(self, text): """ Called when an unknown person name was given. If the given text looks like a full name of a person, create it. """ person_model = rt.models.contacts.Person if person_model.disable_create_choice: return try: values = person_model.parse_to_dict(text) except Exception as e: raise ValidationError( _("Could not create {person} from '{text}'").format( person=person_model._meta.verbose_name, text=text)) obj = person_model(**values) obj.full_clean() obj.save() return obj
class User(AbstractBaseUser, Contactable, CreatedModified, DateRange, Printable): class Meta(object): app_label = 'users' verbose_name = _('User') verbose_name_plural = _('Users') abstract = dd.is_abstract_model(__name__, 'User') ordering = ['last_name', 'first_name', 'username'] USERNAME_FIELD = 'username' _anon_user = None objects = UserManager() preferred_foreignkey_width = 15 hidden_columns = 'password remarks' authenticated = True quick_search_fields = 'username user_type first_name last_name remarks' # seems that Django doesn't like nullable username # username = dd.NullCharField(_('Username'), max_length=30, unique=True) username = models.CharField(_('Username'), max_length=30, unique=True) user_type = UserTypes.field(blank=True) initials = models.CharField(_('Initials'), max_length=10, blank=True) first_name = models.CharField(_('First name'), max_length=30, blank=True) last_name = models.CharField(_('Last name'), max_length=30, blank=True) remarks = models.TextField(_("Remarks"), blank=True) # ,null=True) if settings.USE_TZ: time_zone = TimeZones.field(default='default') else: time_zone = dd.DummyField() change_password = ChangePassword() # sign_in = SignIn() sign_out = SignOut() def __str__(self): return self.get_full_name() @property def is_active(self): if self.start_date and self.start_date > dd.today(): return False if self.end_date and self.end_date < dd.today(): return False return True def get_as_user(self): """ Overrides :meth:`lino_xl.lib.contacts.Partner.get_as_user`. """ return self def get_full_name(self): if not self.first_name and not self.last_name: return self.initials or self.username or str(self.pk) return u'{} {}'.format(self.first_name, self.last_name).strip() @dd.displayfield(_("Name"), max_length=15) def name_column(self, request): # return join_words(self.last_name.upper(),self.first_name) return str(self) # @dd.displayfield(_("Other authentication providers")) # def social_auth_links(self, ar=None): # return settings.SITE.get_social_auth_links () # # elems = [] # # for backend in get_social_auth_backends() # # elems.append(E.a("foo")) # # return E.p(elems) def get_person(self): if self.partner: return self.partner.get_mti_child('person') person = property(get_person) def is_editable_by_all(self): return False def get_row_permission(self, ar, state, ba): #~ print 20120621, self, user, state, action # import pdb ; pdb.set_trace() if not ba.action.readonly: user = ar.get_user() if user != self: if not user.user_type.has_required_roles([SiteAdmin]): if not self.is_editable_by_all(): return False return super(User, self).get_row_permission(ar, state, ba) #~ return False def disabled_fields(self, ar): """ Only System admins may change the `user_type` of users. See also :meth:`Users.get_row_permission`. """ rv = super(User, self).disabled_fields(ar) user = ar.get_user() if not user.user_type.has_required_roles([SiteAdmin]): rv.add('send_email') rv.add('user_type') if user != self: rv.add('change_password') return rv def full_clean(self, *args, **kw): p = self.get_person() if p is not None and p != self: for n in ('first_name', 'last_name', 'email', 'language'): if not getattr(self, n): setattr(self, n, getattr(p, n)) #~ self.language = p.language if not self.language: #~ self.language = settings.SITE.DEFAULT_LANGUAGE.django_code self.language = settings.SITE.get_default_language() if not self.password: self.set_unusable_password() # if not self.initials: # if self.first_name and self.last_name: # self.initials = self.first_name[0] + self.last_name[0] super(User, self).full_clean(*args, **kw) def on_create(self, ar): self.start_date = dd.today() super(User, self).on_create(ar) def get_received_mandates(self): #~ return [ [u.id,_("as %s")%u] for u in self.__class__.objects.all()] return [[u.id, str(u)] for u in self.__class__.objects.all()] #~ return self.__class__.objects.all() # @dd.htmlbox(_("Welcome")) # def welcome_email_body(self, ar): # # return join_words(self.last_name.upper(),self.first_name) # return self.get_welcome_email_body(ar) def get_welcome_email_body(self, ar): template = rt.get_template('users/welcome_email.eml') context = self.get_printable_context(ar) # dict(obj=self, E=E, rt=rt) return template.render(**context) def as_list_item(self, ar): pv = dict(username=self.username) if settings.SITE.is_demo_site: pv.update(password='******') btn = rt.models.users.UsersOverview.get_action_by_name('sign_in') # print btn.get_row_permission(ar, None, None) btn = btn.request(action_param_values=pv, renderer=settings.SITE.kernel.default_renderer) btn = btn.ar2button(label=self.username) items = [btn, ' : ', str(self), ', ', str(self.user_type)] if self.language: items += [ ', ', E.strong(str(settings.SITE.LANGUAGE_DICT.get(self.language))) ] return E.li(*items) # if settings.SITE.is_demo_site: # p = "'{0}', '{1}'".format(self.username, '1234') # else: # p = "'{0}'".format(self.username) # url = "javascript:Lino.show_login_window(null, {0})".format(p) # return E.li(E.a(self.username, href=url), ' : ', # str(self), ', ', # str(self.user_type), ', ', # E.strong(settings.SITE.LANGUAGE_DICT.get(self.language))) @classmethod def get_by_username(cls, username, default=models.NOT_PROVIDED): """ `User.get_by_username(x)` is equivalent to `User.objects.get(username=x)` except that the text of the DoesNotExist exception is more useful. """ try: return cls.objects.get(username=username) except cls.DoesNotExist: if default is models.NOT_PROVIDED: raise cls.DoesNotExist("No %s with username %r" % (str(cls._meta.verbose_name), username)) return default def get_preferences(self): """ Return the preferences of this user. The returned object is a :class:`lino.core.userprefs.UserPrefs` object. """ return userprefs.reg.get(self) @classmethod def get_anonymous_user(cls): return AnonymousUser()
class MailableType(dd.Model): email_template = dd.DummyField() attach_to_email = dd.DummyField() class Meta: abstract = True
# -*- coding: UTF-8 -*- # Copyright 2012-2014 Luc Saffre # # License: BSD (see file COPYING for details) """ The :term:`dummy module` for `outbox`, used by :func:`lino.core.utils.resolve_app`. """ from lino.api import dd class Mailable(object): pass #~ class MailableType(object): pass class MailableType(dd.Model): email_template = dd.DummyField() attach_to_email = dd.DummyField() class Meta: abstract = True MailsByController = dd.DummyField()
class Session(UserAuthored, Started, Ended, Workable): class Meta: app_label = 'working' verbose_name = _("Session") verbose_name_plural = _('Sessions') abstract = dd.is_abstract_model(__name__, 'Session') ticket = dd.ForeignKey(dd.plugins.working.ticket_model, related_name="sessions_by_ticket") session_type = dd.ForeignKey('working.SessionType', null=True, blank=True) summary = models.CharField(_("Summary"), max_length=200, blank=True, help_text=_("Summary of the session.")) description = dd.RichTextField(_("Description"), blank=True) # break_time = models.TimeField( # blank=True, null=True, # verbose_name=_("Break Time")) break_time = dd.DurationField(_("Break Time"), blank=True, null=True) faculty = dd.ForeignKey('skills.Skill', related_name="sessions_by_faculty", blank=True, null=True) reporting_type = ReportingTypes.field(blank=True) is_fixing = models.BooleanField(_("Fixing"), default=False) if settings.USE_TZ: time_zone = TimeZones.field() else: time_zone = dd.DummyField() end_session = EndThisSession() show_today = ShowMySessionsByDay('start_date') # print_activity_report = PrintActivityReport() def __str__(self): if self.start_time and self.end_time: return u"%s %s-%s" % ( self.start_date.strftime(settings.SITE.date_format_strftime), self.start_time.strftime(settings.SITE.time_format_strftime), self.end_time.strftime(settings.SITE.time_format_strftime)) return "%s # %s" % (self._meta.verbose_name, self.pk) def get_ticket(self): return self.ticket def on_create(self, ar): super(Session, self).on_create(ar) if settings.USE_TZ: self.time_zone = self.user.time_zone or \ rt.models.about.TimeZones.default def get_time_zone(self): return self.time_zone def full_clean(self, *args, **kwargs): if self.user_id and not self.time_zone: # can be removed when all production sites have migrated: self.time_zone = self.user.time_zone or \ rt.models.about.TimeZones.default if not settings.SITE.loading_from_dump: if self.start_time is None: self.set_datetime('start', timezone.now()) # value = timezone.now() # if pytz: # tz = pytz.timezone(self.get_timezone()) # value = value.astimezone(tz) # self.start_time = value.time() if self.start_date is None: self.start_date = dd.today() # if self.ticket_id is not None and self.faculty_id is None: # self.faculty = self.ticket.faculty if self.end_time is not None: if self.end_date is None: self.end_date = self.start_date if self.ticket_id: self.ticket.on_worked(self) super(Session, self).full_clean(*args, **kwargs) def unused_save(self, *args, **kwargs): if not settings.SITE.loading_from_dump: if self.start_date is None: self.start_date = dd.today() if self.start_time is None: self.start_time = timezone.now().time() super(Session, self).save(*args, **kwargs) def get_reporting_type(self): if self.reporting_type: return self.reporting_type t = self.get_ticket() if t.ticket_type and t.ticket_type.reporting_type: return t.ticket_type.reporting_type if t.site and t.site.reporting_type: return t.site.reporting_type # if t.project and t.project.reporting_type: # return t.project.reporting_type return dd.plugins.working.default_reporting_type # def after_ui_save(self, ar, cw): # super(Session, self).after_ui_save(ar, cw) # if self.ticket_id: # self.ticket.on_worked(self, ar, cw) def get_root_project(self): """Return the root project for this session (or None if session has no ticket). """ if self.ticket and self.ticket.project: return self.ticket.project.get_parental_line()[0] def get_duration(self): """Return the duration in hours as a :class:`lino.utils.quantities.Quantity`. This inherits from :meth:`StartedEnded <lino_xl.lib.cal.mixins.StartedEnded.get_duration>` but removes :attr:`break_time` if specified. """ diff = super(Session, self).get_duration() if diff and self.break_time: diff -= self.break_time return diff # if self.end_time is None: # diff = datetime.timedelta() # else: # diff = self.get_datetime('end') - self.get_datetime('start') # if self.break_time is not None: # diff -= self.break_time # return Duration(diff) @dd.displayfield(_("Ticket #")) def ticket_no(self, ar): if ar is None: return self.ticket_id return self.ticket.obj2href(ar) # self.ticket_id) @dd.displayfield(_("Site")) def site_ref(self, ar): if not self.ticket: return '' site = self.ticket.site if site is None: return '' if ar is None: return str(site) return site.obj2href(ar)
("theme-crisp", "Theme Crisp"), ("theme-crisp-touch", "Theme crisp touch"), ("theme-gray", "Theme gray"), ("theme-neptune", "Theme neptune"), ("theme-neptune-touch", "Theme neptune touch"), ("theme-triton", "Theme triton"), ) if dd.plugins.extjs.select_theme: dd.inject_field( 'users.User', 'preferred_theme', models.CharField(_("Preferred theme"), choices=EXTJS6_THEMES_CHOICES, default="", blank=True, max_length=25)) else: dd.inject_field('users.User', 'preferred_theme', dd.DummyField()) class ThemedUserDetail(UserDetail): box1 = """ username user_type:20 partner first_name last_name initials email language time_zone preferred_theme id created modified """ Users.set_detail_layout(ThemedUserDetail())
class User(AbstractBaseUser, Contactable, CreatedModified, TimezoneHolder): """Represents a user of this site. .. attribute:: username Must be unique. Leaving this empty means that the user cannot log in. .. attribute:: profile The profile of a user is what defines her or his permissions. Users with an empty `profile` field are considered inactive and cannot log in. .. attribute:: partner Pointer to the :class:`Partner <lino_xl.lib.contacts.models.Partner>` instance related to this user. This is a DummyField when :mod:`lino_xl.lib.contacts` is not installed. .. attribute:: person A virtual read-only field which returns the :class:`Person <lino_xl.lib.contacts.models.Person>` MTI child corresponding to the :attr:`partner` (if it exists) and otherwise `None`. .. attribute:: last_login Not used in Lino. """ class Meta(object): app_label = 'users' verbose_name = _('User') verbose_name_plural = _('Users') abstract = dd.is_abstract_model(__name__, 'User') ordering = ['last_name', 'first_name', 'username'] USERNAME_FIELD = 'username' preferred_foreignkey_width = 15 hidden_columns = 'password remarks' authenticated = True """This is always `True`. See also :attr:`lino.modlib.users.utils.AnonymousUser.authenticated`. """ username = NullCharField( _('Username'), max_length=30, unique=True, help_text=_("Must be unique. " "Leaving this empty means that the user cannot log in.")) profile = UserTypes.field(blank=True) initials = models.CharField(_('Initials'), max_length=10, blank=True) first_name = models.CharField(_('First name'), max_length=30, blank=True) last_name = models.CharField(_('Last name'), max_length=30, blank=True) remarks = models.TextField(_("Remarks"), blank=True) # ,null=True) if dd.is_installed('contacts'): partner = models.ForeignKey('contacts.Partner', blank=True, null=True, on_delete=models.PROTECT) else: partner = dd.DummyField() change_password = ChangePassword() def __str__(self): return self.get_full_name() def get_full_name(self): "Returns the first_name plus the last_name, with a space in between." if not self.first_name and not self.last_name: return self.username return u'{} {}'.format(self.first_name, self.last_name).strip() @dd.displayfield(_("Name"), max_length=15) def name_column(self, request): # return join_words(self.last_name.upper(),self.first_name) return str(self) if dd.is_installed('contacts'): def get_person(self): if self.partner: return self.partner.get_mti_child('person') else: def get_person(self): return None person = property(get_person) def is_editable_by_all(self): return False def get_row_permission(self, ar, state, ba): """Only system managers may edit other users. See also :meth:`User.disabled_fields`. One exception is when AnonymousUser is not readonly. This means that we want to enable online registration. In this case everybody can modify an unsaved user. """ #~ print 20120621, self, user, state, action # import pdb ; pdb.set_trace() if not ba.action.readonly: user = ar.get_user() if user != self: if not user.profile.has_required_roles([SiteAdmin]): if not self.is_editable_by_all(): return False return super(User, self).get_row_permission(ar, state, ba) #~ return False def disabled_fields(self, ar): """ Only System admins may change the `profile` of users. See also :meth:`Users.get_row_permission`. """ rv = super(User, self).disabled_fields(ar) if not ar.get_user().profile.has_required_roles([SiteAdmin]): rv.add('profile') return rv def full_clean(self, *args, **kw): p = self.person if p is not None: for n in ('first_name', 'last_name', 'email', 'language'): if not getattr(self, n): setattr(self, n, getattr(p, n)) #~ self.language = p.language if not self.language: #~ self.language = settings.SITE.DEFAULT_LANGUAGE.django_code self.language = settings.SITE.get_default_language() if not self.password: self.set_unusable_password() if not self.initials: if self.first_name and self.last_name: self.initials = self.first_name[0] + self.last_name[0] super(User, self).full_clean(*args, **kw) def get_received_mandates(self): #~ return [ [u.id,_("as %s")%u] for u in self.__class__.objects.all()] return [[u.id, str(u)] for u in self.__class__.objects.all()] #~ return self.__class__.objects.all() # @dd.htmlbox(_("Welcome")) # def welcome_email_body(self, ar): # # return join_words(self.last_name.upper(),self.first_name) # return self.get_welcome_email_body(ar) def get_welcome_email_body(self, ar): template = rt.get_template('users/welcome_email.eml') context = self.get_printable_context(ar) # dict(obj=self, E=E, rt=rt) return template.render(**context) def as_list_item(self, ar): if settings.SITE.is_demo_site: p = "'{0}', '{1}'".format(self.username, '1234') else: p = "'{0}'".format(self.username) url = "javascript:Lino.show_login_window(null, {0})".format(p) return E.li(E.a(self.username, href=url), ' : ', str(self), ', ', str(self.profile), ', ', E.strong(settings.SITE.LANGUAGE_DICT.get(self.language))) @classmethod def get_by_username(cls, username, default=models.NOT_PROVIDED): """ `User.get_by_username(x)` is equivalent to `User.objects.get(username=x)` except that the text of the DoesNotExist exception is more useful. """ try: return cls.objects.get(username=username) except cls.DoesNotExist: if default is models.NOT_PROVIDED: raise cls.DoesNotExist("No %s with username %r" % (str(cls._meta.verbose_name), username)) return default def get_preferences(self): """Return the preferences of this user. The returned object is a :class:`lino.core.userprefs.UserPrefs` object. """ return userprefs.reg.get(self)