class BasePeoplePlugin(CMSPlugin): STYLE_CHOICES = [('standard', _('Standard')), ('feature', _('Feature'))] + get_additional_styles() style = models.CharField(_('Style'), choices=STYLE_CHOICES, default=STYLE_CHOICES[0][0], max_length=50) people = SortedM2MModelField( Person, blank=True, help_text=_('Select and arrange specific people, or, leave blank to ' 'select all.')) groups = SortedM2MModelField( Group, blank=True, help_text=_('Select and arrange specific groups, or, leave blank to ' 'select specific people.')) # Add an app namespace to related_name to avoid field name clashes # with any other plugins that have a field with the same name as the # lowercase of the class name of this model. # https://github.com/divio/django-cms/issues/5030 if LTE_DJANGO_1_6: # related_name='%(app_label)s_%(class)s' does not work on Django 1.6 cmsplugin_ptr = models.OneToOneField( CMSPlugin, related_name='+', parent_link=True, ) else: cmsplugin_ptr = models.OneToOneField( CMSPlugin, related_name='%(app_label)s_%(class)s', parent_link=True, ) class Meta: abstract = True def copy_relations(self, oldinstance): self.people = oldinstance.people.all() self.groups = oldinstance.groups.all() def get_selected_groups(self): return self.groups.select_related() def get_selected_people(self): return self.people.select_related('visual') def __str__(self): return text_type(self.pk)
class BasePeoplePlugin(CMSPlugin): STYLE_CHOICES = [ ('standard', _('Standard')), ('feature', _('Feature')) ] + get_additional_styles() style = models.CharField( _('Style'), choices=STYLE_CHOICES, default=STYLE_CHOICES[0][0], max_length=50) people = SortedM2MModelField( Person, blank=True, help_text=_('Select and arrange specific people, or, leave blank to ' 'select all.') ) class Meta: abstract = True def copy_relations(self, oldinstance): self.people = oldinstance.people.all() def get_selected_people(self): return self.people.select_related('visual') def __str__(self): return text_type(self.pk)
class CookieConsentPlugin(CMSPlugin): groups = SortedM2MModelField( CookieGroup, blank=True, help_text=_( 'Select and arrange specific cookie groups, or, leave blank to ' 'select all.')) def __str__(self): return 'Cookie consent plugin' def copy_relations(self, oldinstance): self.groups = oldinstance.groups.all()
class BaseDoctorsPlugin(CMSPlugin): STYLE_CHOICES = [ ('standard', _('Standard')), # ('feature', _('Feature')), ('carousel', _('Carousel')), ] + get_additional_styles() style = models.CharField(_('Style'), choices=STYLE_CHOICES, default=STYLE_CHOICES[0][0], max_length=50) doctors = SortedM2MModelField( Doctor, blank=True, help_text=_('Select and arrange specific doctors, or, leave blank to ' 'select all.')) # Add an app namespace to related_name to avoid field name clashes # with any other plugins that have a field with the same name as the # lowercase of the class name of this model. # https://github.com/divio/django-cms/issues/5030 cmsplugin_ptr = models.OneToOneField( CMSPlugin, related_name='%(app_label)s_%(class)s', parent_link=True, on_delete=models.CASCADE, ) class Meta: abstract = True def copy_relations(self, oldinstance): self.doctors.set(oldinstance.doctors.all()) def get_selected_doctors(self): return self.doctors.select_related('visual') def __str__(self): return text_type(self.pk)
class Person(TranslationHelperMixin, TranslatedAutoSlugifyMixin, TranslatableModel): slug_source_field_name = 'name' translations = TranslatedFields( name=models.CharField(_('name'), max_length=255, blank=False, default='', help_text=_("Provide this person's name.")), slug=models.SlugField(_('unique slug'), max_length=255, blank=True, default='', help_text=_("Leave blank to auto-generate a unique slug.")), function=models.CharField(_('role'), max_length=255, blank=True, default=''), description=HTMLField(_('description'), blank=True, default='') ) phone = models.CharField( verbose_name=_('phone'), null=True, blank=True, max_length=100) mobile = models.CharField( verbose_name=_('mobile'), null=True, blank=True, max_length=100) fax = models.CharField( verbose_name=_('fax'), null=True, blank=True, max_length=100) email = models.EmailField( verbose_name=_("email"), blank=True, default='') website = models.URLField( verbose_name=_('website'), null=True, blank=True) groups = SortedM2MModelField( 'aldryn_people.Group', default=None, blank=True, related_name='people', help_text=_('Choose and order the groups for this person, the first ' 'will be the "primary group".')) visual = FilerImageField( null=True, blank=True, default=None, on_delete=models.SET_NULL) vcard_enabled = models.BooleanField( verbose_name=_('enable vCard download'), default=True) user = models.OneToOneField( getattr(settings, 'AUTH_USER_MODEL', 'auth.User'), null=True, blank=True, related_name='persons') class Meta: verbose_name = _('Person') verbose_name_plural = _('People') def __str__(self): pkstr = str(self.pk) if six.PY2: pkstr = six.u(pkstr) name = self.safe_translation_getter( 'name', default='', any_language=True ).strip() return name if len(name) > 0 else pkstr @property def primary_group(self): """Simply returns the first in `groups`, if any, else None.""" return self.groups.first() @property def comment(self): return self.safe_translation_getter('description', '') def get_absolute_url(self, language=None): if not language: language = get_current_language() slug, language = self.known_translation_getter( 'slug', None, language_code=language) if slug: kwargs = {'slug': slug} else: kwargs = {'pk': self.pk} with override(language): # do not fail with 500 error so that if detail view can't be # resolved we still can use plugins. try: url = reverse('aldryn_people:person-detail', kwargs=kwargs) except NoReverseMatch: url = '' return url def get_vcard_url(self, language=None): if not language: language = get_current_language() slug = self.safe_translation_getter( 'slug', None, language_code=language, any_language=False) if slug: kwargs = {'slug': slug} else: kwargs = {'pk': self.pk} with override(language): return reverse('aldryn_people:download_vcard', kwargs=kwargs) def get_vcard(self, request=None): vcard = Vcard() function = self.safe_translation_getter('function') safe_name = self.safe_translation_getter( 'name', default="Person: {0}".format(self.pk)) vcard.add_line('FN', safe_name) vcard.add_line('N', [None, safe_name, None, None, None]) if self.visual: ext = self.visual.extension.upper() try: with open(self.visual.path, 'rb') as f: data = force_text(base64.b64encode(f.read())) vcard.add_line('PHOTO', data, TYPE=ext, ENCODING='b') except IOError: if request: url = urlparse.urljoin(request.build_absolute_uri(), self.visual.url), vcard.add_line('PHOTO', url, TYPE=ext) if self.email: vcard.add_line('EMAIL', self.email) if function: vcard.add_line('TITLE', self.function) if self.phone: vcard.add_line('TEL', self.phone, TYPE='WORK') if self.mobile: vcard.add_line('TEL', self.mobile, TYPE='CELL') if self.fax: vcard.add_line('TEL', self.fax, TYPE='FAX') if self.website: vcard.add_line('URL', self.website) if self.primary_group: group_name = self.primary_group.safe_translation_getter( 'name', default="Group: {0}".format(self.primary_group.pk)) if group_name: vcard.add_line('ORG', group_name) if (self.primary_group.address or self.primary_group.city or self.primary_group.postal_code): vcard.add_line('ADR', ( None, None, self.primary_group.address, self.primary_group.city, None, self.primary_group.postal_code, None, ), TYPE='WORK') if self.primary_group.phone: vcard.add_line('TEL', self.primary_group.phone, TYPE='WORK') if self.primary_group.fax: vcard.add_line('TEL', self.primary_group.fax, TYPE='FAX') if self.primary_group.website: vcard.add_line('URL', self.primary_group.website) if six.PY2: vcard = unicode(vcard) return vcard
class Person(TranslationHelperMixin, TranslatedAutoSlugifyMixin, TranslatableModel): slug_source_field_name = 'name' translations = TranslatedFields( name=models.CharField(_('name'), max_length=255, blank=False, default='', help_text=_("Provide this person's name.")), slug=models.SlugField( _('unique slug'), max_length=255, blank=True, default='', help_text=_("Leave blank to auto-generate a unique slug.")), ) profile = models.OneToOneField( getattr(settings, 'ALDRYN_PEOPLE_PERSON_PROFILE', 'aldryn_people.personprofile')) groups = SortedM2MModelField( 'aldryn_people.Group', default=None, blank=True, related_name='people', help_text=_('Choose and order the groups for this person, the first ' 'will be the "primary group".')) vcard_enabled = models.BooleanField( verbose_name=_('enable vCard download'), default=True) user = models.OneToOneField(getattr(settings, 'AUTH_USER_MODEL', 'auth.User'), null=True, blank=True, related_name='persons') class Meta: verbose_name = _('Person') verbose_name_plural = _('People') def __str__(self): pkstr = str(self.pk) if six.PY2: pkstr = six.u(pkstr) name = self.safe_translation_getter('name', default='', any_language=True).strip() return name if len(name) > 0 else pkstr @property def primary_group(self): """Simply returns the first in `groups`, if any, else None.""" return self.groups.first() @property def comment(self): return """self.safe_translation_getter('description', '')""" def get_absolute_url(self, language=None): if not language: language = get_current_language() slug, language = self.known_translation_getter('slug', None, language_code=language) if slug: kwargs = {'slug': slug} else: kwargs = {'pk': self.pk} with override(language): # do not fail with 500 error so that if detail view can't be # resolved we still can use plugins. try: url = reverse('aldryn_people:person-detail', kwargs=kwargs) except NoReverseMatch: url = '' return url def get_vcard_url(self, language=None): if not language: language = get_current_language() slug = self.safe_translation_getter('slug', None, language_code=language, any_language=False) if slug: kwargs = {'slug': slug} else: kwargs = {'pk': self.pk} with override(language): return reverse('aldryn_people:download_vcard', kwargs=kwargs) def get_vcard(self, request=None): vcard = Vcard() function = self.safe_translation_getter('function') safe_name = self.safe_translation_getter('name', default="Person: {0}".format( self.pk)) vcard.add_line('FN', safe_name) vcard.add_line('N', [None, safe_name, None, None, None]) if hasattr(self.profile, "visual") and self.profile.visual: ext = self.profile.visual.extension.upper() try: with open(self.visual.path, 'rb') as f: data = force_text(base64.b64encode(f.read())) vcard.add_line('PHOTO', data, TYPE=ext, ENCODING='b') except IOError: if request: url = urlparse.urljoin(request.build_absolute_uri(), self.profile.visual.url), vcard.add_line('PHOTO', url, TYPE=ext) fields = [( "EMAIL", "email", ), ('TITLE', "function"), ('TEL', "phone", 'WORK'), ('TEL', "mobile", 'CELL'), ('TEL', "fax", 'FAX'), ('URL', "website")] for field in fields: if hasattr(self.profile, field[1]) and getattr( self.profile, field[1]): if len(field) > 2: vcard.add_line(field[0], getattr(self.profile, field[1]), TYPE=field[2]) else: vcard.add_line(field[0], getattr(self.profile, field[1])) if self.primary_group: group_name = self.primary_group.safe_translation_getter( 'name', default="Group: {0}".format(self.primary_group.pk)) if group_name: vcard.add_line('ORG', group_name) self.primary_group.get_vcard(vcard) return str(vcard)
class Person(TranslationHelperMixin, TranslatedAutoSlugifyMixin, TranslatableModel): slug_source_field_name = 'name' translations = TranslatedFields( name=models.CharField(_('name'), max_length=255, blank=False, default='', help_text=_("Provide this person's name.")), slug=models.SlugField( _('unique slug'), max_length=255, blank=True, default='', help_text=_("Leave blank to auto-generate a unique slug.")), function=models.CharField(_('role'), max_length=255, blank=True, default=''), description=HTMLField(_('description'), blank=True, default='')) phone = models.CharField(verbose_name=_('phone'), null=True, blank=True, max_length=100) mobile = models.CharField(verbose_name=_('mobile'), null=True, blank=True, max_length=100) fax = models.CharField(verbose_name=_('fax'), null=True, blank=True, max_length=100) email = models.EmailField(verbose_name=_("email"), blank=True, default='') website = models.URLField(verbose_name=_('website'), null=True, blank=True) groups = SortedM2MModelField( 'aldryn_people.Group', default=None, blank=True, related_name='people', help_text=_('Choose and order the groups for this person, the first ' 'will be the "primary group".')) regional_group = models.ForeignKey( 'aldryn_people.RegionalGroup', default=None, blank=True, null=True, related_name='people', help_text=_('Choose the regional groups for this person.')) regional_section_number = models.IntegerField( verbose_name=_('Regional section number'), blank=True, default=None, null=True) visual = FilerImageField(null=True, blank=True, default=None, on_delete=models.SET_NULL) vcard_enabled = models.BooleanField( verbose_name=_('enable vCard download'), default=True) user = models.OneToOneField(getattr(settings, 'AUTH_USER_MODEL', 'auth.User'), null=True, blank=True, related_name='persons') sort_order = models.IntegerField(verbose_name=_('sort order'), blank=True, default=999999) unit_number = models.CharField(max_length=10, blank=True, default='', verbose_name=_('Unit number')) street_number = models.CharField(max_length=10, blank=True, default='', verbose_name=_('Street number')) street = models.CharField(max_length=20, blank=True, default='', verbose_name=_('Street/Avenue')) city = models.CharField(max_length=20, blank=True, default='', verbose_name=_('City')) province = models.CharField(max_length=20, blank=True, default='BC', verbose_name=_('Province')) postal = models.CharField( max_length=7, blank=True, default='', verbose_name=_('Postal Code'), validators=[ RegexValidator( regex='^[a-zA-Z ][0-9 ][a-zA-Z ] [0-9 ][a-zA-Z ][0-9 ]$', message='Ex: V1V 9Y9', code=_('Invalid Postal Code')), ]) country = models.CharField(max_length=20, blank=True, default='Canada', verbose_name='Country') latitude = models.FloatField(null=True, blank=True, verbose_name='Latitude') longitude = models.FloatField(null=True, blank=True, verbose_name='Longitude') email_confirmed = models.BooleanField(default=False) parish_account = models.CharField(max_length=30, blank=True, null=True, default=None, verbose_name=_('Parish Account')) RELATIONSHIP_CHOICES = [('self', _('Self')), ('spouse', _('Spouse')), ('child', _('Child')), ('other', _('Other'))] relationship = models.CharField(_('Relationship'), choices=RELATIONSHIP_CHOICES, default=RELATIONSHIP_CHOICES[0][0], max_length=50) class Meta: verbose_name = _('Person') verbose_name_plural = _('People') def __str__(self): pkstr = str(self.pk) if six.PY2: pkstr = six.u(pkstr) name = self.safe_translation_getter('name', default='', any_language=True).strip() return name if len(name) > 0 else pkstr @property def primary_group(self): """Simply returns the first in `groups`, if any, else None.""" return self.groups.first() @property def comment(self): return self.safe_translation_getter('description', '') @property def address(self): address = '' if self.unit_number not in ['', None]: address += '#%s, ' % self.unit_number if self.street_number not in ['', None]: address += '%s ' % self.street_number if self.street not in ['', None]: address += '%s, ' % self.street if self.city not in ['', None]: address += '%s, ' % self.city address += '%s %s, %s' % (self.province, self.postal, self.country) return address def clean(self): geo_location = get_geographic_coordinates( self.get_address_for_geo_location()) self.longitude = geo_location['lng'] self.latitude = geo_location['lat'] if self.regional_section_number and self.regional_group: if self.regional_section_number > self.regional_group.number_of_sections: self.regional_section_number = None else: self.regional_section_number = None def get_address_for_geo_location(self): address = '' if self.street_number not in ['', None]: address += '%s ' % self.street_number else: return None if self.street not in ['', None]: address += '%s, ' % self.street else: return None if self.city not in ['', None]: address += '%s, ' % self.city else: return None address += '%s %s, %s' % (self.province, self.postal, self.country) return address def get_absolute_url(self, language=None): if not language: language = get_current_language() slug, language = self.known_translation_getter('slug', None, language_code=language) if slug: kwargs = {'slug': slug} else: kwargs = {'pk': self.pk} with override(language): # do not fail with 500 error so that if detail view can't be # resolved we still can use plugins. try: url = reverse('aldryn_people:person-detail', kwargs=kwargs) except NoReverseMatch: url = '' return url def get_vcard_url(self, language=None): if not language: language = get_current_language() slug = self.safe_translation_getter('slug', None, language_code=language, any_language=False) if slug: kwargs = {'slug': slug} else: kwargs = {'pk': self.pk} with override(language): return reverse('aldryn_people:download_vcard', kwargs=kwargs) def get_vcard(self, request=None): vcard = Vcard() person_translation = self.translations.model.objects.get( master_id=self.id, language_code='en') function = person_translation.function safe_name = person_translation.name vcard.add_line('FN', safe_name) vcard.add_line('N', [None, safe_name, None, None, None]) if self.visual: ext = self.visual.extension.upper() try: with open(self.visual.path, 'rb') as f: data = force_text(base64.b64encode(f.read())) vcard.add_line('PHOTO', data, TYPE=ext, ENCODING='b') except IOError: if request: url = urlparse.urljoin(request.build_absolute_uri(), self.visual.url), vcard.add_line('PHOTO', url, TYPE=ext) if self.email: vcard.add_line('EMAIL', self.email) if function: vcard.add_line('TITLE', function) if self.phone: vcard.add_line('TEL', self.phone, TYPE='WORK') if self.mobile: vcard.add_line('TEL', self.mobile, TYPE='CELL') if self.fax: vcard.add_line('TEL', self.fax, TYPE='FAX') if self.website: vcard.add_line('URL', self.website) # if self.primary_group: # group_name = self.primary_group.safe_translation_getter( # 'name', default="Group: {0}".format(self.primary_group.pk)) # if group_name: # vcard.add_line('ORG', group_name) # if (self.primary_group.address or self.primary_group.city or # self.primary_group.postal_code): # vcard.add_line('ADR', ( # None, None, # self.primary_group.address, # self.primary_group.city, # None, # self.primary_group.postal_code, # None, # ), TYPE='WORK') # # if self.primary_group.phone: # vcard.add_line('TEL', self.primary_group.phone, TYPE='WORK') # if self.primary_group.fax: # vcard.add_line('TEL', self.primary_group.fax, TYPE='FAX') # if self.primary_group.website: # vcard.add_line('URL', self.primary_group.website) return str(vcard)
class Article(TranslatedAutoSlugifyMixin, TranslationHelperMixin, TranslatableModel): # TranslatedAutoSlugifyMixin options slug_source_field_name = 'title' slug_default = _('untitled-article') # when True, updates the article's search_data field # whenever the article is saved or a plugin is saved # on the article's content placeholder. update_search_on_save = getattr( settings, 'ALDRYN_NEWSBLOG_UPDATE_SEARCH_DATA_ON_SAVE', False) translations = TranslatedFields( title=models.CharField(_('title'), max_length=234), slug=models.SlugField( verbose_name=_('slug'), max_length=255, db_index=True, blank=True, help_text=_('Used in the URL. If changed, the URL will change. ' 'Clear it to have it re-created automatically.'), ), lead_in=HTMLField( verbose_name=_('lead'), default='', help_text=_( 'The lead gives the reader the main idea of the story, this ' 'is useful in overviews, lists or as an introduction to your ' 'article.'), blank=True, ), meta_title=models.CharField(max_length=255, verbose_name=_('meta title'), blank=True, default=''), meta_description=models.TextField(verbose_name=_('meta description'), blank=True, default=''), meta_keywords=models.TextField(verbose_name=_('meta keywords'), blank=True, default=''), meta={'unique_together': (( 'language_code', 'slug', ), )}, search_data=models.TextField(blank=True, editable=False)) content = PlaceholderField('newsblog_article_content', related_name='newsblog_article_content') # original author field #author = models.ForeignKey(Person, null=True, blank=True, # verbose_name=_('author')) # Sorted Many to Many field. author = SortedM2MModelField( 'aldryn_people.Person', default=None, blank=True, related_name='author', help_text=_('Choose and order the authors for this article')) owner = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('owner')) app_config = AppHookConfigField(NewsBlogConfig, verbose_name=_('Apphook configuration')) categories = CategoryManyToManyField('aldryn_categories.Category', verbose_name=_('categories'), blank=True) publishing_date = models.DateTimeField(_('publishing date'), default=now) is_published = models.BooleanField(_('is published'), default=False, db_index=True) is_featured = models.BooleanField(_('is featured'), default=False, db_index=True) featured_image = FilerImageField(null=True, blank=True, on_delete=models.SET_NULL) tags = TaggableManager(blank=True) # Setting "symmetrical" to False since it's a bit unexpected that if you # set "B relates to A" you immediately have also "A relates to B". It have # to be forced to False because by default it's True if rel.to is "self": # # https://github.com/django/django/blob/1.8.4/django/db/models/fields/related.py#L2144 # # which in the end causes to add reversed releted-to entry as well: # # https://github.com/django/django/blob/1.8.4/django/db/models/fields/related.py#L977 related = SortedManyToManyField('self', verbose_name=_('related articles'), blank=True, symmetrical=False) objects = RelatedManager() class Meta: ordering = ['-publishing_date'] @property def published(self): """ Returns True only if the article (is_published == True) AND has a published_date that has passed. """ return (self.is_published and self.publishing_date <= now()) @property def future(self): """ Returns True if the article is published but is scheduled for a future date/time. """ return (self.is_published and self.publishing_date > now()) def get_absolute_url(self, language=None): """Returns the url for this Article in the selected permalink format.""" if not language: language = get_current_language() kwargs = {} permalink_type = self.app_config.permalink_type if 'y' in permalink_type: kwargs.update(year=self.publishing_date.year) if 'm' in permalink_type: kwargs.update(month="%02d" % self.publishing_date.month) if 'd' in permalink_type: kwargs.update(day="%02d" % self.publishing_date.day) if 'i' in permalink_type: kwargs.update(pk=self.pk) if 's' in permalink_type: slug, lang = self.known_translation_getter('slug', default=None, language_code=language) if slug and lang: site_id = getattr(settings, 'SITE_ID', None) if get_redirect_on_fallback(language, site_id): language = lang kwargs.update(slug=slug) if self.app_config and self.app_config.namespace: namespace = '{0}:'.format(self.app_config.namespace) else: namespace = '' with override(language): return reverse('{0}article-detail'.format(namespace), kwargs=kwargs) def get_search_data(self, language=None, request=None): """ Provides an index for use with Haystack, or, for populating Article.translations.search_data. """ if not self.pk: return '' if language is None: language = get_current_language() if request is None: request = get_request(language=language) description = self.safe_translation_getter('lead_in', '') text_bits = [strip_tags(description)] for category in self.categories.all(): text_bits.append( force_unicode(category.safe_translation_getter('name'))) for tag in self.tags.all(): text_bits.append(force_unicode(tag.name)) if self.content: plugins = self.content.cmsplugin_set.filter(language=language) for base_plugin in plugins: plugin_text_content = ' '.join( get_plugin_index_data(base_plugin, request)) text_bits.append(plugin_text_content) return ' '.join(text_bits) def save(self, *args, **kwargs): # Update the search index if self.update_search_on_save: self.search_data = self.get_search_data() # Ensure there is an owner. if self.app_config.create_authors and self.author is None: self.author = Person.objects.get_or_create( user=self.owner, defaults={ 'name': u' '.join((self.owner.first_name, self.owner.last_name)) })[0] # slug would be generated by TranslatedAutoSlugifyMixin super(Article, self).save(*args, **kwargs) def __str__(self): return self.safe_translation_getter('title', any_language=True)